├── LICENSE ├── README.md ├── doc ├── picture │ ├── 1项目启动.png │ ├── 2NetAssist参数配置.png │ ├── 3连接成功.png │ ├── 4客户端心跳指令上传.png │ ├── 5服务端接收到心跳指令.png │ ├── 6postman请求下发控制单锁指令.png │ ├── 7服务端下发控制单锁指令.png │ ├── 8服务端接收到控制单锁指令.png │ ├── 9物联网通讯协议(iot-modbus)交流群群二维码.png │ ├── TF(腾飞)开源公众号二维码.jpg │ └── TF(腾飞)开源公众号二维码.png ├── postman │ └── 通讯指令下发.postman_collection.json ├── serialport │ ├── librxtxParallel.so │ ├── librxtxSerial.so │ ├── rxtxParallel.dll │ └── rxtxSerial.dll └── 物联网通讯协议(iot-modbus)开发指南.pdf ├── iot-modbus-client ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── takeoff │ └── iot │ └── modbus │ └── client │ ├── MiiClient.java │ ├── connect │ └── MiiClientConnect.java │ ├── entity │ └── OpenLock.java │ └── message │ └── sender │ ├── ClientMessageSender.java │ └── MiiClientMessageSender.java ├── iot-modbus-common ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── takeoff │ └── iot │ └── modbus │ └── common │ ├── bytes │ └── factory │ │ ├── MiiAlarmLampDataBytesFactory.java │ │ ├── MiiBarcodeBytesFactory.java │ │ ├── MiiBytesCombinedFactory.java │ │ ├── MiiBytesFactory.java │ │ ├── MiiBytesFactorySubWrapper.java │ │ ├── MiiDataFactory.java │ │ ├── MiiEntityBytesFactory.java │ │ ├── MiiFingerBytesCombinedFactory.java │ │ ├── MiiFingerBytesFactory.java │ │ ├── MiiFingerFeatureBytesCombinedFactory.java │ │ ├── MiiFingerFeatureBytesFactory.java │ │ ├── MiiInteger2BytesFactory.java │ │ ├── MiiLampColorDataBytesFactory.java │ │ ├── MiiLcdBatchBytesFactory.java │ │ ├── MiiLcdDataBytesFactory.java │ │ ├── MiiMultiLockBytesFactory.java │ │ ├── MiiMultiLockDataBytesFactory.java │ │ ├── MiiSlotBytesFactory.java │ │ └── MiiStrings2BytesFactory.java │ ├── data │ ├── Finger.java │ ├── MiiBackLightData.java │ ├── MiiBarcodeData.java │ ├── MiiByteArrayData.java │ ├── MiiCardData.java │ ├── MiiData.java │ ├── MiiFingerData.java │ ├── MiiHeartBeatData.java │ ├── MiiHexData.java │ ├── MiiHumitureData.java │ ├── MiiInData.java │ ├── MiiIntegerData.java │ ├── MiiLockData.java │ ├── MiiSlotData.java │ └── MiiStringData.java │ ├── entity │ ├── AlarmLampData.java │ ├── ChannelConnectData.java │ ├── DeviceData.java │ ├── LampColorData.java │ ├── LcdData.java │ └── LockStatus.java │ ├── enums │ ├── DeviceConnectEnum.java │ └── MitModbusCacheKeyEnum.java │ ├── message │ ├── MiiByteArrayMessage.java │ ├── MiiMessage.java │ └── factory │ │ ├── MiiMessageFactory.java │ │ └── MiiOutMessageFactory.java │ └── utils │ ├── BytesToHexUtil.java │ ├── CacheUtils.java │ ├── IntegerToByteUtil.java │ ├── JudgeEmptyUtils.java │ ├── ModbusCrc16Utils.java │ └── SpringContextUtil.java ├── iot-modbus-netty ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── takeoff │ └── iot │ └── modbus │ └── netty │ ├── channel │ ├── MiiChannel.java │ ├── MiiChannelGroup.java │ └── MiiContextChannel.java │ ├── data │ └── factory │ │ ├── MiiClientDataFactory.java │ │ └── MiiServerDataFactory.java │ ├── device │ ├── MiiControlCentre.java │ ├── MiiDevice.java │ ├── MiiDeviceChannel.java │ └── MiiDeviceGroup.java │ ├── handle │ ├── MiiBasedFrameDecoder.java │ ├── MiiExceptionHandler.java │ ├── MiiListenerHandler.java │ ├── MiiMessageDecoder.java │ └── MiiMessageEncoder.java │ ├── listener │ ├── MiiListener.java │ └── MiiReadWriteLock.java │ ├── message │ └── MiiInMessage.java │ └── service │ ├── Mappings.java │ ├── MappingsImpl.java │ ├── NameValue.java │ └── NameValueImpl.java ├── iot-modbus-serialport ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── takeoff │ └── iot │ └── modbus │ └── serialport │ ├── data │ ├── BackLightData.java │ ├── BarCodeData.java │ ├── CardData.java │ ├── FingerData.java │ ├── HeartBeatData.java │ ├── HumitureData.java │ ├── LockData.java │ ├── ReceiveDataEvent.java │ └── factory │ │ ├── SerialportDataFactory.java │ │ └── SerialportDataReceiveFactory.java │ ├── entity │ └── ReceiveData.java │ ├── enums │ ├── DatebitsEnum.java │ ├── ParityEnum.java │ └── StopbitsEnum.java │ ├── handler │ ├── NettyRxtxDecoderHandler.java │ └── NettyRxtxFrameDecoder.java │ ├── service │ ├── SerialportSendService.java │ ├── SerialportService.java │ └── impl │ │ ├── SerialportSendServiceImpl.java │ │ └── SerialportServiceImpl.java │ └── utils │ ├── NettyRxtxClientUtil.java │ ├── SerialPortUtil.java │ └── SpringContextUtils.java ├── iot-modbus-server ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── takeoff │ └── iot │ └── modbus │ └── server │ ├── MiiServer.java │ ├── connect │ └── MiiServerConnect.java │ └── message │ └── sender │ ├── MiiServerMessageSender.java │ └── ServerMessageSender.java ├── iot-modbus-test ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── takeoff │ │ │ └── iot │ │ │ └── modbus │ │ │ ├── App.java │ │ │ └── test │ │ │ ├── config │ │ │ ├── IotModbusClientConfig.java │ │ │ ├── IotModbusSerialportConfig.java │ │ │ └── IotModbusServerConfig.java │ │ │ ├── controller │ │ │ └── TestController.java │ │ │ ├── listener │ │ │ ├── BackLightListener.java │ │ │ ├── BarCodeListener.java │ │ │ ├── CardListener.java │ │ │ ├── ChannelConnectListener.java │ │ │ ├── FingerListener.java │ │ │ ├── HeartBeatListener.java │ │ │ ├── HumitureListener.java │ │ │ └── LockListener.java │ │ │ ├── properties │ │ │ ├── IotModbusClientProperties.java │ │ │ ├── IotModbusSerialportProperties.java │ │ │ └── IotModbusServerProperties.java │ │ │ └── utils │ │ │ └── R.java │ └── resources │ │ ├── application.yml │ │ ├── banner.txt │ │ └── logback-spring.xml │ └── test │ └── java │ └── com │ └── takeoff │ └── iot │ └── modbus │ └── test │ └── AppTest.java ├── pom.xml └── tools ├── NetAssist.exe ├── UartAssist.exe └── vspd7.2.308(串口模拟工具).zip /README.md: -------------------------------------------------------------------------------- 1 | # iot-modbus 2 | 3 | #### 介绍 4 | 物联网通讯协议,基于netty框架,支持COM(串口)和TCP协议,支持服务端和客户端两种模式,实现Java控制智能设备,同时支持设备组多台设备高并发通讯。采用工厂设计模式,代码采用继承和重写的方式实现高度封装,可作为SDK提供封装的接口,让具体的业务开发人员无需关心通讯协议的底层实现,直接调用接口即可使用。实现了心跳、背光灯、扫码、刷卡、指静脉、温湿度和门锁(支持多锁)、LCD显示屏等指令控制、三色报警灯控制。代码注释丰富,包括上传和下发指令调用例子,非常容易上手。 5 | 6 | #### 视频教程 7 | 8 | [物联网通信协议(iot-modbus)视频教程,创作不易,别忘了点亮Star,你们的支持,是我源源不断的动力。(持续更新中。。。)](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzAxMjUzMDUxNw==&action=getalbum&album_id=2714994150789922817&scene=173&from_msgid=2247484129&from_itemidx=1&count=3&nolastread=1#wechat_redirect) 9 | 10 | #### 版本说明 11 | 1. V1.0.0版本仅支持TCP服务端通讯模式; 12 | 2. V2.0.0版本支持TCP服务端和客户端两种模式,客户端模式还增加了心跳重连机制。 13 | 3. V3.0.0版本支持COM(串口)和TCP协议,增加logback日志按文件大小和时间切割输出。 14 | 4. V3.1.0版本代码优化,抽取公共模块子工程。 15 | 5. V3.2.0版本TCP通讯增加支持LCD显示屏控制指令,支持批量控制LCD显示屏。 16 | 6. V3.2.1版本串口通讯增加支持LCD显示屏控制指令,支持批量控制LCD显示屏。 17 | 7. V3.2.2版本串口通讯接收指令数据拆包处理代码优化,网口通讯增加支持三色报警灯控制指令。 18 | 8. V3.2.3版本串口通讯增加支持三色报警灯控制指令,串口通讯接收指令数据拆包处理代码优化。 19 | 9. V3.2.4版本使用netty集成Rxtx对串口数据进行数据拆包处理,并且对指静脉指令进行优化。 20 | 10. V3.2.5版本客户端模式支持同时连接多个服务端下发和接收指令数据。 21 | 11. V3.2.6版本支持设备上线、掉线、处理业务异常监听处理。 22 | 12. V3.2.7版本优化客户端模式掉线/断网向服务端发起重连的机制。 23 | 13. V3.2.8版本优化服务端上线、掉线监听处理,以及对客户端心跳检测。 24 | 14. V3.2.9版本主要是集成了spring-boot-devtools工具来支持热部署,提高开发效率,无需手动重启应用。 25 | 26 | #### 软件架构 27 | 软件架构说明 28 | 基础架构采用Spring Boot2.x + Netty4.X + Maven3.6.x,日志采用logback。 29 | 30 | #### 安装环境 31 | 32 | 1. 系统Windows7以上; 33 | 2. 安装Jdk1.8以上; 34 | 2. 安装Maven3.6以上; 35 | 3. 代码以Maven工程导入Eclipse或Idea。 36 | 37 | #### 使用说明 38 | 39 | 1. 工程结构说明: 40 | - iot-modbus //物联网通讯父工程 41 | - ├── doc //文档管理 42 | - ├── iot-modbus-client //netty通讯客户端 43 | - ├── iot-modbus-common //公共模块子工程 44 | - ├── iot-modbus-netty //netty通讯子工程 45 | - ├── iot-modbus-serialport //串口通讯子工程 46 | - ├── iot-modbus-server //netty通讯服务端 47 | - ├── iot-modbus-test //使用样例子工程 48 | - └── tools //通讯指令调试工具 49 | 50 | 2. 配置文件查看iot-modbus-test子工程resources目录下的application.yml文件; 51 | 3. 启动文件查看iot-modbus-test子工程App.java文件; 52 | 4. 服务启动后,服务端端口默认为:8080,网口通讯端口默认为:4000,串口通讯默认串口为:COM1; 53 | 5. 通讯指令调试工具,TCP通讯模式使用tools目录下的NetAssist.exe,串口通讯模式使用tools目录下的UartAssist.exe(注意先安装串口模拟工具:解压tools目录下的vspd7.2.308(串口模拟工具).zip,双击vspd.exe安装,拷贝vspdctl.dll破解文件到vspd.exe安装目录替换); 54 | 6. 通讯指令采用Hex编码(十六进制); 55 | 7. 串口通讯依赖文件查看doc/serialport目录,Windows环境下rxtxParallel.dll和rxtxSerial.dll文件拷贝到JDK安装的bin目录下,Linux环境将librxtxParallel.so和librxtxSerial.so文件拷贝到JDK安装的bin目录下; 56 | 8. postman指令下发样例请查看doc/postman/通讯指令下发.postman_collection.json文件,包括门锁(单锁/多锁)、扫码、背光灯、LCD显示屏、三色报警灯指令。 57 | 58 | #### 指令格式 59 | 60 | 1. 以心跳指令(7E 04 00 BE 01 00 00 74 77 7F)作为样例说明,下标从0开始; 61 | 2. 第0位为起始符,长度固定占1个字节,固定格式:7E; 62 | 3. 第1、2位为数据长度,计算方法是从命令符到数据位(即:从3位到指令长度-3位),长度固定占2个字节,例如:04 00,表示长度为4; 63 | 4. 第3位为指令符,长度固定占1个字节,例如:BE,表示心跳指令; 64 | 5. 第4位为设备号,长度固定占1个字节,例如:01,表示设备号为1; 65 | 6. 第5位为层地址,长度固定占1个字节,例如:00,表示设备所有的层不执行; 66 | 7. 第6位为槽地址,长度固定占1个字节,例如:00,表示设备所有的槽不执行; 67 | 8. 指令长度-3位到-2位为校验位,采用CRC16_MODBUS(长度,命令,地址,数据)校验,例如:74 77,详细查看:ModbusCrc16Utils.java工具类; 68 | 9. 末位为结束符,长度固定占1个字节,固定格式:7F。 69 | 70 | #### 通讯指令 71 | 72 | 1. 心跳上传指令 73 | - iot-modbus作为服务端,通过心跳来维持通讯,启动服务端后,打开NetAssist.exe指令调试工具,先往服务端发送心跳指令; 74 | - 硬件往服务端发送:7E 04 00 BE 01 00 00 74 77 7F ,为必要指令。 75 | 2. 背光灯上传指令 76 | - 硬件往服务端发送:7E 05 00 88 01 00 00 00 AF E3 7F 77 | 3. 扫码指令下发 78 | - 服务端往硬件下发:7E 05 00 08 01 00 00 01 6F FD 7F 79 | - 第7位为数据位,长度固定占1个字节,例如:01,表示开开启扫码头。 80 | 4. 扫码指令上传 81 | - 硬件往服务端发送:7E 24 00 8F 01 00 00 03 45 30 30 34 30 31 30 38 32 38 30 32 41 36 39 33 0D 02 00 00 01 02 13 73 02 00 00 01 02 13 73 9B 79 7F 82 | - 数据位:03 45 30 30 34 30 31 30 38 32 38 30 32 41 36 39 33 0D 02 00 00 01 02 13 73 02 00 00 01 02 13 73为条码信息。 83 | 5. 刷卡指令上传 84 | - 硬件往服务端发送:7E 08 00 84 01 00 00 86 14 AE 02 7C 53 7F 85 | - 数据位:86 14 AE 02为卡号信息。 86 | 6. 单开锁下发指令 87 | - 服务端往硬件下发:7E 05 00 03 01 00 00 01 CA 3C 7F 88 | - 第7位为数据位,长度固定占1个字节,例如:01,表示开1号锁。 89 | 7. 多开锁下发指令 90 | - 服务端往硬件下发:7E 08 00 03 FF FF FF 01 00 02 01 7F B0 7F 91 | - FF FF FF为指令做兼容填补位,后面 01 00 02 01是数据位,其中:01表示1号锁,00表示上锁;02表示2号锁,01表示开锁。 92 | 8. 锁状态上传指令 93 | - 硬件往服务端发送:7E 0D 00 83 01 00 00 FF FF FF 01 00 05 02 00 01 EE 99 7F 94 | - FF FF FF为指令做兼容填补位,后面 01 00 05 02 00 01是数据位,其中:01表示1号锁,00表示上锁,05表示传感器状态码;02表示2号锁,00表示上锁,01表示传感器状态码。 95 | 9. 指静脉和温湿度指令(不作说明,详细查看代码); 96 | 10. LCD显示屏批量控制下发指令(不作说明,详细查看代码); 97 | 11. 三色报警灯控制下发指令(不作说明,详细查看代码)。 98 | 99 | #### 调试说明 100 | 101 | 1. 找到iot-modbus-test子工程App.java文件启动服务端,如下图所示: 102 | - ![输入图片说明](doc/picture/1%E9%A1%B9%E7%9B%AE%E5%90%AF%E5%8A%A8.png) 103 | - 说明:项目启动成功后,控制台日志输出服务端的端口为:8080;项目服务名为:iot-modbus-test;服务端开启socket通讯端口为:4000。 104 | 2. 将工程tools目录下通讯指令调试工具NetAssist拷贝到Windows桌面,双击打开,并配置参数,如下图所示: 105 | - ![输入图片说明](doc/picture/2NetAssist%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE.png) 106 | - 说明:协议类型选择TCP Client(调试工具作为模拟硬件通讯的客服端);远程主机地址输入本地电脑的IP地址;远程主机端口输入服务端端口4000;接收和发送的编码选择HEX;最后点击连接按钮进行连接,连接成功后服务端控制台日志输出如下图所示: 107 | - ![输入图片说明](doc/picture/3%E8%BF%9E%E6%8E%A5%E6%88%90%E5%8A%9F.png) 108 | 3. 客户端往服务端上传心跳指令,如下图所示: 109 | - ![输入图片说明](doc/picture/4%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BF%83%E8%B7%B3%E6%8C%87%E4%BB%A4%E4%B8%8A%E4%BC%A0.png) 110 | - 说明:拷贝心跳指令到通讯指令调试工具NetAssist的数据发送窗口粘贴进来,然后点击发送按钮;此时服务端将接收到心跳指令,如下图所示: 111 | - ![输入图片说明](doc/picture/5%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8E%A5%E6%94%B6%E5%88%B0%E5%BF%83%E8%B7%B3%E6%8C%87%E4%BB%A4.png) 112 | - 说明:客服端与服务端的通讯连接需要通过客户端定时往服务端发送心跳指令来维持,在生产环境中发送频率一般可设置为每5秒一次,如果通讯连接断开则客服端与服务端无法通讯。注意:在调试的过程中,如果通讯指令调试工具NetAssist与服务端通讯连接断开,则要手动点击NetAssist的连接按钮,重新往服务端发送一条心跳的指令。 113 | 4. 使用Postman请求服务端往客户端下发控制单锁指令,如下图所示: 114 | - ![输入图片说明](doc/picture/6postman%E8%AF%B7%E6%B1%82%E4%B8%8B%E5%8F%91%E6%8E%A7%E5%88%B6%E5%8D%95%E9%94%81%E6%8C%87%E4%BB%A4.png) 115 | - 说明:在Postman输入服务端发送控制单锁指令接口,填入请求地址和参数:http://127.0.0.1:8080/iot-modbus-test/test/openlock/1/1,服务端控制台日志输出如下图所示: 116 | - ![输入图片说明](doc/picture/7%E6%9C%8D%E5%8A%A1%E7%AB%AF%E4%B8%8B%E5%8F%91%E6%8E%A7%E5%88%B6%E5%8D%95%E9%94%81%E6%8C%87%E4%BB%A4.png) 117 | - 客服端指令调试工具NetAssist接收到控制单锁指令,如下图所示: 118 | - ![输入图片说明](doc/picture/8%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8E%A5%E6%94%B6%E5%88%B0%E6%8E%A7%E5%88%B6%E5%8D%95%E9%94%81%E6%8C%87%E4%BB%A4.png) 119 | 5. 其他上传和下发的指令不作详细介绍,感兴趣的同学可以参考通讯指令和工程目录doc/postman目录下的请求样例。 120 | 121 | #### 创作不易,别忘了点亮Star,你们的支持,是我源源不断的动力。 122 | 123 | #### 欢迎加入交流群 124 | 125 | - 微信公众号 126 | - ![输入图片说明](doc/picture/TF%EF%BC%88%E8%85%BE%E9%A3%9E%EF%BC%89%E5%BC%80%E6%BA%90%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BA%8C%E7%BB%B4%E7%A0%81.jpg) 127 | - QQ技术交流群 128 | - ![输入图片说明](doc/picture/9%E7%89%A9%E8%81%94%E7%BD%91%E9%80%9A%E8%AE%AF%E5%8D%8F%E8%AE%AE%EF%BC%88iot-modbus%EF%BC%89%E4%BA%A4%E6%B5%81%E7%BE%A4%E7%BE%A4%E4%BA%8C%E7%BB%B4%E7%A0%81.png) 129 | -------------------------------------------------------------------------------- /doc/picture/1项目启动.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/1项目启动.png -------------------------------------------------------------------------------- /doc/picture/2NetAssist参数配置.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/2NetAssist参数配置.png -------------------------------------------------------------------------------- /doc/picture/3连接成功.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/3连接成功.png -------------------------------------------------------------------------------- /doc/picture/4客户端心跳指令上传.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/4客户端心跳指令上传.png -------------------------------------------------------------------------------- /doc/picture/5服务端接收到心跳指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/5服务端接收到心跳指令.png -------------------------------------------------------------------------------- /doc/picture/6postman请求下发控制单锁指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/6postman请求下发控制单锁指令.png -------------------------------------------------------------------------------- /doc/picture/7服务端下发控制单锁指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/7服务端下发控制单锁指令.png -------------------------------------------------------------------------------- /doc/picture/8服务端接收到控制单锁指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/8服务端接收到控制单锁指令.png -------------------------------------------------------------------------------- /doc/picture/9物联网通讯协议(iot-modbus)交流群群二维码.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/9物联网通讯协议(iot-modbus)交流群群二维码.png -------------------------------------------------------------------------------- /doc/picture/TF(腾飞)开源公众号二维码.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/TF(腾飞)开源公众号二维码.jpg -------------------------------------------------------------------------------- /doc/picture/TF(腾飞)开源公众号二维码.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/picture/TF(腾飞)开源公众号二维码.png -------------------------------------------------------------------------------- /doc/serialport/librxtxParallel.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/serialport/librxtxParallel.so -------------------------------------------------------------------------------- /doc/serialport/librxtxSerial.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/serialport/librxtxSerial.so -------------------------------------------------------------------------------- /doc/serialport/rxtxParallel.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/serialport/rxtxParallel.dll -------------------------------------------------------------------------------- /doc/serialport/rxtxSerial.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/serialport/rxtxSerial.dll -------------------------------------------------------------------------------- /doc/物联网通讯协议(iot-modbus)开发指南.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/doc/物联网通讯协议(iot-modbus)开发指南.pdf -------------------------------------------------------------------------------- /iot-modbus-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | iot-modbus 8 | com.takeoff.iot 9 | 3.2.9-SNAPSHOT 10 | 11 | iot-modbus-client 12 | jar 13 | iot-modbus-client 14 | 物联网网口通讯协议客户端模块 15 | 16 | 17 | 3.2.9-SNAPSHOT 18 | 19 | 20 | 21 | 22 | com.takeoff.iot 23 | iot-modbus-netty 24 | ${iot-modbus-netty.version} 25 | 26 | 27 | 28 | 29 | ${project.artifactId} 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-surefire-plugin 35 | 36 | true 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /iot-modbus-client/src/main/java/com/takeoff/iot/modbus/client/MiiClient.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.client; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Map; 7 | import java.util.Set; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import com.takeoff.iot.modbus.client.connect.MiiClientConnect; 11 | import com.takeoff.iot.modbus.client.message.sender.ClientMessageSender; 12 | import com.takeoff.iot.modbus.client.message.sender.MiiClientMessageSender; 13 | import com.takeoff.iot.modbus.common.bytes.factory.MiiDataFactory; 14 | import com.takeoff.iot.modbus.common.message.MiiMessage; 15 | import com.takeoff.iot.modbus.common.utils.CacheUtils; 16 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 17 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 18 | import com.takeoff.iot.modbus.netty.data.factory.MiiClientDataFactory; 19 | import com.takeoff.iot.modbus.netty.device.MiiDeviceChannel; 20 | import com.takeoff.iot.modbus.netty.handle.*; 21 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 22 | 23 | import io.netty.bootstrap.Bootstrap; 24 | import io.netty.channel.ChannelFuture; 25 | import io.netty.channel.ChannelHandlerContext; 26 | import io.netty.channel.ChannelInboundHandlerAdapter; 27 | import io.netty.channel.ChannelInitializer; 28 | import io.netty.channel.ChannelPipeline; 29 | import io.netty.channel.EventLoopGroup; 30 | import io.netty.channel.nio.NioEventLoopGroup; 31 | import io.netty.channel.socket.SocketChannel; 32 | import io.netty.channel.socket.nio.NioSocketChannel; 33 | import io.netty.handler.timeout.IdleStateEvent; 34 | import io.netty.handler.timeout.IdleStateHandler; 35 | import lombok.extern.slf4j.Slf4j; 36 | 37 | /** 38 | * 类功能说明:设备通讯客户端
39 | * 公司名称:TF(腾飞)开源
40 | * 作者:luorongxi
41 | */ 42 | @Slf4j 43 | public class MiiClient extends ChannelInitializer { 44 | 45 | private static int IDLE_TIMEOUT = 60000; 46 | 47 | private Map workerGroupMap = new HashMap(); 48 | 49 | private String deviceGroup; 50 | private int nThread; 51 | private MiiListenerHandler handler; 52 | private MiiDataFactory dataFactory; 53 | 54 | private Set addressSet = new HashSet<>(); 55 | 56 | private Map cmMap = new HashMap(); 57 | 58 | private Map futureMap = new HashMap(); 59 | 60 | public MiiClient(String deviceGroup){ 61 | this(deviceGroup, 0, IDLE_TIMEOUT); 62 | } 63 | 64 | public MiiClient(String deviceGroup,int nThread, int heartBeatTime){ 65 | this.deviceGroup = deviceGroup; 66 | this.nThread = nThread; 67 | this.IDLE_TIMEOUT = heartBeatTime; 68 | this.handler = new MiiListenerHandler(); 69 | this.dataFactory = new MiiClientDataFactory(); 70 | } 71 | 72 | /** 73 | * 连接服务端 74 | */ 75 | public ChannelFuture connect(String serverHost, int serverPort, int reconnectTime) throws Exception { 76 | EventLoopGroup workerGroup = new NioEventLoopGroup(nThread); 77 | workerGroupMap.put(serverHost, workerGroup); 78 | Bootstrap boot = new Bootstrap() 79 | .group(workerGroup) 80 | .channel(NioSocketChannel.class) 81 | .handler(this); 82 | InetSocketAddress address = InetSocketAddress.createUnresolved(serverHost, serverPort); 83 | addressSet.add(address); 84 | MiiClientConnect cm = new MiiClientConnect(boot, address, reconnectTime){ 85 | @Override 86 | public void afterSuccess() { 87 | sender().registerGroup(serverHost, deviceGroup); 88 | } 89 | }; 90 | cmMap.put(serverHost, cm); 91 | ChannelFuture future = cm.connect(); 92 | futureMap.put(serverHost, future); 93 | return future; 94 | } 95 | 96 | /** 97 | * 停止服务 98 | */ 99 | public ChannelFuture disconnect(String serverHost){ 100 | MiiClientConnect cm = (MiiClientConnect) cmMap.get(serverHost); 101 | ChannelFuture future = (ChannelFuture) futureMap.get(serverHost); 102 | ChannelFuture f = future.channel().closeFuture(); 103 | EventLoopGroup workerGroup = (EventLoopGroup) workerGroupMap.get(serverHost); 104 | workerGroup.shutdownGracefully(); 105 | future = null; 106 | return f; 107 | } 108 | 109 | @Override 110 | protected void initChannel(SocketChannel ch) throws Exception { 111 | ChannelPipeline p = ch.pipeline(); 112 | for(InetSocketAddress address : addressSet){ 113 | MiiChannel channel = new MiiDeviceChannel(address, ch); 114 | MiiChannel isExist = (MiiChannel) CacheUtils.get(channel.name()); 115 | if(JudgeEmptyUtils.isEmpty(isExist)){ 116 | MiiClientConnect cm = (MiiClientConnect) cmMap.get(channel.name()); 117 | p.addLast(cm); 118 | p.addLast(new IdleStateHandler(0, 0, IDLE_TIMEOUT, TimeUnit.MILLISECONDS)); 119 | p.addLast(new ChannelInboundHandlerAdapter(){ 120 | 121 | @Override 122 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 123 | if(evt instanceof IdleStateEvent){ 124 | sender().registerGroup(channel.name(), deviceGroup); 125 | } else { 126 | super.userEventTriggered(ctx, evt); 127 | } 128 | } 129 | 130 | }); 131 | p.addLast(new MiiMessageEncoder()); 132 | p.addLast(new MiiBasedFrameDecoder()); 133 | p.addLast(new MiiMessageDecoder(dataFactory)); 134 | p.addLast(handler); 135 | p.addLast(new MiiExceptionHandler()); 136 | } 137 | } 138 | } 139 | 140 | public ClientMessageSender sender(){ 141 | return new MiiClientMessageSender(); 142 | } 143 | 144 | /** 145 | * 添加接收指定指令的消息监听器 146 | * @param command 指令类型 {@link MiiMessage} 147 | * @param listener 消息监听器 148 | * @return 上一个消息监听器,如果没有返回null 149 | */ 150 | public MiiListener addListener(int command, MiiListener listener){ 151 | return handler.addListener(command, listener); 152 | } 153 | 154 | /** 155 | * 移除接收指定指令的消息监听器 156 | * @param command 指令类型 {@link MiiMessage} 157 | * @return 移除消息监听器,如果没有返回null 158 | */ 159 | public MiiListener removeListener(int command){ 160 | return handler.removeListener(command); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /iot-modbus-client/src/main/java/com/takeoff/iot/modbus/client/connect/MiiClientConnect.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.client.connect; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.net.SocketAddress; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import com.takeoff.iot.modbus.client.message.sender.MiiClientMessageSender; 10 | import com.takeoff.iot.modbus.common.entity.ChannelConnectData; 11 | import com.takeoff.iot.modbus.common.enums.DeviceConnectEnum; 12 | import com.takeoff.iot.modbus.common.utils.CacheUtils; 13 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 14 | import com.takeoff.iot.modbus.common.utils.SpringContextUtil; 15 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 16 | import com.takeoff.iot.modbus.netty.device.MiiDeviceChannel; 17 | import io.netty.bootstrap.Bootstrap; 18 | import io.netty.channel.*; 19 | import io.netty.channel.ChannelHandler.Sharable; 20 | import io.netty.util.HashedWheelTimer; 21 | import io.netty.util.Timeout; 22 | import io.netty.util.Timer; 23 | import io.netty.util.TimerTask; 24 | import lombok.Getter; 25 | import lombok.extern.slf4j.Slf4j; 26 | import org.springframework.context.ApplicationContext; 27 | import org.springframework.util.ObjectUtils; 28 | 29 | /** 30 | * 类功能说明:客户端链接管理器
31 | * 公司名称:TF(腾飞)开源
32 | * 作者:luorongxi
33 | */ 34 | @Slf4j 35 | @Sharable 36 | public abstract class MiiClientConnect extends ChannelInboundHandlerAdapter implements TimerTask { 37 | 38 | private ApplicationContext getApplicationContext = SpringContextUtil.applicationContext; 39 | 40 | private static int TIMEOUT = 5000; 41 | 42 | private final Bootstrap boot; 43 | private SocketAddress address; 44 | private Timer timer; 45 | 46 | /** 47 | * 连接成功次数 48 | */ 49 | private Map onLineMap = new HashMap<>(); 50 | 51 | /** 52 | * 连接断开次数 53 | */ 54 | private Map breakOffMap = new HashMap<>(); 55 | 56 | /** 57 | * 重连失败次数 58 | */ 59 | private Map retriesMap = new HashMap<>(); 60 | 61 | public MiiClientConnect(Bootstrap boot, SocketAddress address, int reconnectTime){ 62 | this.boot = boot; 63 | this.address = address; 64 | this.TIMEOUT = reconnectTime; 65 | this.timer = new HashedWheelTimer(); 66 | } 67 | 68 | public ChannelFuture connect() throws Exception { 69 | ChannelFuture future = boot.connect(address); 70 | future.addListener(new ChannelFutureListener() { 71 | @Override 72 | public void operationComplete(ChannelFuture future) throws Exception { 73 | if (!future.isSuccess()) { 74 | //连接失败,重连服务端,重连交给后端线程执行 75 | future.channel().eventLoop().schedule(() -> { 76 | Integer retries = (ObjectUtils.isEmpty(retriesMap.get(address.toString())) ? 0 : retriesMap.get(address.toString())) + 1; 77 | retriesMap.put(address.toString(), retries); 78 | if(!JudgeEmptyUtils.isEmpty(address)){ 79 | ChannelConnectData connectServerData = new ChannelConnectData(this, DeviceConnectEnum.BREAK_RECONNECT.getKey(), address.toString(), retries); 80 | if(!JudgeEmptyUtils.isEmpty(connectServerData) && !JudgeEmptyUtils.isEmpty(getApplicationContext)){ 81 | getApplicationContext.publishEvent(connectServerData); 82 | } 83 | } 84 | try { 85 | connect(); 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | } 89 | }, TIMEOUT, TimeUnit.MILLISECONDS); 90 | } else { 91 | //服务端连接成功 92 | afterSuccess(); 93 | Integer onLine = (ObjectUtils.isEmpty(onLineMap.get(address.toString())) ? 0 : onLineMap.get(address.toString())) + 1; 94 | onLineMap.put(address.toString(), onLine); 95 | if(!JudgeEmptyUtils.isEmpty(address)){ 96 | ChannelConnectData connectServerData = new ChannelConnectData(this, DeviceConnectEnum.ON_LINE.getKey(), address.toString(), onLine); 97 | if(!JudgeEmptyUtils.isEmpty(connectServerData) && !JudgeEmptyUtils.isEmpty(getApplicationContext)){ 98 | getApplicationContext.publishEvent(connectServerData); 99 | } 100 | } 101 | } 102 | } 103 | }); 104 | //对通道关闭进行监听 105 | future.channel().closeFuture().sync(); 106 | return future; 107 | } 108 | 109 | @Override 110 | public void run(Timeout timeout) throws Exception { 111 | connect(); 112 | } 113 | 114 | @Override 115 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 116 | //成功后,重连失败次数清零 117 | Channel channel = ctx.channel(); 118 | ctx.fireChannelActive(); 119 | if(!JudgeEmptyUtils.isEmpty(channel.remoteAddress())){ 120 | String address = channel.remoteAddress().toString().substring(1,channel.remoteAddress().toString().length()); 121 | retriesMap.put(address.toString(), 0); 122 | //将柜地址与通讯管道的绑定关系写入缓存 123 | MiiChannel miiChannel = new MiiDeviceChannel(channel); 124 | CacheUtils.put(miiChannel.name(), miiChannel); 125 | } 126 | } 127 | 128 | @Override 129 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 130 | ctx.fireChannelInactive(); 131 | Channel channel = ctx.channel(); 132 | if(!JudgeEmptyUtils.isEmpty(channel) && !JudgeEmptyUtils.isEmpty(channel.remoteAddress())){ 133 | String address = channel.remoteAddress().toString().substring(1,channel.remoteAddress().toString().length()); 134 | Integer breakOff = (ObjectUtils.isEmpty(breakOffMap.get(address)) ? 0 : breakOffMap.get(address)) + 1; 135 | breakOffMap.put(address, breakOff); 136 | ChannelConnectData connectServerData = new ChannelConnectData(this, DeviceConnectEnum.BREAK_OFF.getKey(), address, breakOff); 137 | if(!JudgeEmptyUtils.isEmpty(connectServerData) && !JudgeEmptyUtils.isEmpty(getApplicationContext)){ 138 | getApplicationContext.publishEvent(connectServerData); 139 | } 140 | //将通讯管道的绑定关系从缓存中删除 141 | MiiChannel miiChannel = new MiiDeviceChannel(channel); 142 | MiiChannel isExist = (MiiChannel) CacheUtils.get(miiChannel.name()); 143 | if(!JudgeEmptyUtils.isEmpty(isExist)){ 144 | CacheUtils.remove(miiChannel.name()); 145 | timer.newTimeout(this, TIMEOUT, TimeUnit.MILLISECONDS); 146 | } 147 | //连接断开后的最后处理 148 | ctx.pipeline().remove(ctx.handler()); 149 | ctx.deregister(); 150 | ctx.close(); 151 | } 152 | } 153 | 154 | public abstract void afterSuccess(); 155 | } 156 | -------------------------------------------------------------------------------- /iot-modbus-client/src/main/java/com/takeoff/iot/modbus/client/entity/OpenLock.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.client.entity; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class OpenLock { 7 | 8 | /** 9 | * 服务IP 10 | */ 11 | private String ip; 12 | 13 | /** 14 | * 设备号 15 | */ 16 | private int device; 17 | 18 | /** 19 | * 锁状态(0:上锁;1:开锁) 20 | */ 21 | private int status; 22 | } 23 | -------------------------------------------------------------------------------- /iot-modbus-client/src/main/java/com/takeoff/iot/modbus/client/message/sender/ClientMessageSender.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.client.message.sender; 2 | 3 | import java.util.List; 4 | 5 | import com.takeoff.iot.modbus.common.data.Finger; 6 | 7 | /** 8 | * 类功能说明:上传指令接口
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | public interface ClientMessageSender { 13 | 14 | /** 15 | * 上传设备组指令. 16 | * @param ip 设备IP 17 | * @param deviceGroup 设备组号 18 | */ 19 | void registerGroup(String ip, String deviceGroup); 20 | 21 | /** 22 | * 上传控制单锁指令. 23 | * @param ip 设备IP 24 | * @param device 设备号 25 | * @param status 锁状态(0:上锁;1:开锁) 26 | */ 27 | void unlock(String ip, int device, int status); 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /iot-modbus-client/src/main/java/com/takeoff/iot/modbus/client/message/sender/MiiClientMessageSender.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.client.message.sender; 2 | 3 | import com.takeoff.iot.modbus.common.utils.CacheUtils; 4 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 5 | import org.bouncycastle.util.encoders.Hex; 6 | 7 | import com.takeoff.iot.modbus.common.bytes.factory.MiiBytesCombinedFactory; 8 | import com.takeoff.iot.modbus.common.bytes.factory.MiiBytesFactory; 9 | import com.takeoff.iot.modbus.common.bytes.factory.MiiBytesFactorySubWrapper; 10 | import com.takeoff.iot.modbus.common.bytes.factory.MiiSlotBytesFactory; 11 | import com.takeoff.iot.modbus.common.bytes.factory.MiiStrings2BytesFactory; 12 | import com.takeoff.iot.modbus.common.data.MiiData; 13 | import com.takeoff.iot.modbus.common.message.MiiMessage; 14 | import com.takeoff.iot.modbus.common.message.factory.MiiMessageFactory; 15 | import com.takeoff.iot.modbus.common.message.factory.MiiOutMessageFactory; 16 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 17 | 18 | import lombok.extern.slf4j.Slf4j; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | /** 24 | * 类功能说明:指令下发接口实现
25 | * 公司名称:TF(腾飞)开源
26 | * 作者:luorongxi
27 | */ 28 | @Slf4j 29 | public class MiiClientMessageSender implements ClientMessageSender { 30 | 31 | private static final MiiBytesFactory BYTESFACTORY_SLOT = new MiiSlotBytesFactory(); 32 | 33 | private static final MiiMessageFactory SINGLE_LOCK = new MiiOutMessageFactory<>(BYTESFACTORY_SLOT); 34 | 35 | private static final MiiMessageFactory REGISTERGROUP = new MiiOutMessageFactory<>( 36 | new MiiBytesCombinedFactory( 37 | new MiiBytesFactorySubWrapper(BYTESFACTORY_SLOT, 0, 4) 38 | ,new MiiBytesFactorySubWrapper(new MiiStrings2BytesFactory(), 4, 5) 39 | )); 40 | 41 | public MiiClientMessageSender(){ 42 | 43 | } 44 | 45 | private void sendMessage(MiiMessageFactory factory, String ip, E... datas){ 46 | if(JudgeEmptyUtils.isEmpty(CacheUtils.get(ip))){ 47 | log.info("未找到对应的通讯连接:"+ ip +",下发指令失败"); 48 | }else{ 49 | MiiChannel channel = (MiiChannel) CacheUtils.get(ip); 50 | MiiMessage message = factory.buildMessage(channel.name(), datas); 51 | log.info("待上报指令数据:"+ Hex.toHexString(message.toBytes())); 52 | channel.send(message); 53 | } 54 | } 55 | 56 | @Override 57 | public void registerGroup(String ip, String deviceGroup) { 58 | sendMessage(REGISTERGROUP, ip,0, 0, 0, 0, deviceGroup); 59 | } 60 | 61 | @Override 62 | public void unlock(String ip, int device, int status) { 63 | sendMessage(SINGLE_LOCK, ip, MiiMessage.LOCK, device, MiiData.NULL, MiiData.NULL, status); 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /iot-modbus-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.takeoff.iot 8 | iot-modbus 9 | 3.2.9-SNAPSHOT 10 | 11 | iot-modbus-common 12 | jar 13 | iot-modbus-common 14 | 物联网通讯协议公共模块 15 | 16 | 17 | 3.5 18 | 3.2.1 19 | 1.6 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-aop 26 | 27 | 28 | org.apache.commons 29 | commons-lang3 30 | ${commons-lang3.version} 31 | 32 | 33 | commons-collections 34 | commons-collections 35 | ${commons-collections.version} 36 | 37 | 38 | commons-codec 39 | commons-codec 40 | ${commons-codec.version} 41 | 42 | 43 | 44 | 45 | ${project.artifactId} 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-surefire-plugin 51 | 52 | true 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiAlarmLampDataBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | public class MiiAlarmLampDataBytesFactory extends MiiInteger2BytesFactory implements MiiBytesFactory { 4 | 5 | private static final int ALARM_LAMP_BATCH_BYTES = 1, ALARM_LAMP_BATCH_COUNT = 4; 6 | 7 | public MiiAlarmLampDataBytesFactory() { 8 | this(0); 9 | } 10 | 11 | public MiiAlarmLampDataBytesFactory(int startPos) { 12 | super(ALARM_LAMP_BATCH_BYTES, ALARM_LAMP_BATCH_COUNT, startPos); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiBarcodeBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | public class MiiBarcodeBytesFactory extends MiiInteger2BytesFactory implements MiiBytesFactory { 4 | 5 | private static final int INTEGER_BYTES = 1,INTEGER_COUNT = 6; 6 | 7 | public MiiBarcodeBytesFactory() { 8 | this(0); 9 | } 10 | 11 | public MiiBarcodeBytesFactory(int startPos) { 12 | super(INTEGER_BYTES, INTEGER_COUNT, startPos); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiBytesCombinedFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | 6 | import org.apache.commons.lang3.ArrayUtils; 7 | 8 | /** 9 | * 类功能说明:组合指令工厂
10 | * 公司名称:TF(腾飞)开源
11 | * 作者:luorongxi
12 | */ 13 | public class MiiBytesCombinedFactory implements MiiBytesFactory { 14 | 15 | private MiiBytesFactory[] factorys; 16 | 17 | @SafeVarargs 18 | public MiiBytesCombinedFactory(MiiBytesFactory... factorys) { 19 | this.factorys = factorys; 20 | } 21 | 22 | @Override 23 | public byte[] toBytes(E... contents) { 24 | ArrayList list = new ArrayList<>(); 25 | for(MiiBytesFactory factory : factorys){ 26 | list.addAll(Arrays.asList( 27 | ArrayUtils.toObject(factory.toBytes(contents)))); 28 | } 29 | return ArrayUtils.toPrimitive(list.toArray(new Byte[list.size()])); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | public interface MiiBytesFactory { 4 | 5 | byte[] toBytes(E... contents); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiBytesFactorySubWrapper.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | import java.lang.reflect.Array; 4 | import java.lang.reflect.ParameterizedType; 5 | import java.lang.reflect.Type; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import org.apache.commons.lang3.ArrayUtils; 10 | 11 | /** 12 | * 类功能说明:指令长度截取工厂
13 | * 公司名称:TF(腾飞)开源
14 | * 作者:luorongxi
15 | */ 16 | public class MiiBytesFactorySubWrapper implements MiiBytesFactory { 17 | private MiiBytesFactory factory; 18 | private int startPos, endPos; 19 | private Type vType; 20 | 21 | public MiiBytesFactorySubWrapper(MiiBytesFactory factory, int startPos, int endPos){ 22 | this.factory = factory; 23 | this.startPos = startPos; 24 | this.endPos = endPos; 25 | 26 | if(this.factory.getClass().getGenericSuperclass() instanceof ParameterizedType){ 27 | vType = ((ParameterizedType) this.factory.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 28 | if(!(vType instanceof Class)){ 29 | throw new RuntimeException("因为校验类型需要,请factory参数在new创建对象的构造函数后添加{}"); 30 | } 31 | } else if(this.factory.getClass().getGenericInterfaces()[0] instanceof ParameterizedType){ 32 | vType = ((ParameterizedType) this.factory.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]; 33 | if(vType instanceof ParameterizedType){ 34 | vType = ((ParameterizedType) vType).getRawType(); 35 | } 36 | if(!(vType instanceof Class)){ 37 | throw new RuntimeException("因为校验类型需要,请factory参数在new创建对象的构造函数后添加{}"); 38 | } 39 | } else { 40 | throw new RuntimeException("因为校验类型需要,请factory参数在new创建对象的构造函数后添加{}"); 41 | } 42 | } 43 | 44 | @Override 45 | public byte[] toBytes(E... contents) { 46 | 47 | if(startPos >= contents.length){ 48 | return ArrayUtils.EMPTY_BYTE_ARRAY; 49 | } 50 | 51 | E[] subs = ArrayUtils.subarray(contents, startPos 52 | , Math.min(endPos > 0 ? endPos : contents.length, contents.length)); 53 | List list = new ArrayList<>(); 54 | for(E content : subs){ 55 | try { 56 | list.add((V) content); 57 | } catch (ClassCastException e) { 58 | throw new ClassCastException( 59 | String.format("%s和%s之间存在%s类型." 60 | , startPos, endPos, content.getClass().getSimpleName())); 61 | } 62 | } 63 | 64 | return factory.toBytes(list.toArray((V[]) Array.newInstance((Class) vType, list.size()))); 65 | } 66 | 67 | public Type getVType(){ 68 | return vType; 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiDataFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiData; 4 | 5 | public interface MiiDataFactory { 6 | 7 | MiiData buildData(int command, byte[] datas); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiEntityBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | public interface MiiEntityBytesFactory extends MiiBytesFactory { 4 | 5 | byte[] toBytes(T t); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiFingerBytesCombinedFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | import org.apache.commons.lang3.ArrayUtils; 4 | 5 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | 10 | /** 11 | * 类功能说明:指静脉组合指令工厂
12 | * 公司名称:TF(腾飞)开源
13 | * 作者:luorongxi
14 | */ 15 | public class MiiFingerBytesCombinedFactory implements MiiBytesFactory { 16 | 17 | private MiiBytesFactory[] factorys; 18 | 19 | @SafeVarargs 20 | public MiiFingerBytesCombinedFactory(MiiBytesFactory... factorys) { 21 | this.factorys = factorys; 22 | } 23 | 24 | @Override 25 | public byte[] toBytes(E... contents) { 26 | ArrayList list = new ArrayList<>(); 27 | for(MiiBytesFactory factory : factorys){ 28 | list.addAll(Arrays.asList(ArrayUtils.toObject(factory.toBytes(contents)))); 29 | } 30 | byte[] bytes = ArrayUtils.toPrimitive(list.toArray(new Byte[list.size()])); 31 | byte[] cabinetBytes = ArrayUtils.subarray(bytes, 0, bytes.length - 7); 32 | byte[] cmdBytes = ArrayUtils.subarray(bytes, bytes.length - 7, bytes.length - 4); 33 | byte[] gidBytes = ArrayUtils.subarray(bytes, bytes.length - 4, bytes.length -3); 34 | byte[] endBytes = ArrayUtils.subarray(bytes, bytes.length - 3, bytes.length - 2); 35 | byte[] fingerIdBytes = ArrayUtils.subarray(bytes, bytes.length - 2, bytes.length); 36 | byte[] dataBytes = new byte[cmdBytes.length + fingerIdBytes.length + gidBytes.length]; 37 | System.arraycopy(cmdBytes, 0, dataBytes, 0, cmdBytes.length); 38 | System.arraycopy(fingerIdBytes, 0, dataBytes, dataBytes.length - 3, fingerIdBytes.length); 39 | System.arraycopy(gidBytes, 0, dataBytes, dataBytes.length - 1, gidBytes.length); 40 | byte[] allBytes = new byte[cabinetBytes.length + dataBytes.length + 2]; 41 | System.arraycopy(cabinetBytes, 0, allBytes, 0, cabinetBytes.length); 42 | System.arraycopy(dataBytes, 0, allBytes, cabinetBytes.length, dataBytes.length); 43 | //最后赋值,以免干扰crc校验,切勿移到最前方 44 | allBytes[allBytes.length - 2] = IntegerToByteUtil.checkout(dataBytes, 0); 45 | System.arraycopy(endBytes, 0, allBytes, allBytes.length - 1, endBytes.length); 46 | return allBytes; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiFingerBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | /** 4 | * 类功能说明:指静脉指令工厂
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiFingerBytesFactory extends MiiInteger2BytesFactory implements MiiBytesFactory { 9 | 10 | private static final int FINGER_BYTES = 1,FINGER_COUNT = 10; 11 | 12 | public MiiFingerBytesFactory() { 13 | this(0); 14 | } 15 | 16 | public MiiFingerBytesFactory(int startPos) { 17 | super(FINGER_BYTES, FINGER_COUNT, startPos); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiFingerFeatureBytesCombinedFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | import org.apache.commons.lang3.ArrayUtils; 8 | 9 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 10 | 11 | /** 12 | * 类功能说明:指静脉特征组合指令工厂
13 | * 公司名称:TF(腾飞)开源
14 | * 作者:luorongxi
15 | */ 16 | public class MiiFingerFeatureBytesCombinedFactory implements MiiBytesFactory { 17 | 18 | private MiiBytesFactory[] factorys; 19 | 20 | @SafeVarargs 21 | public MiiFingerFeatureBytesCombinedFactory(MiiBytesFactory... factorys) { 22 | this.factorys = factorys; 23 | } 24 | 25 | @Override 26 | public byte[] toBytes(E... contents) { 27 | ArrayList list = new ArrayList<>(); 28 | for(MiiBytesFactory factory : factorys){ 29 | list.addAll(Arrays.asList(ArrayUtils.toObject(factory.toBytes(contents)))); 30 | } 31 | byte[] bytes = ArrayUtils.toPrimitive(list.toArray(new Byte[list.size()])); 32 | byte[] cabinetBytes = ArrayUtils.subarray(bytes, 0, 5); 33 | byte[] cmdBytes = ArrayUtils.subarray(bytes, 5, 8); 34 | byte[] gidBytes = ArrayUtils.subarray(bytes, 8, 9); 35 | byte[] endBytes = ArrayUtils.subarray(bytes, 9, 10); 36 | byte[] fingerIdBytes = ArrayUtils.subarray(bytes, 11, 13); 37 | byte[] featureBytes = ArrayUtils.subarray(bytes, 13, bytes.length); 38 | byte[] dataBytes = new byte[cmdBytes.length + fingerIdBytes.length + gidBytes.length]; 39 | System.arraycopy(cmdBytes, 0, dataBytes, 0, cmdBytes.length); 40 | System.arraycopy(fingerIdBytes, 0, dataBytes, dataBytes.length - 3, fingerIdBytes.length); 41 | System.arraycopy(gidBytes, 0, dataBytes, dataBytes.length - 1, gidBytes.length); 42 | byte[] allBytes = new byte[cabinetBytes.length + dataBytes.length + 2 + cmdBytes.length + featureBytes.length + 2]; 43 | System.arraycopy(cabinetBytes, 0, allBytes, 0, cabinetBytes.length); 44 | System.arraycopy(dataBytes, 0, allBytes, cabinetBytes.length, dataBytes.length); 45 | //最后赋值,以免干扰crc校验,切勿移到最前方 46 | allBytes[cabinetBytes.length + dataBytes.length] = IntegerToByteUtil.checkout(dataBytes, 0); 47 | System.arraycopy(endBytes, 0, allBytes, cabinetBytes.length + dataBytes.length + 1, endBytes.length); 48 | //模板指令数据 49 | byte[] templateBytes = new byte[cmdBytes.length + featureBytes.length + 2]; 50 | byte[] statusCodeExBytes = ArrayUtils.subarray(bytes, 10, 11); 51 | System.arraycopy(statusCodeExBytes, 0, templateBytes, 0, statusCodeExBytes.length); 52 | System.arraycopy(cmdBytes, 1, templateBytes, 1, cmdBytes.length - 1); 53 | System.arraycopy(featureBytes, 0, templateBytes, cmdBytes.length, featureBytes.length); 54 | //最后赋值,以免干扰crc校验,切勿移到最前方 55 | templateBytes[templateBytes.length - 2] = IntegerToByteUtil.checkout(templateBytes, 0); 56 | System.arraycopy(endBytes, 0, templateBytes, templateBytes.length - 1, endBytes.length); 57 | System.arraycopy(templateBytes, 0, allBytes, cabinetBytes.length + dataBytes.length + 2, templateBytes.length); 58 | return allBytes; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiFingerFeatureBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | /** 4 | * 类功能说明:指静脉特征指令工厂
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiFingerFeatureBytesFactory extends MiiInteger2BytesFactory implements MiiBytesFactory { 9 | 10 | private static final int FINGER_BYTES = 1,FINGER_COUNT = 11; 11 | 12 | public MiiFingerFeatureBytesFactory() { 13 | this(0); 14 | } 15 | 16 | public MiiFingerFeatureBytesFactory(int startPos) { 17 | super(FINGER_BYTES, FINGER_COUNT, startPos); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiInteger2BytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | /** 4 | * 类功能说明:整型指令工厂
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiInteger2BytesFactory implements MiiBytesFactory { 9 | 10 | private int byteSize,startPos; 11 | private int total; 12 | 13 | public MiiInteger2BytesFactory(int byteSize,int maxLenght,int startPos){ 14 | this.byteSize = byteSize; 15 | this.startPos = startPos; 16 | this.total = byteSize * maxLenght; 17 | } 18 | 19 | @Override 20 | public byte[] toBytes(Number... contents) { 21 | byte[] bytes = new byte[total]; 22 | int length = contents.length, n = startPos; 23 | for(int i = 0; i < total; ){ 24 | if(n < length){ 25 | long content = contents[n].longValue(); 26 | for(int j = 0; j < byteSize; j++){ 27 | bytes[i] = (byte) (content & 0xFF); 28 | content >>>= 8; 29 | i++; 30 | } 31 | n++; 32 | } else { 33 | bytes[i] = 0x00; 34 | i++; 35 | } 36 | } 37 | return bytes; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiLampColorDataBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | import java.nio.charset.Charset; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import com.takeoff.iot.modbus.common.data.MiiData; 11 | import com.takeoff.iot.modbus.common.entity.AlarmLampData; 12 | import com.takeoff.iot.modbus.common.entity.LampColorData; 13 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 14 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 15 | import org.apache.commons.lang3.ArrayUtils; 16 | 17 | public class MiiLampColorDataBytesFactory implements MiiEntityBytesFactory { 18 | 19 | private static final Charset DEFAULT_CHARSET = Charset.forName("GBK"); 20 | 21 | private Charset charset; 22 | 23 | public MiiLampColorDataBytesFactory(Charset charset){ 24 | this.charset = charset; 25 | } 26 | 27 | public MiiLampColorDataBytesFactory(){ 28 | this(DEFAULT_CHARSET); 29 | } 30 | 31 | /** 32 | * 描述: 字符串转16进制byte[]
33 | * @see com.takeoff.iot.modbus.common.bytes.factory.MiiBytesFactory#toBytes(java.lang.Object[])
34 | */ 35 | @Override 36 | public byte[] toBytes(String... contents) { 37 | List resList = new ArrayList<>(); 38 | for(int i = 0; i < contents.length;i++){ 39 | String content = contents[i]; 40 | Collections.addAll(resList, ArrayUtils.toObject(content.getBytes(charset))); 41 | } 42 | return ArrayUtils.toPrimitive( 43 | resList.toArray(new Byte[resList.size()])); 44 | } 45 | 46 | @Override 47 | public byte[] toBytes(AlarmLampData alarmLampData) { 48 | byte[] alarmLampDataBytes = new byte[0]; 49 | if(!JudgeEmptyUtils.isEmpty(alarmLampData.getLampColorDataList()) && alarmLampData.getLampColorDataList().size() == 4){ 50 | int sumLength = 0; 51 | Map map = new HashMap<>(); 52 | for(int i=0; i map, int alarmTime) { 82 | byte[] alarmLampDataBytes = new byte[0]; 83 | if(sumLength > 0){ 84 | alarmLampDataBytes = new byte[sumLength + 2]; 85 | for(Map.Entry entry : map.entrySet()){ 86 | if(MiiData.LAMP_RED == entry.getKey()){ 87 | System.arraycopy(entry.getValue(), 0, alarmLampDataBytes, 0, entry.getValue().length); 88 | }else if(MiiData.LAMP_GREEN == entry.getKey()){ 89 | System.arraycopy(entry.getValue(), 0, alarmLampDataBytes, 5, entry.getValue().length); 90 | }else if(MiiData.LAMP_YELLOW == entry.getKey()){ 91 | System.arraycopy(entry.getValue(), 0, alarmLampDataBytes, 10, entry.getValue().length); 92 | }else if(MiiData.BUZZER == entry.getKey()){ 93 | System.arraycopy(entry.getValue(), 0, alarmLampDataBytes, 15, entry.getValue().length); 94 | } 95 | } 96 | //亮灯时间 97 | byte[] alarmTimeByte = new byte[0]; 98 | if(JudgeEmptyUtils.isEmpty(alarmTime)){ 99 | alarmTimeByte = IntegerToByteUtil.intToBytes(alarmTime); 100 | } 101 | System.arraycopy(alarmTimeByte, 0, alarmLampDataBytes, alarmLampDataBytes.length - 2, alarmTimeByte.length); 102 | } 103 | return alarmLampDataBytes; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiLcdBatchBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | /** 4 | * 类功能说明:LCD指令工厂
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiLcdBatchBytesFactory extends MiiInteger2BytesFactory implements MiiBytesFactory { 9 | 10 | private static final int LCD_BATCH_BYTES = 1, LCD_BATCH_COUNT = 4; 11 | 12 | public MiiLcdBatchBytesFactory() { 13 | this(0); 14 | } 15 | 16 | public MiiLcdBatchBytesFactory(int startPos) { 17 | super(LCD_BATCH_BYTES, LCD_BATCH_COUNT, startPos); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiLcdDataBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | import java.nio.charset.Charset; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import com.takeoff.iot.modbus.common.data.MiiData; 9 | import com.takeoff.iot.modbus.common.entity.LcdData; 10 | import com.takeoff.iot.modbus.common.message.MiiMessage; 11 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 12 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 13 | import org.apache.commons.lang3.ArrayUtils; 14 | 15 | /** 16 | * 类功能说明:LCD数据拼装工厂
17 | * 公司名称:TF(腾飞)开源
18 | * 作者:luorongxi
19 | */ 20 | public class MiiLcdDataBytesFactory implements MiiEntityBytesFactory { 21 | 22 | private static final Charset DEFAULT_CHARSET = Charset.forName("GBK"); 23 | 24 | private Charset charset; 25 | 26 | public MiiLcdDataBytesFactory(Charset charset){ 27 | this.charset = charset; 28 | } 29 | 30 | public MiiLcdDataBytesFactory(){ 31 | this(DEFAULT_CHARSET); 32 | } 33 | 34 | @Override 35 | public byte[] toBytes(LcdData lcdData) { 36 | //商品名 37 | byte[] nameByte = new byte[0]; 38 | if(!JudgeEmptyUtils.isEmpty(lcdData.getName())){ 39 | byte[] bytes = toBytes(lcdData.getName()); 40 | nameByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_NAME); 41 | } 42 | //商品规格 43 | byte[] specByte = new byte[0]; 44 | if(!JudgeEmptyUtils.isEmpty(lcdData.getSpec())){ 45 | byte[] bytes = toBytes(lcdData.getSpec()); 46 | specByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_SPEC); 47 | } 48 | //数量参数(库存) 49 | byte[] stockByte = new byte[0]; 50 | if(lcdData.getStock() >= 0){ 51 | byte[] bytes = toBytes(String.valueOf(lcdData.getStock())); 52 | stockByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_NUM); 53 | } 54 | //生产厂家 55 | byte[] factoryByte = new byte[0]; 56 | if(!JudgeEmptyUtils.isEmpty(lcdData.getFactory())){ 57 | byte[] bytes = toBytes(lcdData.getFactory()); 58 | factoryByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_VENDER); 59 | } 60 | //商品编码 61 | byte[] codeByte = new byte[0]; 62 | if(!JudgeEmptyUtils.isEmpty(lcdData.getCode())){ 63 | byte[] bytes = toBytes(lcdData.getCode()); 64 | codeByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_CODE); 65 | } 66 | //商品单位 67 | byte[] unitByte = new byte[0]; 68 | if(!JudgeEmptyUtils.isEmpty(lcdData.getUnit())){ 69 | byte[] bytes = toBytes(lcdData.getUnit()); 70 | unitByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_UNIT); 71 | } 72 | //上架/拣选数量 73 | byte[] quantityByte = new byte[0]; 74 | if(lcdData.getQuantity() >= 0){ 75 | byte[] bytes = toBytes(String.valueOf(lcdData.getQuantity())); 76 | quantityByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_EX1); 77 | } 78 | //任务id(识别码) 79 | byte[] taskIdByte = new byte[0]; 80 | if(!JudgeEmptyUtils.isEmpty(lcdData.getTaskId())){ 81 | byte[] bytes = toBytes(lcdData.getTaskId()); 82 | taskIdByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_SN); 83 | } 84 | //显示模式 85 | byte[] setByte = new byte[0]; 86 | if(lcdData.getShowType() >= 0){ 87 | byte[] bytes = IntegerToByteUtil.intToByteArray(lcdData.getShowType(), 1); 88 | setByte = getDataBytes(bytes, MiiData.AMP_LCD_PARA_SET); 89 | } 90 | //所有LCD显示数据 91 | byte[] lcdDataBytes = new byte[nameByte.length + specByte.length + stockByte.length + factoryByte.length + codeByte.length + unitByte.length + quantityByte.length + taskIdByte.length + setByte.length]; 92 | System.arraycopy(nameByte, 0, lcdDataBytes, 0, nameByte.length); 93 | System.arraycopy(specByte, 0, lcdDataBytes, nameByte.length, specByte.length); 94 | System.arraycopy(stockByte, 0, lcdDataBytes, nameByte.length + specByte.length, stockByte.length); 95 | System.arraycopy(factoryByte, 0, lcdDataBytes, nameByte.length + specByte.length + stockByte.length, factoryByte.length); 96 | System.arraycopy(codeByte, 0, lcdDataBytes, nameByte.length + specByte.length + stockByte.length + factoryByte.length, codeByte.length); 97 | System.arraycopy(unitByte, 0, lcdDataBytes, nameByte.length + specByte.length + stockByte.length + factoryByte.length + codeByte.length, unitByte.length); 98 | System.arraycopy(quantityByte, 0, lcdDataBytes, nameByte.length + specByte.length + stockByte.length + factoryByte.length + codeByte.length + unitByte.length, quantityByte.length); 99 | System.arraycopy(taskIdByte, 0, lcdDataBytes, nameByte.length + specByte.length + stockByte.length + factoryByte.length + codeByte.length + unitByte.length + quantityByte.length, taskIdByte.length); 100 | System.arraycopy(setByte, 0, lcdDataBytes, nameByte.length + specByte.length + stockByte.length + factoryByte.length + codeByte.length + unitByte.length + quantityByte.length + taskIdByte.length, setByte.length); 101 | return lcdDataBytes; 102 | } 103 | 104 | /** 105 | * 拼装LCD显示数据(格式:命令符1位 + 数据长度1位 + 数据字符串) 106 | * 参数:@param bytes 107 | * 参数:@param flag 108 | * 参数:@return
109 | */ 110 | private byte[] getDataBytes(byte[] bytes, int flag) { 111 | byte[] dateBytes = new byte[bytes.length + 2]; 112 | dateBytes[MiiMessage.BEGIN_INDEX] = (byte) flag; 113 | byte[] lengthBytes = IntegerToByteUtil.intToByteArray(bytes.length, 1); 114 | System.arraycopy(lengthBytes, 0, dateBytes, 1, lengthBytes.length); 115 | System.arraycopy(bytes, 0, dateBytes , 2, bytes.length); 116 | return dateBytes; 117 | } 118 | 119 | /** 120 | * 字符串转16进制byte[] 121 | * @param contents 122 | * @return 123 | */ 124 | @Override 125 | public byte[] toBytes(String... contents) { 126 | List resList = new ArrayList<>(); 127 | for(int i = 0; i < contents.length;i++){ 128 | String content = contents[i]; 129 | Collections.addAll(resList, ArrayUtils.toObject(content.getBytes(charset))); 130 | } 131 | return ArrayUtils.toPrimitive( 132 | resList.toArray(new Byte[resList.size()])); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiMultiLockBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | /** 4 | * 类功能说明:多锁指令工厂
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiMultiLockBytesFactory extends MiiInteger2BytesFactory implements MiiBytesFactory { 9 | 10 | private static final int MULTI_LOCK_BYTES = 1,MULTI_LOCK_COUNT = 4; 11 | 12 | public MiiMultiLockBytesFactory() { 13 | this(0); 14 | } 15 | 16 | public MiiMultiLockBytesFactory(int startPos) { 17 | super(MULTI_LOCK_BYTES, MULTI_LOCK_COUNT, startPos); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiMultiLockDataBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | public class MiiMultiLockDataBytesFactory implements MiiBytesFactory { 4 | 5 | private static final int BYTE_SIZE = 1,START_POS = 0; 6 | 7 | @Override 8 | public byte[] toBytes(Integer... contents) { 9 | byte[] bytes = new byte[contents.length]; 10 | int length = contents.length, n = START_POS; 11 | for(int i = 0; i < contents.length; ){ 12 | if(n < length){ 13 | long content = contents[n].longValue(); 14 | for(int j = 0; j < BYTE_SIZE; j++){ 15 | bytes[i] = (byte) (content & 0xFF); 16 | content >>>= 8; 17 | i++; 18 | } 19 | n++; 20 | } else { 21 | bytes[i] = 0x00; 22 | i++; 23 | } 24 | } 25 | return bytes; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiSlotBytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | /** 4 | * 类功能说明:短整型指令工厂
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiSlotBytesFactory extends MiiInteger2BytesFactory implements MiiBytesFactory { 9 | 10 | private static final int INTEGER_BYTES = 1,INTEGER_COUNT = 5; 11 | 12 | public MiiSlotBytesFactory() { 13 | this(0); 14 | } 15 | 16 | public MiiSlotBytesFactory(int startPos) { 17 | super(INTEGER_BYTES, INTEGER_COUNT, startPos); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/bytes/factory/MiiStrings2BytesFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.bytes.factory; 2 | 3 | import com.takeoff.iot.modbus.common.utils.BytesToHexUtil; 4 | 5 | /** 6 | * 类功能说明:字符串类型指令工厂
7 | * 公司名称:TF(腾飞)开源
8 | * 作者:luorongxi
9 | */ 10 | public class MiiStrings2BytesFactory implements MiiBytesFactory { 11 | 12 | private static final int START_POS = 0; 13 | 14 | @Override 15 | public byte[] toBytes(String... contents) { 16 | String content = contents[START_POS]; 17 | return BytesToHexUtil.hexToByteArray(content); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/Finger.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | public interface Finger { 4 | 5 | /** 6 | * 返回枚举标识 7 | * @return 枚举标识 8 | */ 9 | int fingerType(); 10 | 11 | /** 12 | * 返回状态码 13 | * @return 状态码 14 | */ 15 | int fingerStatus(); 16 | 17 | /** 18 | * 返回指令码 19 | * @return 指令码 20 | */ 21 | int fingerCmd(); 22 | 23 | /** 24 | * 返回手指ID 25 | * @return 手指ID 26 | */ 27 | int fingerId(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiBackLightData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | /** 4 | * 类功能说明:背光灯指令处理
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiBackLightData extends MiiInData { 9 | 10 | public MiiBackLightData(byte[] datas) { 11 | super(datas); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiBarcodeData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | import com.takeoff.iot.modbus.common.utils.BytesToHexUtil; 4 | import org.apache.commons.lang3.ArrayUtils; 5 | 6 | /** 7 | * 类功能说明:扫码指令处理
8 | * 公司名称:TF(腾飞)开源
9 | * 作者:luorongxi
10 | */ 11 | public class MiiBarcodeData extends MiiHexData { 12 | 13 | public MiiBarcodeData(byte[] datas) { 14 | super(datas); 15 | byte[] barcodeByte = ArrayUtils.subarray(datas, CONTENT_INDEX, datas.length); 16 | content = BytesToHexUtil.asciiToString(barcodeByte); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiByteArrayData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | /** 4 | * 类功能说明:包含数组类型的指令数据处理
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiByteArrayData implements MiiData { 9 | 10 | protected byte[] datas; 11 | 12 | public MiiByteArrayData(byte[] datas) { 13 | this.datas = datas; 14 | } 15 | 16 | public byte[] toBytes() { 17 | return datas; 18 | } 19 | 20 | public int length() { 21 | return datas.length; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiCardData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | /** 4 | * 类功能说明:刷卡指令处理
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiCardData extends MiiHexData { 9 | 10 | public MiiCardData(byte[] datas) { 11 | super(datas); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiFingerData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 7 | 8 | import org.apache.commons.lang3.ArrayUtils; 9 | import org.bouncycastle.util.encoders.Hex; 10 | 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | /** 14 | * 类功能说明:指静脉指令处理
15 | * 公司名称:TF(腾飞)开源
16 | * 作者:luorongxi
17 | */ 18 | @Slf4j 19 | public class MiiFingerData extends MiiSlotData implements Finger { 20 | 21 | private int fingerType; 22 | private int fingerCmd; 23 | private int fingerId; 24 | private List fingerIdList; 25 | private byte[] fingerTemplate; 26 | 27 | public MiiFingerData(byte[] datas) { 28 | super(datas); 29 | fingerDataHandle(); 30 | } 31 | 32 | private void fingerDataHandle() { 33 | fingerType = datas[FINGERTYPE_INDEX] & 0xFF; 34 | boolean userTran = false; 35 | switch (fingerType) { 36 | case WDH320S_USER_Veri_GetID: 37 | log.info("WDH320S_USER_Veri_GetID:" +Hex.toHexString(datas)); 38 | byte[] fingerByte = ArrayUtils.subarray(datas, 8, datas.length - 3); 39 | fingerId = IntegerToByteUtil.bytesToInt(fingerByte); 40 | break; 41 | case WDH320S_USER_Tran: 42 | log.info("WDH320S_USER_Tran:" +Hex.toHexString(datas)); 43 | fingerDeviceData(); 44 | userTran = true; 45 | break; 46 | } 47 | //如果是透传则不用再次解析 48 | if(!userTran){ 49 | fingerDeviceData(); 50 | } 51 | } 52 | 53 | private void fingerDeviceData() { 54 | fingerCmd = datas[FINGERCMD_INDEX] & 0xFF; 55 | switch (fingerCmd) { 56 | case CMD_UPLOAD_ALL_ID://上传所有手指 ID 57 | log.info("CMD_UPLOAD_ALL_ID:" +Hex.toHexString(datas)); 58 | byte[] fingerCountByte = ArrayUtils.subarray(datas, 16, 18); 59 | int fingerCount = IntegerToByteUtil.bytesToInt(fingerCountByte); 60 | byte[] fingerIds = ArrayUtils.subarray(datas, 18, 18 + fingerCount * 3); 61 | fingerIdList = new ArrayList(); 62 | for (int i = 0; i < fingerIds.length; i += 3) { 63 | byte[] fingerIdByte = new byte[2]; 64 | fingerIdByte[0] = fingerIds[i]; 65 | fingerIdByte[1] = fingerIds[i + 1]; 66 | fingerId = IntegerToByteUtil.bytesToInt(fingerIdByte); 67 | fingerIdList.add(fingerId); 68 | } 69 | break; 70 | case CMD_UPLOAD_INFOR_TEMPLATES://上传指定手指以及对应模板 71 | log.info("CMD_UPLOAD_INFOR_TEMPLATES:" +Hex.toHexString(datas)); 72 | //大于9说明有附加指令 73 | if (datas.length > 9) { 74 | byte[] fingerIdByte = ArrayUtils.subarray(datas, 16, 18); 75 | fingerId = IntegerToByteUtil.bytesToInt(fingerIdByte); 76 | fingerTemplate = ArrayUtils.subarray(datas, 16, datas.length - 2); 77 | } 78 | break; 79 | case CMD_REG_END://注册手指 80 | log.info("CMD_REG_END:" +Hex.toHexString(datas)); 81 | byte gid = datas[GID_INDEX]; 82 | if (gid == 0x00) { 83 | fingerType = WDH320S_USER_Reg_End; 84 | } else { 85 | fingerType = WDH320S_USER_FALT; 86 | } 87 | break; 88 | case CMD_UPLOAD_VERSION://获取固件版本号 89 | log.info("CMD_UPLOAD_VERSION:" +Hex.toHexString(datas)); 90 | break; 91 | } 92 | } 93 | 94 | /** 95 | * 返回枚举标识 96 | * @return 枚举标识 97 | */ 98 | @Override 99 | public int fingerType() { 100 | return fingerType; 101 | } 102 | 103 | /** 104 | * 返回状态码 105 | * @return 状态码 106 | */ 107 | @Override 108 | public int fingerStatus() { 109 | return datas[FINGERSTATUS_INDEX] & 0xFF; 110 | } 111 | 112 | /** 113 | * 返回指令码 114 | * @return 指令码 115 | */ 116 | @Override 117 | public int fingerCmd() { 118 | return fingerCmd; 119 | } 120 | 121 | /** 122 | * 返回手指ID 123 | * @return 手指ID 124 | */ 125 | @Override 126 | public int fingerId() { 127 | return fingerId; 128 | } 129 | 130 | /** 131 | * 返回手指ID集合 132 | * @return 手指ID集合 133 | */ 134 | public List fingerIdList() { 135 | return fingerIdList; 136 | } 137 | 138 | /** 139 | * 返回指静脉模板 140 | * @return 指静脉模板 141 | */ 142 | public byte[] fingerTemplate() { 143 | return fingerTemplate; 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiHeartBeatData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * 类功能说明:心跳指令数据
7 | * 公司名称:TF(腾飞)开源
8 | * 作者:luorongxi
9 | */ 10 | public class MiiHeartBeatData extends MiiStringData { 11 | 12 | static final int DEVICE_GROUPNO_INDEX = 1; 13 | 14 | public MiiHeartBeatData(byte[] datas) { 15 | super(datas); 16 | } 17 | 18 | /** 19 | * 返回设备组编码 20 | * @return 设备编码 21 | */ 22 | public String deviceGroup() { 23 | return StringUtils.isBlank(content()) ? 24 | String.valueOf(toBytes()[DEVICE_GROUPNO_INDEX]) : content(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiHexData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | import org.bouncycastle.util.encoders.Hex; 3 | 4 | /** 5 | * 类功能说明:包含反馈的十六进制信息内容的指令数据处理
6 | * 公司名称:TF(腾飞)开源
7 | * 作者:luorongxi
8 | */ 9 | public class MiiHexData extends MiiStringData { 10 | 11 | public MiiHexData(byte[] datas) { 12 | super(datas); 13 | content = Hex.toHexString(datas, CONTENT_INDEX, length() - CONTENT_INDEX); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiHumitureData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | /** 4 | * 类功能说明:温湿度指令数据
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiHumitureData extends MiiSlotData { 9 | 10 | private double temperature; 11 | private double humidity; 12 | 13 | 14 | public MiiHumitureData(byte[] datas) { 15 | super(datas); 16 | short humiture = (short)(((datas[8] & 0x00FF) << 8) | (0x00FF & datas[7])); 17 | double td = ((humiture * 175.72) / 65536.0 - 46.85); 18 | temperature = (double)Math.round(td*100)/100; 19 | humiture =(short)(((datas[10] & 0x00FF) << 8) | (0x00FF & datas[9])); 20 | double hd = ((humiture * 125.0) / 65536.0 - 6); 21 | humidity = (double)Math.round(hd*100)/100; 22 | } 23 | 24 | public double temperature(){ 25 | return temperature; 26 | } 27 | 28 | public double humidity(){ 29 | return humidity; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiInData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | public class MiiInData extends MiiSlotData { 4 | 5 | public MiiInData(byte[] datas){ 6 | super(datas); 7 | } 8 | 9 | /** 10 | * 返回状态码 11 | * @return 状态码 12 | */ 13 | public int statusCode() { 14 | return toBytes()[CONTENT_INDEX] & 0xFF; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiIntegerData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | import org.apache.commons.lang3.ArrayUtils; 4 | 5 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 6 | 7 | /** 8 | * 类功能说明:包含整型的指令数据处理
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | public class MiiIntegerData extends MiiSlotData { 13 | 14 | protected Integer content; 15 | 16 | public MiiIntegerData(byte[] datas) { 17 | super(datas); 18 | if(length() > CONTENT_INDEX){ 19 | byte[] countByte = ArrayUtils.subarray(datas, CONTENT_INDEX, datas.length); 20 | content = IntegerToByteUtil.bytesToInt(countByte); 21 | }else{ 22 | content = null; 23 | } 24 | } 25 | 26 | public Integer content(){ 27 | return content; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiLockData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.takeoff.iot.modbus.common.entity.LockStatus; 7 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 8 | 9 | import org.apache.commons.lang3.ArrayUtils; 10 | 11 | /** 12 | * 类功能说明:门锁指令数据
13 | * 公司名称:TF(腾飞)开源
14 | * 作者:luorongxi
15 | */ 16 | public class MiiLockData extends MiiSlotData implements MiiData { 17 | 18 | private List list; 19 | 20 | public MiiLockData(byte[] datas) { 21 | super(datas); 22 | byte[] dataByte = ArrayUtils.subarray(datas, CONTENT_INDEX, datas.length); 23 | if(dataByte.length > 3){ 24 | dataByte = ArrayUtils.subarray(dataByte, CONTENT_INDEX - 1, dataByte.length); 25 | } 26 | list = new ArrayList(); 27 | LockStatus lockStatus = null; 28 | for(int i = 0; i < dataByte.length; i++){ 29 | byte[] bytes = new byte[1]; 30 | bytes[0] = dataByte[i]; 31 | Integer content = IntegerToByteUtil.bytesToInt(bytes); 32 | if (i % 3 == 0){ 33 | lockStatus = new LockStatus(); 34 | lockStatus.setLockNo(content); 35 | if(dataByte.length == 1){ 36 | list.add(lockStatus); 37 | } 38 | }else if ((i+1)%3 == 0){ 39 | lockStatus.setSensorStatus(content); 40 | list.add(lockStatus); 41 | }else{ 42 | lockStatus.setLockStatus(content); 43 | } 44 | } 45 | } 46 | 47 | public List list(){ 48 | return list; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiSlotData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | /** 4 | * 类功能说明:设备信息数据
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class MiiSlotData extends MiiByteArrayData { 9 | 10 | public MiiSlotData(byte[] datas) { 11 | super(datas); 12 | } 13 | 14 | /** 15 | * 返回设备号 16 | * @return 设备号 17 | */ 18 | public int device() { 19 | return toBytes()[DEVICE_INDEX]; 20 | } 21 | 22 | /** 23 | * 返回层号 24 | * @return 层号 25 | */ 26 | public int shelf() { 27 | return toBytes()[SHELF_INDEX]; 28 | } 29 | 30 | /** 31 | * 返回槽位号 32 | * @return 槽位号 33 | */ 34 | public int slot() { 35 | return toBytes()[SLOT_INDEX]; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/data/MiiStringData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.data; 2 | 3 | import org.apache.commons.lang3.ArrayUtils; 4 | 5 | /** 6 | * 类功能说明:包含反馈内容的指令数据处理
7 | * 公司名称:TF(腾飞)开源
8 | * 作者:luorongxi
9 | */ 10 | public class MiiStringData extends MiiSlotData { 11 | 12 | protected String content; 13 | 14 | public MiiStringData(byte[] datas) { 15 | super(datas); 16 | if(length() > CONTENT_INDEX){ 17 | content = new String(ArrayUtils.subarray(datas, CONTENT_INDEX, length())); 18 | }else{ 19 | content = null; 20 | } 21 | } 22 | 23 | /** 24 | * 返回柜体的反馈内容信息 25 | * @return 反馈内容信息 26 | */ 27 | public String content() { 28 | return content; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/entity/AlarmLampData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.entity; 2 | 3 | import java.util.List; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class AlarmLampData extends DeviceData { 9 | 10 | /** 11 | * 灯的颜色控制数据 12 | */ 13 | private List lampColorDataList; 14 | 15 | /** 16 | * 报警时间(毫秒数) 17 | */ 18 | private int alarmTime; 19 | } 20 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/entity/ChannelConnectData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.entity; 2 | 3 | import com.takeoff.iot.modbus.common.enums.DeviceConnectEnum; 4 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 5 | import org.springframework.context.ApplicationEvent; 6 | 7 | import lombok.Getter; 8 | 9 | @Getter 10 | public class ChannelConnectData extends ApplicationEvent { 11 | 12 | /** 13 | * 描述: TODO
14 | * Fields serialVersionUID : TODO
15 | */ 16 | private static final long serialVersionUID = 2111432846029949421L; 17 | 18 | private String deviceAddress = null; 19 | 20 | private Integer deviceConnect = null; 21 | 22 | private String connectMsg = null; 23 | 24 | public ChannelConnectData(Object source, Integer deviceConnect, String deviceAddress, int count) { 25 | super(source); 26 | if(!JudgeEmptyUtils.isEmpty(deviceAddress)){ 27 | this.deviceConnect = deviceConnect; 28 | this.deviceAddress = deviceAddress; 29 | this.connectMsg = "设备:"+ deviceAddress + DeviceConnectEnum.getName(deviceConnect) + ",累计:"+ count + "次"; 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/entity/DeviceData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.entity; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class DeviceData { 7 | 8 | /** 9 | * 设备IP 10 | */ 11 | private String deviceIp; 12 | 13 | /** 14 | * 设备号 15 | */ 16 | private int device; 17 | 18 | /** 19 | * 层号 20 | */ 21 | private int shelf; 22 | 23 | /** 24 | * 槽位号 25 | */ 26 | private int slot; 27 | } 28 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/entity/LampColorData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.entity; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class LampColorData { 7 | 8 | /** 9 | * 类型(0:红色灯;1:绿色灯;2:黄色灯;3:蜂鸣器) 10 | */ 11 | private int tyte; 12 | 13 | /** 14 | * 开关(0:关;1:开) 15 | */ 16 | private int onOff; 17 | 18 | /** 19 | * 亮灯时间(毫秒数) 20 | */ 21 | private int onTime; 22 | 23 | /** 24 | * 灭灯时间(毫秒数) 25 | */ 26 | private int offTime; 27 | } 28 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/entity/LcdData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 类功能说明:门锁状态实体类
7 | * 公司名称:TF(腾飞)开源
8 | * 作者:luorongxi
9 | */ 10 | @Data 11 | public class LcdData extends DeviceData { 12 | 13 | /** 14 | * 任务id(识别码) 15 | */ 16 | private String taskId; 17 | 18 | /** 19 | * 显示模式(0:无;1:清除匹配内容,需要与识别码一起下发;2:清除全部显示数据;3:刷新时间同步;4:层板断电;5:层板上电;6:显示上架模式;7:显示库存模式;8:显示拣选模式) 20 | */ 21 | private int showType; 22 | 23 | /** 24 | * 标题 25 | */ 26 | private String title; 27 | 28 | /** 29 | * 商品编码 30 | */ 31 | private String code; 32 | 33 | /** 34 | * 商品名称 35 | */ 36 | private String name; 37 | 38 | /** 39 | * 商品规格 40 | */ 41 | private String spec; 42 | 43 | /** 44 | * 生产厂家 45 | */ 46 | private String factory; 47 | 48 | /** 49 | * 商品单位 50 | */ 51 | private String unit; 52 | 53 | /** 54 | * 库存数量 55 | */ 56 | private int stock; 57 | 58 | /** 59 | * 上架/拣选数量 60 | */ 61 | private int quantity; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/entity/LockStatus.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 类功能说明:门锁状态实体类
7 | * 公司名称:TF(腾飞)开源
8 | * 作者:luorongxi
9 | */ 10 | @Data 11 | public class LockStatus { 12 | 13 | /** 14 | * 返回门锁号 15 | * @return 门锁号 16 | */ 17 | private int lockNo; 18 | 19 | /** 20 | * 返回门锁状态码 21 | * @return 门锁状态码 22 | */ 23 | private int lockStatus; 24 | 25 | /** 26 | * 返回传感器状态码 27 | * @return 传感器状态码 28 | */ 29 | private int sensorStatus; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/enums/DeviceConnectEnum.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.enums; 2 | 3 | public enum DeviceConnectEnum { 4 | 5 | ON_LINE(0, "已连接"), 6 | BREAK_OFF(1, "已断开"), 7 | ABNORMAL(2, "处理业务异常"), 8 | BREAK_RECONNECT(3, "断线重连"), 9 | ; 10 | 11 | // 成员变量 12 | private Integer key; 13 | private String value; 14 | 15 | // 构造方法 16 | private DeviceConnectEnum(Integer key, String value) { 17 | this.key = key; 18 | this.value = value; 19 | } 20 | 21 | // 普通方法 22 | public static String getName(Integer key) { 23 | for (DeviceConnectEnum c : DeviceConnectEnum.values()) { 24 | if (c.getKey().equals(key)) { 25 | return c.value; 26 | } 27 | } 28 | return null; 29 | } 30 | 31 | public Integer getKey() { 32 | return key; 33 | } 34 | 35 | public String getValue() { 36 | return value; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/enums/MitModbusCacheKeyEnum.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.enums; 2 | 3 | /** 4 | * 类功能说明:Bytes与Hex转换工具类
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public enum MitModbusCacheKeyEnum { 9 | 10 | CLIENT_CHANNEL("CLIENT_CHANNEL", "客户端通讯管道"), 11 | ; 12 | 13 | // 成员变量 14 | private String key; 15 | private String value; 16 | 17 | // 构造方法 18 | private MitModbusCacheKeyEnum(String key, String value) { 19 | this.key = key; 20 | this.value = value; 21 | } 22 | 23 | // 普通方法 24 | public static String getName(String key) { 25 | for (MitModbusCacheKeyEnum c : MitModbusCacheKeyEnum.values()) { 26 | if (c.getKey().equals(key)) { 27 | return c.value; 28 | } 29 | } 30 | return null; 31 | } 32 | 33 | public String getKey() { 34 | return key; 35 | } 36 | 37 | public String getValue() { 38 | return value; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/message/MiiByteArrayMessage.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.message; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiByteArrayData; 4 | import com.takeoff.iot.modbus.common.data.MiiData; 5 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 6 | 7 | import org.apache.commons.lang3.ArrayUtils; 8 | 9 | public class MiiByteArrayMessage implements MiiMessage { 10 | 11 | private String deviceGroup; 12 | private byte[] bytes; 13 | private MiiData data; 14 | 15 | public MiiByteArrayMessage(String deviceGroup, byte[] bytes){ 16 | this.deviceGroup = deviceGroup; 17 | this.bytes = bytes; 18 | this.data = new MiiByteArrayData(ArrayUtils.subarray(bytes, MiiMessage.COMMAND_INDEX, bytes.length - 3)); 19 | } 20 | 21 | @Override 22 | public String deviceGroup() { 23 | return this.deviceGroup; 24 | } 25 | 26 | @Override 27 | public int length() { 28 | byte[] dataLength = ArrayUtils.subarray(bytes, DATA_INDEX, COMMAND_INDEX); 29 | return IntegerToByteUtil.bytesToInt(dataLength); 30 | } 31 | 32 | @Override 33 | public int command() { 34 | return bytes[COMMAND_INDEX] & 0x7F; 35 | } 36 | 37 | @Override 38 | public int type() { 39 | return SEND; 40 | } 41 | 42 | @Override 43 | public MiiData data() { 44 | return this.data; 45 | } 46 | 47 | @Override 48 | public byte[] toBytes() { 49 | return bytes; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/message/MiiMessage.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.message; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiData; 4 | 5 | /** 6 | * 类功能说明:设备通讯信息
7 | * 公司名称:TF(腾飞)开源
8 | * 作者:luorongxi
9 | */ 10 | public interface MiiMessage { 11 | 12 | /** 13 | * 起始符 14 | */ 15 | final byte[] BEGIN_BYTES = {(byte) 0x7E}; 16 | 17 | /** 18 | * 结束符 19 | */ 20 | final byte[] END_BYTES = {(byte) 0x7F}; 21 | 22 | /** 23 | * 数据长度.
24 | * 25 | * 26 | * 27 | * 28 | * 29 | *
START_SIZE DATA_SIZE COMMAND_SIZE DEVICE_SIZE
起始符 数据长度 指令位 设备地址
SHELF_SIZE SLOT_SIZE CHECKCODE_SIZE END_SIZE
层地址 槽地址 校验位 结束符

30 | */ 31 | final int BEGIN_SIZE = 1, DATA_SIZE = 2, COMMAND_SIZE = 1, DEVICE_SIZE = 1, SHELF_SIZE = 1, SLOT_SIZE = 1, CHECKCODE_SIZE = 2, END_SIZE = 1; 32 | 33 | /** 34 | * 数据下标.
35 | * 36 | * 37 | * 38 | *
HEADER_LINDEX DATA_LINDEX DATA_LINDEX COMMAND_INDEX
起始符 数据长度开始位 数据长度结束位 指令位

39 | */ 40 | final int BEGIN_INDEX = 0, DATA_INDEX = 1, COMMAND_INDEX = 3; 41 | 42 | /** 43 | * command() 返回消息的指令类型.
44 | * 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * 53 | * 54 | * 55 | * 56 | * 57 | *
ACK LED LOCK CARD POWER
应答类型 LED灯 门锁命令 读卡器 层板供电控制
STATUS GETOLINE BACKLIGHT LCD LCDCONFIG
上报状态 检查在线设备 背光灯控制 LCD内容显示数据传输命令 LCD配置命令
LCDPOWER LCDCLOCK HM ESCALE BARCODE
LCD电源供电控制指令 LCD显示同步时间 温湿度数据 电子称相关命令 扫描头数据
FINGER MCUCONFIG OTA APDS INFO
手指识别数据 配置信息 在线升级 光电传感器 信息
WLED WLEDACTION CABPH LAYPH CARDPH
三色灯报警器 按键输入 设备接口透传 层接口透传 读卡器接口透传
HEARTBEAT CHECKSLEVE
空闲时与上位机连接的心跳包(5秒) 主机查询从机数据命令

58 | */ 59 | final int ACK = 0x01,LED = 0x02, LOCK = 0x03, CARD = 0x04, POWER = 0x05, 60 | STATUS = 0x06, GETOLINE = 0x07, BACKLIGHT = 0x08, LCD = 0x09, LCDCONFIG = 0x0A, 61 | LCDPOWER = 0x0B, LCDCLOCK = 0x0C, HM = 0x0D, ESCALE = 0x0E, BARCODE = 0x0F, 62 | FINGER = 0x10, MCUCONFIG = 0x11, OTA = 0x12, APDS = 0x13, INFO = 0x14, 63 | WLED = 0x15, WLEDACTION = 0x16, CABPH = 0x17, LAYPH = 0x18, CARDPH = 0x19, 64 | HEARTBEAT = 0x3E, CHECKSLEVE = 0x3F; 65 | 66 | /** 67 | * 指静脉数据常量.
68 | * 69 | * 70 | * 71 | *
USER_CODE STATUS_CODE DEVID GID STATUS_CODE_EX
用户码 状态码 Devid GID(分组) 指静脉模板状态码

72 | */ 73 | final int USER_CODE = 0x00, STATUS_CODE = 0x40, DEVID = 0xFF, GID = 0x00, END_CODE = 0x0D, STATUS_CODE_EX = 0x3E; 74 | 75 | /** 76 | * type() 信息类型.
77 | * SEND 发送
78 | * RECV 接收
79 | */ 80 | final int SEND = 0x00,RECV = 0x80; 81 | 82 | /** 83 | * 返回设备组编码. 84 | * @return 设备组编码 85 | */ 86 | String deviceGroup(); 87 | 88 | /** 89 | * 返回消息的指令类型.
90 | * 91 | * 92 | * 93 | * 94 | * 95 | *
UNLOCK LED BACKLIGHT READRF ENDRF PRINTING TEMPANDHUM
开锁 LED灯 背光灯 开始读RFID RFID读取停止 打印 温度和湿度
REGOFINGER CHECKFINGER DELFINGER CARD BARCODE CLOSEDOOR
注册指纹 检查指纹 删除指纹 工卡信息 扫码信息 关门信息

96 | * @return 指令类型
97 | */ 98 | int command(); 99 | 100 | /** 101 | * 返回消息的数据对象的字节长度. 102 | * @return 数据对象的字节长度 103 | */ 104 | int length(); 105 | 106 | /** 107 | * 返回信息类型. 108 | * SEND 发送
109 | * RECV 接收
110 | * @return 信息类型 111 | */ 112 | int type(); 113 | 114 | /** 115 | * 返回消息的数据对象. 116 | * @return 数据对象 117 | */ 118 | MiiData data(); 119 | 120 | /** 121 | * 返回消息的字节数组. 122 | * @return 字节数组 123 | */ 124 | byte[] toBytes(); 125 | } 126 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/message/factory/MiiMessageFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.message.factory; 2 | 3 | import com.takeoff.iot.modbus.common.message.MiiMessage; 4 | 5 | public interface MiiMessageFactory { 6 | 7 | MiiMessage buildMessage(String deviceGroup, E... datas); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/message/factory/MiiOutMessageFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.message.factory; 2 | 3 | import com.takeoff.iot.modbus.common.bytes.factory.MiiBytesFactory; 4 | import com.takeoff.iot.modbus.common.message.MiiByteArrayMessage; 5 | import com.takeoff.iot.modbus.common.message.MiiMessage; 6 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 7 | import com.takeoff.iot.modbus.common.utils.ModbusCrc16Utils; 8 | 9 | import org.apache.commons.lang3.ArrayUtils; 10 | 11 | /** 12 | * 类功能说明:指令下发处理工厂
13 | * 公司名称:TF(腾飞)开源
14 | * 作者:luorongxi
15 | */ 16 | public class MiiOutMessageFactory implements MiiMessageFactory { 17 | 18 | private MiiBytesFactory factory; 19 | 20 | public MiiOutMessageFactory(MiiBytesFactory factory) { 21 | this.factory = factory; 22 | } 23 | 24 | @Override 25 | public MiiMessage buildMessage(String deviceGroup, E... datas) { 26 | byte[] bytes = factory.toBytes(datas); 27 | byte[] msg = new byte[MiiMessage.BEGIN_SIZE + MiiMessage.DATA_SIZE + bytes.length + MiiMessage.CHECKCODE_SIZE + MiiMessage.END_SIZE]; 28 | msg[MiiMessage.BEGIN_INDEX] = MiiMessage.BEGIN_BYTES[MiiMessage.BEGIN_INDEX]; 29 | byte[] lengthBytes = IntegerToByteUtil.intToByteArray(bytes.length, MiiMessage.DATA_SIZE); 30 | System.arraycopy(lengthBytes, 0, msg, MiiMessage.DATA_INDEX, lengthBytes.length); 31 | System.arraycopy(bytes, 0, msg , MiiMessage.COMMAND_INDEX, bytes.length); 32 | byte[] checkData = ArrayUtils.subarray(msg, MiiMessage.DATA_INDEX, msg.length - 3); 33 | byte[] checkCodeBytes = ModbusCrc16Utils.getCrcByte(checkData); 34 | System.arraycopy(checkCodeBytes, 0, msg , msg.length - 3, checkCodeBytes.length); 35 | System.arraycopy(MiiMessage.END_BYTES, 0, msg , msg.length - 1, 1); 36 | return new MiiByteArrayMessage(deviceGroup, msg); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/utils/BytesToHexUtil.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.utils; 2 | 3 | /** 4 | * 类功能说明:Bytes与Hex转换工具类
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public class BytesToHexUtil { 9 | 10 | /** 11 | * Hex字符串转byte 12 | * @param inHex 待转换的Hex字符串 13 | * @return 转换后的byte 14 | */ 15 | public static byte hexToByte(String inHex) { 16 | return (byte) Integer.parseInt(inHex, 16); 17 | } 18 | 19 | /** 20 | * hex字符串转byte数组 21 | * @param inHex 待转换的Hex字符串 22 | * @return 转换后的byte数组结果 23 | */ 24 | public static byte[] hexToByteArray(String inHex) { 25 | int hexlen = inHex.length(); 26 | byte[] result; 27 | if (hexlen % 2 == 1) { 28 | // 奇数 29 | hexlen++; 30 | result = new byte[(hexlen / 2)]; 31 | inHex = "0" + inHex; 32 | } else { 33 | // 偶数 34 | result = new byte[(hexlen / 2)]; 35 | } 36 | int j = 0; 37 | for (int i = 0; i < hexlen; i += 2) { 38 | result[j] = hexToByte(inHex.substring(i, i + 2)); 39 | j++; 40 | } 41 | return result; 42 | } 43 | 44 | /** 45 | * 字节转十六进制 46 | * @param b 需要进行转换的byte字节 47 | * @return 转换后的Hex字符串 48 | */ 49 | public static String byteToHex(byte b){ 50 | String hex = Integer.toHexString(b & 0xFF); 51 | if(hex.length() < 2){ 52 | hex = "0" + hex; 53 | } 54 | return hex; 55 | } 56 | 57 | /** 58 | * 数组转换成十六进制字符串 59 | * @param bArray 需要转换的byte数组 60 | * @return 转换后的Hex字符串 61 | */ 62 | public static String bytesToHexString(byte[] bArray) { 63 | StringBuffer sb = new StringBuffer(bArray.length); 64 | for (int i = 0; i < bArray.length; i++) { 65 | String hexStr = Integer.toHexString(0xFF & bArray[i]); 66 | if (hexStr.length() < 2) 67 | sb.append(0); 68 | sb.append(hexStr.toUpperCase()); 69 | } 70 | return sb.toString(); 71 | } 72 | 73 | /** 74 | * 函数功能说明 : Ascii编码byte数组转换字符串
75 | * 修改者名字:
76 | * 修改日期:
77 | * 修改内容:
78 | * 作者:Lion
79 | * 参数:@param bArray 80 | * 参数:@return
81 | * return:String
82 | */ 83 | public static String asciiToString(byte[] bArray) { 84 | StringBuilder sbu = new StringBuilder(); 85 | byte b; 86 | int i; 87 | byte[] arrayOfByte; 88 | for (i = (arrayOfByte = bArray).length, b = 0; b < i; ) { 89 | byte b1 = arrayOfByte[b]; 90 | if (b1 == 0) 91 | break; 92 | sbu.append((char)Integer.parseInt(String.valueOf(b1))); 93 | b++; 94 | } 95 | //去除转义字符、回车等范围 96 | return sbu.toString().replaceAll("[\\u0000-\\u001f\b]",""); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/utils/CacheUtils.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.Executors; 6 | import java.util.concurrent.Future; 7 | import java.util.concurrent.ScheduledExecutorService; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * 类功能说明:Bytes与Hex转换工具类
12 | * 公司名称:TF(腾飞)开源
13 | * 作者:luorongxi
14 | */ 15 | public class CacheUtils { 16 | //键值对集合 17 | private final static Map map = new HashMap<>(); 18 | //定时器线程池,用于清除过期缓存 19 | private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 20 | 21 | /** 22 | * 添加缓存 23 | * 24 | * @param key 键 25 | * @param data 值 26 | */ 27 | public synchronized static void put(String key, Object data) { 28 | CacheUtils.put(key, data, 0); 29 | } 30 | 31 | /** 32 | * 添加缓存 33 | * 34 | * @param key 键 35 | * @param data 值 36 | * @param expire 过期时间,单位:毫秒, 0表示无限长 37 | */ 38 | public synchronized static void put(String key, Object data, long expire) { 39 | //清除原键值对 40 | CacheUtils.remove(key); 41 | //设置过期时间 42 | if (expire > 0) { 43 | Future future = executor.schedule(new Runnable() { 44 | @Override 45 | public void run() { 46 | //过期后清除该键值对 47 | synchronized (CacheUtils.class) { 48 | map.remove(key); 49 | } 50 | } 51 | }, expire, TimeUnit.MILLISECONDS); 52 | map.put(key, new Entity(data, future)); 53 | } else { 54 | //不设置过期时间 55 | map.put(key, new Entity(data, null)); 56 | } 57 | } 58 | 59 | /** 60 | * 读取缓存 61 | * 62 | * @param key 键 63 | * @return 64 | */ 65 | public synchronized static Object get(String key) { 66 | Entity entity = map.get(key); 67 | return entity == null ? null : entity.getValue(); 68 | } 69 | 70 | /** 71 | * 读取缓存 72 | * 73 | * @param key 键 74 | * * @param clazz 值类型 75 | * @return 76 | */ 77 | public synchronized static T get(String key, Class clazz) { 78 | return clazz.cast(CacheUtils.get(key)); 79 | } 80 | 81 | /** 82 | * 清除缓存 83 | * 84 | * @param key 85 | * @return 86 | */ 87 | public synchronized static Object remove(String key) { 88 | //清除原缓存数据 89 | Entity entity = map.remove(key); 90 | if (entity == null) { 91 | return null; 92 | } 93 | //清除原键值对定时器 94 | Future future = entity.getFuture(); 95 | if (future != null) { 96 | future.cancel(true); 97 | } 98 | return entity.getValue(); 99 | } 100 | 101 | /** 102 | * 查询当前缓存的键值对数量 103 | * 104 | * @return 105 | */ 106 | public synchronized static int size() { 107 | return map.size(); 108 | } 109 | 110 | /** 111 | * 缓存实体类 112 | */ 113 | private static class Entity { 114 | //键值对的value 115 | private Object value; 116 | //定时器Future 117 | private Future future; 118 | 119 | public Entity(Object value, Future future) { 120 | this.value = value; 121 | this.future = future; 122 | } 123 | 124 | /** 125 | * 获取值 126 | * 127 | * @return 128 | */ 129 | public Object getValue() { 130 | return value; 131 | } 132 | 133 | /** 134 | * 获取Future对象 135 | * 136 | * @return 137 | */ 138 | public Future getFuture() { 139 | return future; 140 | } 141 | } 142 | 143 | /** 144 | * 生成key值 145 | */ 146 | public static String generateKey(String key, String param){ 147 | return key +":"+ param; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/utils/IntegerToByteUtil.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.utils; 2 | 3 | public class IntegerToByteUtil { 4 | 5 | /** 6 | * 函数功能说明 :将整数转换为byte数组并指定长度
7 | * 参数:@param contents 内容 8 | * 参数:@param byteLength 指定长度 9 | * 参数:@return
10 | * return:byte[]
11 | */ 12 | public static byte[] intToByteArray(int contents, int byteLength) { 13 | byte[] bs = new byte[byteLength]; 14 | for (int i = 0; i < bs.length; i++) { 15 | bs[i] = (byte) (contents % 256); 16 | contents = contents / 256; 17 | } 18 | return bs; 19 | } 20 | 21 | /** 22 | * 函数功能说明 : 从第beginPos位开始计算
23 | * 参数:@param bs 24 | * 参数:@param beginPos 25 | * 参数:@return
26 | * return:byte
27 | */ 28 | public static byte checkout(byte[] bs, int beginPos) { 29 | byte nSum = 0x00; 30 | for (int j = beginPos; j < bs.length; j++) 31 | { 32 | nSum ^= bs[j]; 33 | } 34 | return nSum; 35 | } 36 | 37 | /** 38 | * 函数功能说明 :将byte数组转换为整数
39 | * 参数:@param bs 40 | * 参数:@return
41 | * return:int
42 | */ 43 | public static int bytesToInt(byte[] bs) { 44 | int a = 0; 45 | for (int i = 0; i < bs.length; i++) { 46 | a += (bs[i] & 0xFF) * Math.pow(256, i); 47 | } 48 | return a; 49 | } 50 | 51 | /** 52 | * 函数功能说明 :将int数值转换为占四个字节的byte数组,本方法适用于(低位在前,高位在后)的顺序
53 | * 参数:@param n 要转换的int值 54 | * 参数:@return
55 | * return:byte[] byte数组
56 | */ 57 | public static byte[] intToBytes(int n) { 58 | byte[] src = new byte[2]; 59 | src[1] = (byte) ((n>>8) & 0xFF); 60 | src[0] = (byte) (n & 0xFF); 61 | return src; 62 | } 63 | 64 | /** 65 | * 函数功能说明 :将int数值转换为占四个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序
66 | * 参数:@param value 要转换的int值 67 | * 参数:@return
68 | * return:byte[] byte数组
69 | */ 70 | public static byte[] intToBytes2(int n) { 71 | byte[] src = new byte[2]; 72 | src[0] = (byte) ((n>>8)&0xFF); 73 | src[1] = (byte) (n & 0xFF); 74 | return src; 75 | } 76 | 77 | 78 | /** 79 | * 函数功能说明 :将int数值转换为占两个字节的byte数组,本方法适用于(低位在前,高位在后)的顺序
80 | * 参数:@param n 要转换的int值 81 | * 参数:@return
82 | * return:byte[] byte数组
83 | */ 84 | public static byte[] intToBytes3(int n) { 85 | byte[] src = new byte[2]; 86 | src[1] = (byte) ((n>>8) & 0xFF); 87 | src[0] = (byte) (n & 0xFF); 88 | return src; 89 | } 90 | 91 | /** 92 | * 函数功能说明 : byte数组中取int数值,本方法适用于(低位在前,高位在后)的顺序
93 | * 参数:@param src byte数组 94 | * 参数:@param offset 从数组的第offset位开始 95 | * 参数:@return
96 | * return:int int数值
97 | */ 98 | public static int bytesToInt(byte[] src, int offset) { 99 | int value; 100 | value = (int) ((src[offset] & 0xFF) 101 | | ((src[offset+1] & 0xFF)<<8) 102 | | ((src[offset+2] & 0xFF)<<16) 103 | | ((src[offset+3] & 0xFF)<<24)); 104 | return value; 105 | } 106 | 107 | /** 108 | * 函数功能说明 :byte数组中取int数值,本方法适用于(低位在后,高位在前)的顺序
109 | * 参数:@param src byte数组 110 | * 参数:@param offset 从数组的第offset位开始 111 | * 参数:@return
112 | * return:int int数值
113 | */ 114 | public static int bytesToInt2(byte[] src, int offset) { 115 | int value; 116 | value = (int) ( ((src[offset] & 0xFF)<<24) 117 | |((src[offset+1] & 0xFF)<<16) 118 | |((src[offset+2] & 0xFF)<<8) 119 | |(src[offset+3] & 0xFF)); 120 | return value; 121 | } 122 | 123 | 124 | } 125 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/utils/JudgeEmptyUtils.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.utils; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | /** 8 | * 类功能说明:判空工具类
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | public final class JudgeEmptyUtils{ 13 | 14 | /** 15 | * 私有构造方法,将该工具类设为单例模式 16 | */ 17 | private JudgeEmptyUtils() { 18 | } 19 | 20 | /** 21 | * 判断字符串是否为空 22 | * @param str 23 | * @return 24 | */ 25 | public static boolean isEmpty(String str) { 26 | return null == str || "".equals(str); 27 | } 28 | 29 | /** 30 | * 判断对象数组是否为空 31 | * @param obj 32 | * @return 33 | */ 34 | public static boolean isEmpty(Object[] obj) { 35 | return null == obj || 0 == obj.length; 36 | } 37 | 38 | /** 39 | * 判断对象是否为空 40 | * @param obj 41 | * @return 42 | */ 43 | public static boolean isEmpty(Object obj) { 44 | if (null == obj) { 45 | return true; 46 | } 47 | if (obj instanceof String) { 48 | return ((String) obj).trim().isEmpty(); 49 | } 50 | return !(obj instanceof Number) ? false : true; 51 | } 52 | 53 | /** 54 | * 判断集合是否为空 55 | * @param obj 56 | * @return 57 | */ 58 | public static boolean isEmpty(List obj) { 59 | return null == obj || obj.isEmpty(); 60 | } 61 | 62 | 63 | /** 64 | * 判断Map集合是否为空 65 | * @param obj 66 | * @return 67 | */ 68 | public static boolean isEmpty(Map obj) { 69 | return null == obj || obj.isEmpty(); 70 | } 71 | 72 | /** 73 | * 判断Set集合是否为空 74 | * @param obj 75 | * @return 76 | */ 77 | public static boolean isEmpty(Set obj) { 78 | return null == obj || obj.isEmpty(); 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/utils/ModbusCrc16Utils.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.utils; 2 | 3 | public class ModbusCrc16Utils { 4 | 5 | /** 6 | * 计算CRC16校验码 7 | * 8 | * @param bytes 9 | * 字节数组 10 | * @return {@link String} 校验码 11 | * @since 1.0 12 | */ 13 | public static String getCrcString(byte[] bytes) { 14 | // CRC寄存器全为1 15 | int CRC = 0x0000ffff; 16 | // 多项式校验值 17 | int POLYNOMIAL = 0x0000a001; 18 | int i, j; 19 | for (i = 0; i < bytes.length; i++) { 20 | CRC ^= ((int) bytes[i] & 0x000000ff); 21 | for (j = 0; j < 8; j++) { 22 | if ((CRC & 0x00000001) != 0) { 23 | CRC >>= 1; 24 | CRC ^= POLYNOMIAL; 25 | } else { 26 | CRC >>= 1; 27 | } 28 | } 29 | } 30 | // 结果转换为16进制 31 | String result = Integer.toHexString(CRC).toLowerCase(); 32 | if (result.length() != 4) { 33 | StringBuffer sb = new StringBuffer("0000"); 34 | result = sb.replace(4 - result.length(), 4, result).toString(); 35 | } 36 | //高位在前地位在后 37 | //return result.substring(2, 4) + result.substring(0, 2); 38 | // 交换高低位,低位在前高位在后 39 | return result.substring(2, 4) + result.substring(0, 2); 40 | } 41 | 42 | /** 43 | * 计算CRC16校验码 44 | * 45 | * @param bytes 46 | * @return 47 | */ 48 | public static byte[] getCrcByte(byte[] bytes) { 49 | int CRC = 0x0000ffff; 50 | int POLYNOMIAL = 0x0000a001; 51 | 52 | int i, j; 53 | for (i = 0; i < bytes.length; i++) { 54 | CRC ^= ((int) bytes[i] & 0x000000ff); 55 | for (j = 0; j < 8; j++) { 56 | if ((CRC & 0x00000001) != 0) { 57 | CRC >>= 1; 58 | CRC ^= POLYNOMIAL; 59 | } else { 60 | CRC >>= 1; 61 | } 62 | } 63 | } 64 | return new byte[]{ 65 | (byte) (CRC & 0xff), (byte) (CRC >>> 8 & 0xff) 66 | }; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /iot-modbus-common/src/main/java/com/takeoff/iot/modbus/common/utils/SpringContextUtil.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.common.utils; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * 类功能说明:Bytes与Hex转换工具类
10 | * 公司名称:TF(腾飞)开源
11 | * 作者:luorongxi
12 | */ 13 | @Component 14 | public class SpringContextUtil implements ApplicationContextAware { 15 | 16 | public static ApplicationContext applicationContext; 17 | 18 | @Override 19 | public void setApplicationContext(ApplicationContext applicationContext) 20 | throws BeansException { 21 | this.applicationContext = applicationContext; 22 | } 23 | 24 | public static Object getBean(String name) { 25 | return applicationContext.getBean(name); 26 | } 27 | 28 | public static T getBean(String name, Class requiredType) { 29 | return applicationContext.getBean(name, requiredType); 30 | } 31 | 32 | public static boolean containsBean(String name) { 33 | return applicationContext.containsBean(name); 34 | } 35 | 36 | public static boolean isSingleton(String name) { 37 | return applicationContext.isSingleton(name); 38 | } 39 | 40 | public static Class getType(String name) { 41 | return applicationContext.getType(name); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /iot-modbus-netty/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.takeoff.iot 8 | iot-modbus 9 | 3.2.9-SNAPSHOT 10 | 11 | iot-modbus-netty 12 | jar 13 | iot-modbus-netty 14 | netty通讯模块 15 | 16 | 17 | 3.2.9-SNAPSHOT 18 | 4.1.39.Final 19 | 20 | 21 | 22 | 23 | com.takeoff.iot 24 | iot-modbus-common 25 | ${iot-modbus-common.version} 26 | 27 | 28 | io.netty 29 | netty-handler 30 | ${netty.version} 31 | 32 | 33 | io.netty 34 | netty-example 35 | ${netty.version} 36 | provided 37 | 38 | 39 | 40 | io.netty 41 | * 42 | 43 | 44 | org.bouncycastle 45 | bcprov-jdk15on 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | ${maven-compiler-plugin} 57 | 58 | ${java.version} 59 | ${java.version} 60 | UTF-8 61 | true 62 | 63 | 64 | 65 | maven-source-plugin 66 | ${maven-source-plugin.version} 67 | 68 | true 69 | 70 | 71 | 72 | compile 73 | 74 | jar 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/channel/MiiChannel.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.channel; 2 | 3 | import com.takeoff.iot.modbus.common.message.MiiMessage; 4 | import com.takeoff.iot.modbus.netty.service.NameValue; 5 | 6 | /** 7 | * 类功能说明:通讯通道
8 | * 公司名称:TF(腾飞)开源
9 | * 作者:luorongxi
10 | */ 11 | public interface MiiChannel extends NameValue { 12 | 13 | /** 14 | * 发送指令信息 15 | * @param msg 指令信息 16 | */ 17 | void send(MiiMessage msg); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/channel/MiiChannelGroup.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.channel; 2 | 3 | import com.takeoff.iot.modbus.netty.device.MiiControlCentre; 4 | import com.takeoff.iot.modbus.netty.service.MappingsImpl; 5 | 6 | import java.util.List; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | public class MiiChannelGroup extends MappingsImpl implements MiiControlCentre { 10 | 11 | public MiiChannelGroup(){ 12 | super(new ConcurrentHashMap<>()); 13 | } 14 | 15 | @Override 16 | public List groups() { 17 | return list(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/channel/MiiContextChannel.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.channel; 2 | 3 | import java.net.InetSocketAddress; 4 | 5 | import com.takeoff.iot.modbus.common.message.MiiMessage; 6 | import com.takeoff.iot.modbus.netty.service.NameValueImpl; 7 | import io.netty.channel.ChannelHandlerContext; 8 | 9 | public class MiiContextChannel extends NameValueImpl implements MiiChannel { 10 | 11 | private final ChannelHandlerContext ctx; 12 | private final InetSocketAddress address; 13 | 14 | public MiiContextChannel(ChannelHandlerContext ctx) { 15 | this.ctx = ctx; 16 | this.address = (InetSocketAddress) this.ctx.channel().remoteAddress(); 17 | name(address.getHostString()); 18 | } 19 | 20 | public void send(MiiMessage msg) { 21 | this.ctx.writeAndFlush(msg); 22 | } 23 | 24 | public InetSocketAddress remoteAddress() { 25 | return (InetSocketAddress) this.ctx.channel().remoteAddress(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/data/factory/MiiClientDataFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.data.factory; 2 | 3 | import com.takeoff.iot.modbus.common.bytes.factory.MiiDataFactory; 4 | import com.takeoff.iot.modbus.common.data.MiiData; 5 | import com.takeoff.iot.modbus.common.data.MiiInData; 6 | import com.takeoff.iot.modbus.common.data.MiiLockData; 7 | import com.takeoff.iot.modbus.common.message.MiiMessage; 8 | 9 | public class MiiClientDataFactory implements MiiDataFactory { 10 | 11 | @Override 12 | public MiiData buildData(int command, byte[] datas) { 13 | MiiData data = null; 14 | switch (command) { 15 | case MiiMessage.LOCK: 16 | data = new MiiLockData(datas); 17 | break; 18 | default: data = new MiiInData(datas); 19 | break; 20 | } 21 | return data; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/data/factory/MiiServerDataFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.data.factory; 2 | 3 | import com.takeoff.iot.modbus.common.bytes.factory.MiiDataFactory; 4 | import com.takeoff.iot.modbus.common.data.MiiBackLightData; 5 | import com.takeoff.iot.modbus.common.data.MiiBarcodeData; 6 | import com.takeoff.iot.modbus.common.data.MiiCardData; 7 | import com.takeoff.iot.modbus.common.data.MiiData; 8 | import com.takeoff.iot.modbus.common.data.MiiFingerData; 9 | import com.takeoff.iot.modbus.common.data.MiiHeartBeatData; 10 | import com.takeoff.iot.modbus.common.data.MiiHumitureData; 11 | import com.takeoff.iot.modbus.common.data.MiiInData; 12 | import com.takeoff.iot.modbus.common.data.MiiLockData; 13 | import com.takeoff.iot.modbus.common.message.MiiMessage; 14 | import com.takeoff.iot.modbus.netty.data.*; 15 | 16 | /** 17 | * 类功能说明:指令数据处理工厂
18 | * 公司名称:TF(腾飞)开源
19 | * 作者:luorongxi
20 | */ 21 | public class MiiServerDataFactory implements MiiDataFactory { 22 | 23 | @Override 24 | public MiiData buildData(int command, byte[] datas) { 25 | MiiData data = null; 26 | switch (command) { 27 | case MiiMessage.HEARTBEAT: 28 | data = new MiiHeartBeatData(datas); 29 | break; 30 | case MiiMessage.LOCK: 31 | data = new MiiLockData(datas); 32 | break; 33 | case MiiMessage.CARD: 34 | data = new MiiCardData(datas); 35 | break; 36 | case MiiMessage.BARCODE: 37 | data = new MiiBarcodeData(datas); 38 | break; 39 | case MiiMessage.BACKLIGHT: 40 | data = new MiiBackLightData(datas); 41 | break; 42 | case MiiMessage.FINGER: 43 | data = new MiiFingerData(datas); 44 | break; 45 | case MiiMessage.HM: 46 | data = new MiiHumitureData(datas); 47 | break; 48 | case MiiMessage.LED: 49 | default: data = new MiiInData(datas); 50 | break; 51 | } 52 | return data; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/device/MiiControlCentre.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.device; 2 | 3 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 4 | 5 | import java.util.List; 6 | 7 | public interface MiiControlCentre { 8 | 9 | boolean add(MiiChannel group); 10 | 11 | MiiChannel remove(String address); 12 | 13 | MiiChannel get(String address); 14 | 15 | List groups(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/device/MiiDevice.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.device; 2 | 3 | public class MiiDevice { 4 | 5 | /** 6 | * 设备组编码 7 | */ 8 | private String deviceGroup; 9 | 10 | /** 11 | * 设备号 12 | */ 13 | private int device; 14 | 15 | public MiiDevice(String deviceGroup, int device){ 16 | this.deviceGroup = deviceGroup; 17 | this.device = device; 18 | } 19 | 20 | public String deviceGroup(){ 21 | return deviceGroup; 22 | } 23 | 24 | public int device(){ 25 | return device; 26 | } 27 | 28 | @Override 29 | public int hashCode() { 30 | final int prime = 31; 31 | int result = 1; 32 | result = prime * result 33 | + ((deviceGroup() == null) ? 0 : deviceGroup().hashCode()); 34 | result = prime * result + device(); 35 | return result; 36 | } 37 | 38 | @Override 39 | public boolean equals(Object obj) { 40 | if(obj instanceof MiiDevice){ 41 | MiiDevice cab = (MiiDevice) obj; 42 | return deviceGroup().equals(cab.deviceGroup()) 43 | && device() == cab.device(); 44 | } else { 45 | return false; 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/device/MiiDeviceChannel.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.device; 2 | 3 | import java.net.InetSocketAddress; 4 | 5 | import com.takeoff.iot.modbus.common.message.MiiMessage; 6 | import com.takeoff.iot.modbus.netty.service.NameValueImpl; 7 | import io.netty.channel.Channel; 8 | 9 | public class MiiDeviceChannel extends NameValueImpl implements MiiDeviceGroup { 10 | 11 | private InetSocketAddress address; 12 | private Channel channel; 13 | 14 | public MiiDeviceChannel(InetSocketAddress address, Channel channel){ 15 | this.channel = channel; 16 | this.address = address; 17 | name(address.getHostString()); 18 | } 19 | 20 | public MiiDeviceChannel(Channel channel){ 21 | this((InetSocketAddress) channel.remoteAddress(), channel); 22 | } 23 | 24 | public int port(){ 25 | return address.getPort(); 26 | } 27 | public String address() { 28 | return address.getAddress().getHostAddress(); 29 | } 30 | 31 | public void send(MiiMessage msg) { 32 | channel.writeAndFlush(msg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/device/MiiDeviceGroup.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.device; 2 | 3 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 4 | 5 | /** 6 | * 类功能说明:设备信息
7 | * 公司名称:TF(腾飞)开源
8 | * 作者:luorongxi
9 | */ 10 | public interface MiiDeviceGroup extends MiiChannel { 11 | 12 | /** 13 | * 返回设备组地址. 14 | * @return 设备组地址 15 | */ 16 | String address(); 17 | 18 | /** 19 | * 返回设备组名称 20 | * @return 设备组名称 21 | */ 22 | String name(); 23 | 24 | /** 25 | * 返回设备组端口 26 | * @return 设备组端口 27 | */ 28 | int port(); 29 | } 30 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/handle/MiiBasedFrameDecoder.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.handle; 2 | 3 | import java.nio.ByteOrder; 4 | 5 | import com.takeoff.iot.modbus.common.message.MiiMessage; 6 | 7 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 8 | 9 | /** 10 | * 类功能说明:自定义解码器长度,解决TCP粘包黏包问题
11 | * 公司名称:TF(腾飞)开源
12 | * 作者:luorongxi
13 | */ 14 | public class MiiBasedFrameDecoder extends LengthFieldBasedFrameDecoder { 15 | 16 | public MiiBasedFrameDecoder(){ 17 | super(ByteOrder.LITTLE_ENDIAN, Integer.MAX_VALUE, MiiMessage.DATA_INDEX, MiiMessage.DATA_SIZE, 3, 0, true); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/handle/MiiExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.handle; 2 | 3 | import com.takeoff.iot.modbus.common.entity.ChannelConnectData; 4 | import com.takeoff.iot.modbus.common.enums.DeviceConnectEnum; 5 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 6 | import com.takeoff.iot.modbus.common.utils.SpringContextUtil; 7 | import org.springframework.context.ApplicationContext; 8 | 9 | import io.netty.channel.Channel; 10 | import io.netty.channel.ChannelDuplexHandler; 11 | import io.netty.channel.ChannelHandlerContext; 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | @Slf4j 15 | public class MiiExceptionHandler extends ChannelDuplexHandler { 16 | 17 | private ApplicationContext getApplicationContext = SpringContextUtil.applicationContext; 18 | 19 | /** 20 | * 处理业务异常次数 21 | */ 22 | private int exception; 23 | 24 | public MiiExceptionHandler(){ 25 | 26 | } 27 | 28 | @Override 29 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 30 | exception++; 31 | Channel channel = ctx.channel(); 32 | if(!JudgeEmptyUtils.isEmpty(channel.remoteAddress())){ 33 | String address = channel.remoteAddress().toString().substring(1,channel.remoteAddress().toString().length()); 34 | ChannelConnectData connectServerData = new ChannelConnectData(this, DeviceConnectEnum.ABNORMAL.getKey(), address, exception); 35 | if(!JudgeEmptyUtils.isEmpty(connectServerData) && !JudgeEmptyUtils.isEmpty(getApplicationContext)){ 36 | getApplicationContext.publishEvent(connectServerData); 37 | } 38 | //由Tail节点对异常进行统一处理 39 | if(cause instanceof RuntimeException){ 40 | log.info("处理业务异常:"+channel.remoteAddress()); 41 | } 42 | super.exceptionCaught(ctx, cause); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/handle/MiiListenerHandler.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.handle; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | import com.takeoff.iot.modbus.common.entity.ChannelConnectData; 7 | import com.takeoff.iot.modbus.common.enums.DeviceConnectEnum; 8 | import com.takeoff.iot.modbus.common.utils.CacheUtils; 9 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 10 | import com.takeoff.iot.modbus.common.utils.SpringContextUtil; 11 | import com.takeoff.iot.modbus.netty.device.MiiControlCentre; 12 | import com.takeoff.iot.modbus.common.message.MiiMessage; 13 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 14 | import com.takeoff.iot.modbus.netty.channel.MiiContextChannel; 15 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 16 | 17 | import io.netty.channel.Channel; 18 | import io.netty.channel.ChannelHandler.Sharable; 19 | import io.netty.channel.ChannelHandlerContext; 20 | import io.netty.channel.SimpleChannelInboundHandler; 21 | import org.springframework.context.ApplicationContext; 22 | 23 | /** 24 | * 类功能说明:自定义数据接收监听器
25 | * 公司名称:TF(腾飞)开源
26 | * 作者:luorongxi
27 | */ 28 | @Sharable 29 | public class MiiListenerHandler extends SimpleChannelInboundHandler { 30 | 31 | private Map listeners = new ConcurrentHashMap<>(); 32 | private MiiControlCentre centre; 33 | 34 | public MiiListenerHandler(){ 35 | } 36 | 37 | public MiiListenerHandler(MiiControlCentre centre){ 38 | this.centre = centre; 39 | } 40 | 41 | public MiiListener addListener(int command, MiiListener listener){ 42 | MiiListener pre = null; 43 | if(hasListener(command)){ 44 | pre = listeners.get(command); 45 | } 46 | listeners.put(command, listener); 47 | return pre; 48 | } 49 | 50 | public MiiListener removeListener(int command){ 51 | return listeners.remove(command); 52 | } 53 | 54 | public boolean hasListener(int command){ 55 | return listeners.get(command) != null; 56 | } 57 | 58 | @Override 59 | protected void channelRead0(ChannelHandlerContext ctx, MiiMessage msg) throws Exception { 60 | MiiListener listener = listeners.get(msg.command()); 61 | if(listener != null){ 62 | MiiChannel channel = centre == null ? new MiiContextChannel(ctx) : centre.get(msg.deviceGroup()); 63 | listener.receive(channel == null ? new MiiContextChannel(ctx) : channel, msg); 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/handle/MiiMessageDecoder.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.handle; 2 | 3 | import java.util.List; 4 | 5 | import com.takeoff.iot.modbus.common.bytes.factory.MiiDataFactory; 6 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 7 | import com.takeoff.iot.modbus.netty.device.MiiDeviceChannel; 8 | import com.takeoff.iot.modbus.netty.message.MiiInMessage; 9 | import io.netty.channel.Channel; 10 | import org.bouncycastle.util.encoders.Hex; 11 | 12 | import io.netty.buffer.ByteBuf; 13 | import io.netty.channel.ChannelHandlerContext; 14 | import io.netty.handler.codec.ByteToMessageDecoder; 15 | import lombok.extern.slf4j.Slf4j; 16 | 17 | /** 18 | * 类功能说明:自定义解码器,主要是用来解决半包积累的问题
19 | * 公司名称:TF(腾飞)开源
20 | * 作者:luorongxi
21 | */ 22 | @Slf4j 23 | public class MiiMessageDecoder extends ByteToMessageDecoder { 24 | 25 | private MiiDataFactory dataFactory; 26 | 27 | public MiiMessageDecoder(MiiDataFactory dataFactory) { 28 | this.dataFactory = dataFactory; 29 | } 30 | 31 | @Override 32 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 33 | try { 34 | byte[] array = new byte[in.readableBytes()]; 35 | in.readBytes(array); 36 | log.info("接收到待处理指令:"+Hex.toHexString(array)); 37 | MiiChannel miiChannel = new MiiDeviceChannel(ctx.channel()); 38 | MiiInMessage msg = new MiiInMessage(miiChannel.name(),array,dataFactory); 39 | out.add(msg); 40 | } catch (Exception e) { 41 | log.error(e.getMessage(), e); 42 | throw e; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/handle/MiiMessageEncoder.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.handle; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | 5 | import com.takeoff.iot.modbus.common.message.MiiMessage; 6 | 7 | import io.netty.buffer.ByteBuf; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.handler.codec.MessageToByteEncoder; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | /** 13 | * 类功能说明:自定义编码器,将数据编码后进行下发
14 | * 公司名称:TF(腾飞)开源
15 | * 作者:luorongxi
16 | */ 17 | @Slf4j 18 | public class MiiMessageEncoder extends MessageToByteEncoder { 19 | 20 | @Override 21 | public void encode(ChannelHandlerContext ctx, MiiMessage msg, ByteBuf out) throws Exception { 22 | try { 23 | out.writeBytes(msg.toBytes()); 24 | log.debug(Hex.toHexString(msg.toBytes())); 25 | } catch (Exception e) { 26 | throw new RuntimeException(e.getMessage(), e); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/listener/MiiListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.listener; 2 | 3 | import com.takeoff.iot.modbus.common.message.MiiMessage; 4 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 5 | 6 | /** 7 | * 类功能说明:设备回传信息监听器接口,处理设备返回信息需实现这个接口,并使用 addListener()添加到{@link MiiServer}的实例中
8 | * 公司名称:TF(腾飞)开源
9 | * 作者:luorongxi
10 | */ 11 | @FunctionalInterface 12 | public interface MiiListener { 13 | 14 | /** 15 | * 处理接收到的设备信息 16 | * @param channel 消息通道 17 | * @param message 接收到的设备信息 18 | */ 19 | void receive(MiiChannel channel, MiiMessage message); 20 | } 21 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/listener/MiiReadWriteLock.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.listener; 2 | 3 | import com.takeoff.iot.modbus.netty.device.MiiDevice; 4 | import com.takeoff.iot.modbus.common.data.MiiSlotData; 5 | import com.takeoff.iot.modbus.common.message.MiiMessage; 6 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.concurrent.locks.Lock; 11 | import java.util.concurrent.locks.ReadWriteLock; 12 | import java.util.concurrent.locks.ReentrantReadWriteLock; 13 | 14 | /** 15 | * 类功能说明:为信息监听器 {@link MiiListener} 加读写锁.统一分组下写锁与写锁或者读锁之间互斥,读锁与读锁之间可以同时获得锁
16 | * 公司名称:TF(腾飞)开源
17 | * 作者:luorongxi
18 | */ 19 | public class MiiReadWriteLock { 20 | 21 | private boolean fair; 22 | private Map locks; 23 | 24 | /** 25 | * 使用默认(非公平)的排序属性创建一个读写锁. 26 | */ 27 | public MiiReadWriteLock() { 28 | this(false); 29 | } 30 | 31 | /** 32 | * 使用给定的公平策略创建一个读写锁. 33 | * @param fair 是否使用公平排序策略 34 | */ 35 | public MiiReadWriteLock(boolean fair) { 36 | this.fair = fair; 37 | locks = new HashMap<>(); 38 | } 39 | 40 | /** 41 | * 为信息监听器添加读锁. 42 | * @param listener 原信息监听器 43 | * @return 添加读锁信息监听器 44 | */ 45 | public MiiListener readLockListener(MiiListener listener){ 46 | return new ReadLockListener(this, listener); 47 | } 48 | 49 | /** 50 | * 为信息监听器添加写锁. 51 | * @param listener 原信息监听器 52 | * @return 添加写锁信息监听器 53 | */ 54 | public MiiListener writeLockListener(MiiListener listener){ 55 | return new WriteLockListener(this, listener); 56 | } 57 | 58 | /** 59 | * 生成分组对象. 60 | * 默认以设备组通讯deviceGroup加上设备号作为分组条件,可重写该方法改变分组条件. 61 | * @param deviceGroup 62 | * @param message 63 | * @return 64 | */ 65 | protected Object group(String deviceGroup, MiiMessage message) { 66 | if(message instanceof MiiSlotData){ 67 | return new MiiDevice(deviceGroup,((MiiSlotData) message).device()); 68 | } else { 69 | return new MiiDevice(deviceGroup, 0); 70 | } 71 | } 72 | 73 | ReadWriteLock lock(Object deviceGroup){ 74 | ReadWriteLock lock; 75 | if((lock = locks.get(deviceGroup)) == null){ 76 | synchronized (this) { 77 | lock = locks.computeIfAbsent(deviceGroup 78 | , v -> new ReentrantReadWriteLock(fair)); 79 | } 80 | } 81 | return lock; 82 | } 83 | } 84 | 85 | abstract class LockListener implements MiiListener { 86 | protected MiiReadWriteLock rwlock; 87 | protected MiiListener listener; 88 | public LockListener(MiiReadWriteLock rwlock, MiiListener listener) { 89 | this.rwlock = rwlock; 90 | this.listener = listener; 91 | } 92 | 93 | public void receive(MiiChannel channel, MiiMessage message) { 94 | Lock lock = lock(message.deviceGroup(), message); 95 | lock.lock(); 96 | try { 97 | listener.receive(channel, message); 98 | } finally { 99 | lock.unlock(); 100 | } 101 | } 102 | 103 | protected abstract Lock lock(String deviceGroup, MiiMessage message); 104 | } 105 | 106 | class WriteLockListener extends LockListener { 107 | 108 | public WriteLockListener(MiiReadWriteLock rwlock, MiiListener listener) { 109 | super(rwlock, listener); 110 | } 111 | 112 | @Override 113 | protected Lock lock(String deviceGroup, MiiMessage message) { 114 | return rwlock.lock(rwlock.group(deviceGroup, message)).writeLock(); 115 | } 116 | 117 | } 118 | 119 | class ReadLockListener extends LockListener { 120 | 121 | public ReadLockListener(MiiReadWriteLock rwlock, MiiListener listener) { 122 | super(rwlock, listener); 123 | } 124 | 125 | @Override 126 | protected Lock lock(String deviceGroup, MiiMessage message) { 127 | return rwlock.lock(rwlock.group(deviceGroup, message)).readLock(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/message/MiiInMessage.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.message; 2 | 3 | import java.util.Arrays; 4 | 5 | import com.takeoff.iot.modbus.common.bytes.factory.MiiDataFactory; 6 | import com.takeoff.iot.modbus.common.data.MiiData; 7 | import com.takeoff.iot.modbus.common.message.MiiMessage; 8 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 9 | import com.takeoff.iot.modbus.common.utils.ModbusCrc16Utils; 10 | 11 | import org.apache.commons.lang3.ArrayUtils; 12 | import org.bouncycastle.util.encoders.Hex; 13 | 14 | import io.netty.handler.codec.DecoderException; 15 | 16 | /** 17 | * 类功能说明:对接收到的指令数据进行校验
18 | * 公司名称:TF(腾飞)开源
19 | * 作者:luorongxi
20 | */ 21 | public class MiiInMessage implements MiiMessage { 22 | 23 | private String deviceGroup; 24 | private byte[] msg; 25 | private MiiData data; 26 | 27 | public MiiInMessage(byte[] msg, MiiDataFactory dataFactory){ 28 | this(null, msg, dataFactory); 29 | } 30 | 31 | public MiiInMessage(String channelName, byte[] msg, MiiDataFactory dataFactory){ 32 | this.deviceGroup = channelName; 33 | this.msg = msg; 34 | byte[] headBytes = {msg[BEGIN_INDEX]}; 35 | if(!Arrays.equals(BEGIN_BYTES, headBytes)){ 36 | throw new DecoderException(String.format("报文头异常:%s", Hex.toHexString(msg))); 37 | } 38 | byte[] datas = ArrayUtils.subarray(msg, COMMAND_INDEX, msg.length - 3); 39 | byte[] dataLength = ArrayUtils.subarray(msg, DATA_INDEX, COMMAND_INDEX); 40 | if(datas.length != IntegerToByteUtil.bytesToInt(dataLength)){ 41 | throw new DecoderException(String.format("报文长短异常:%s", Hex.toHexString(msg))); 42 | } 43 | byte[] checkcode = {msg[msg.length - 3],msg[msg.length - 2]}; 44 | byte[] checkData = ArrayUtils.subarray(msg, DATA_INDEX, msg.length - 3); 45 | if(!ModbusCrc16Utils.getCrcString(checkData).equals(Hex.toHexString(checkcode))){ 46 | throw new DecoderException(String.format("报文校验码校验错误:%s", Hex.toHexString(msg))); 47 | } 48 | int command = msg[COMMAND_INDEX] & 0x7F; 49 | this.data = dataFactory.buildData(command, datas); 50 | } 51 | 52 | public String deviceGroup() { 53 | return deviceGroup; 54 | } 55 | 56 | public int command() { 57 | return msg[COMMAND_INDEX] & 0x7F; 58 | } 59 | 60 | public int length() { 61 | byte[] dataLength = ArrayUtils.subarray(msg, DATA_INDEX, COMMAND_INDEX); 62 | return IntegerToByteUtil.bytesToInt(dataLength); 63 | } 64 | 65 | public MiiData data() { 66 | return data; 67 | } 68 | 69 | public int type() { 70 | return RECV; 71 | } 72 | 73 | public byte[] toBytes() { 74 | return msg; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/service/Mappings.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.service; 2 | 3 | import java.util.List; 4 | 5 | public interface Mappings { 6 | 7 | boolean add(NV nv); 8 | 9 | NV get(String name); 10 | 11 | NV remove(String name); 12 | 13 | List list(); 14 | } 15 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/service/MappingsImpl.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class MappingsImpl implements Mappings { 9 | 10 | protected transient final Map namedMap; 11 | 12 | public MappingsImpl(){ 13 | this(new HashMap()); 14 | } 15 | 16 | public MappingsImpl(Map namedMap){ 17 | this.namedMap = namedMap; 18 | } 19 | 20 | @Override 21 | public boolean add(NV nv) { 22 | boolean exists = false; 23 | 24 | if(nv.map() == null || nv.map() == this){ 25 | exists = get(nv.name()) != null; 26 | if(exists){ 27 | remove(nv.name()); 28 | } 29 | namedMap.put(nv.name(), nv); 30 | nv.map(this); 31 | } else { 32 | throw new RuntimeException(String.format("%s已经存在其他映射组里!", nv.name())); 33 | } 34 | 35 | return ! exists; 36 | } 37 | 38 | @Override 39 | public NV get(String name) { 40 | return (NV) namedMap.get(name); 41 | } 42 | 43 | @Override 44 | public NV remove(String name) { 45 | NV nv = namedMap.remove(name); 46 | if(nv != null){ 47 | nv.map(null); 48 | } 49 | return nv; 50 | } 51 | 52 | @Override 53 | public List list() { 54 | return new ArrayList<>(namedMap.values()); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/service/NameValue.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.service; 2 | 3 | public interface NameValue { 4 | 5 | boolean name(String name); 6 | 7 | String name(); 8 | 9 | void map(Mappings mapping); 10 | 11 | Mappings map(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /iot-modbus-netty/src/main/java/com/takeoff/iot/modbus/netty/service/NameValueImpl.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.netty.service; 2 | 3 | public class NameValueImpl implements NameValue { 4 | 5 | private String name; 6 | private Mappings mapping; 7 | 8 | public NameValueImpl(){ 9 | } 10 | 11 | public NameValueImpl(String name){ 12 | this.name = name; 13 | } 14 | 15 | @Override 16 | public String name() { 17 | return name; 18 | } 19 | 20 | @Override 21 | public boolean name(String name) { 22 | Mappings map = map(); 23 | if(map != null) map.remove(this.name); 24 | this.name = name; 25 | if(map != null) map.add(this); 26 | return map == null; 27 | } 28 | 29 | @Override 30 | public Mappings map() { 31 | return mapping; 32 | } 33 | 34 | @Override 35 | public void map(Mappings mapping) { 36 | Mappings map = map(); 37 | if(map != null && mapping != map) map.remove(this.name()); 38 | this.mapping = mapping; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /iot-modbus-serialport/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | iot-modbus 8 | com.takeoff.iot 9 | 3.2.9-SNAPSHOT 10 | 11 | iot-modbus-serialport 12 | jar 13 | iot-modbus-serialport 14 | 物联网串口通讯模块 15 | 16 | 17 | 2.2 18 | 3.2.9-SNAPSHOT 19 | 18.0 20 | 4.1.39.Final 21 | 22 | 23 | 24 | 25 | org.apache.logging.log4j 26 | log4j-to-slf4j 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-aop 31 | 32 | 33 | org.bidib.jbidib.org.qbang.rxtx 34 | rxtxcomm 35 | ${rxtxcomm.version} 36 | 37 | 38 | com.takeoff.iot 39 | iot-modbus-common 40 | ${iot-modbus-common.version} 41 | 42 | 43 | com.google.guava 44 | guava 45 | ${guava.version} 46 | 47 | 48 | io.netty 49 | netty-handler 50 | ${netty.version} 51 | 52 | 53 | io.netty 54 | netty-transport-rxtx 55 | ${netty.version} 56 | 57 | 58 | 59 | 60 | ${project.artifactId} 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-surefire-plugin 66 | 67 | true 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/BackLightData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiBackLightData; 4 | 5 | import lombok.Getter; 6 | 7 | /** 8 | * 类功能说明:背光灯数据
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | @Getter 13 | public class BackLightData extends ReceiveDataEvent { 14 | 15 | private Integer statusCode; 16 | 17 | public BackLightData(Object source, Integer command, MiiBackLightData data) { 18 | super(source, command, data.device(), data.shelf(), data.slot()); 19 | this.statusCode = data.statusCode(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/BarCodeData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiBarcodeData; 4 | 5 | import lombok.Getter; 6 | 7 | /** 8 | * 类功能说明:扫码数据
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | @Getter 13 | public class BarCodeData extends ReceiveDataEvent { 14 | 15 | private String barCode; 16 | 17 | public BarCodeData(Object source, int command, MiiBarcodeData data) { 18 | super(source, command, data.device(), data.shelf(), data.slot()); 19 | this.barCode = data.content(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/CardData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiCardData; 4 | 5 | import lombok.Getter; 6 | 7 | /** 8 | * 类功能说明:刷卡数据
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | @Getter 13 | public class CardData extends ReceiveDataEvent { 14 | 15 | private String cardCode; 16 | 17 | public CardData(Object source, int command, MiiCardData data) { 18 | super(source, command, data.device(), data.shelf(), data.slot()); 19 | this.cardCode = data.content(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/FingerData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data; 2 | 3 | import java.util.List; 4 | 5 | import com.takeoff.iot.modbus.common.data.MiiFingerData; 6 | 7 | import lombok.Getter; 8 | 9 | /** 10 | * 类功能说明:指静脉数据
11 | * 公司名称:TF(腾飞)开源
12 | * 作者:luorongxi
13 | */ 14 | @Getter 15 | public class FingerData extends ReceiveDataEvent { 16 | 17 | private int fingerType; 18 | private int fingerCmd; 19 | private int fingerId; 20 | private List fingerIdList; 21 | private byte[] fingerTemplate; 22 | 23 | public FingerData(Object source, int command, MiiFingerData data) { 24 | super(source, command, data.device(), data.shelf(), data.slot()); 25 | this.fingerType = data.fingerType(); 26 | this.fingerCmd = data.fingerCmd(); 27 | this.fingerId = data.fingerId(); 28 | this.fingerIdList = data.fingerIdList(); 29 | this.fingerTemplate = data.fingerTemplate(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/HeartBeatData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiHeartBeatData; 4 | 5 | import lombok.Getter; 6 | 7 | /** 8 | * 类功能说明:心跳数据
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | @Getter 13 | public class HeartBeatData extends ReceiveDataEvent { 14 | 15 | private String deviceGroup; 16 | 17 | public HeartBeatData(Object source, int command, MiiHeartBeatData data) { 18 | super(source, command, data.device(), data.shelf(), data.slot()); 19 | this.deviceGroup = data.deviceGroup(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/HumitureData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiHumitureData; 4 | 5 | import lombok.Getter; 6 | 7 | /** 8 | * 类功能说明:温湿度数据
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | @Getter 13 | public class HumitureData extends ReceiveDataEvent{ 14 | 15 | /** 16 | * 温度 17 | */ 18 | private double temperature; 19 | 20 | /** 21 | * 湿度 22 | */ 23 | private double humidity; 24 | 25 | public HumitureData(Object source, int command, MiiHumitureData data) { 26 | super(source, command, data.device(), data.shelf(), data.slot()); 27 | this.temperature = data.temperature(); 28 | this.humidity = data.humidity(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/LockData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data; 2 | 3 | import java.util.List; 4 | 5 | import com.takeoff.iot.modbus.common.data.MiiLockData; 6 | import com.takeoff.iot.modbus.common.entity.LockStatus; 7 | 8 | import lombok.Getter; 9 | 10 | /** 11 | * 类功能说明:门锁数据
12 | * 公司名称:TF(腾飞)开源
13 | * 作者:luorongxi
14 | */ 15 | @Getter 16 | public class LockData extends ReceiveDataEvent { 17 | 18 | /** 19 | * 锁状态 20 | */ 21 | private List list; 22 | 23 | public LockData(Object source, int command, MiiLockData data) { 24 | super(source, command, data.device(), data.shelf(), data.slot()); 25 | this.list = data.list(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/ReceiveDataEvent.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | import lombok.Getter; 6 | 7 | /** 8 | * 类功能说明:接收数据
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | @Getter 13 | public class ReceiveDataEvent extends ApplicationEvent { 14 | 15 | /** 16 | * 指令 17 | */ 18 | private Integer command; 19 | 20 | /** 21 | * 设备号 22 | */ 23 | private Integer device; 24 | 25 | /** 26 | * 层号 27 | */ 28 | private Integer shelf; 29 | 30 | /** 31 | * 槽位号 32 | */ 33 | private Integer slot; 34 | 35 | public ReceiveDataEvent(Object source, Integer command, Integer device, Integer shelf, Integer slot) { 36 | super(source); 37 | this.command=command; 38 | this.device=device; 39 | this.shelf=shelf; 40 | this.slot=slot; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/factory/SerialportDataFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data.factory; 2 | 3 | public interface SerialportDataFactory { 4 | 5 | void buildData(byte[] msg); 6 | } 7 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/data/factory/SerialportDataReceiveFactory.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.data.factory; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.apache.commons.lang3.ArrayUtils; 6 | import org.bouncycastle.util.encoders.Hex; 7 | import org.springframework.context.ApplicationContext; 8 | import org.springframework.stereotype.Service; 9 | 10 | import com.alibaba.fastjson.JSON; 11 | import com.takeoff.iot.modbus.common.data.MiiBackLightData; 12 | import com.takeoff.iot.modbus.common.data.MiiBarcodeData; 13 | import com.takeoff.iot.modbus.common.data.MiiCardData; 14 | import com.takeoff.iot.modbus.common.data.MiiFingerData; 15 | import com.takeoff.iot.modbus.common.data.MiiHeartBeatData; 16 | import com.takeoff.iot.modbus.common.data.MiiHumitureData; 17 | import com.takeoff.iot.modbus.common.data.MiiLockData; 18 | import com.takeoff.iot.modbus.common.message.MiiMessage; 19 | import com.takeoff.iot.modbus.common.utils.IntegerToByteUtil; 20 | import com.takeoff.iot.modbus.common.utils.ModbusCrc16Utils; 21 | import com.takeoff.iot.modbus.serialport.data.BackLightData; 22 | import com.takeoff.iot.modbus.serialport.data.BarCodeData; 23 | import com.takeoff.iot.modbus.serialport.data.CardData; 24 | import com.takeoff.iot.modbus.serialport.data.FingerData; 25 | import com.takeoff.iot.modbus.serialport.data.HeartBeatData; 26 | import com.takeoff.iot.modbus.serialport.data.HumitureData; 27 | import com.takeoff.iot.modbus.serialport.data.LockData; 28 | import com.takeoff.iot.modbus.serialport.data.ReceiveDataEvent; 29 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 30 | import com.takeoff.iot.modbus.serialport.utils.SpringContextUtils; 31 | 32 | import lombok.extern.slf4j.Slf4j; 33 | 34 | /** 35 | * 类功能说明:接收指令处理工厂
36 | * 公司名称:TF(腾飞)开源
37 | * 作者:luorongxi
38 | */ 39 | @Slf4j 40 | @Service 41 | public class SerialportDataReceiveFactory implements SerialportDataFactory { 42 | 43 | @Override 44 | public void buildData(byte[] msg) { 45 | byte[] headBytes = {msg[MiiMessage.BEGIN_INDEX]}; 46 | if(!Arrays.equals(MiiMessage.BEGIN_BYTES, headBytes)){ 47 | log.error(String.format("报文头异常:%s", Hex.toHexString(msg))); 48 | return; 49 | } 50 | byte[] datas = ArrayUtils.subarray(msg, MiiMessage.COMMAND_INDEX, msg.length - 3); 51 | byte[] dataLength = ArrayUtils.subarray(msg, MiiMessage.DATA_INDEX, MiiMessage.COMMAND_INDEX); 52 | if(datas.length != IntegerToByteUtil.bytesToInt(dataLength)){ 53 | log.error(String.format("报文长短异常:%s", Hex.toHexString(msg))); 54 | return; 55 | } 56 | byte[] checkcode = {msg[msg.length - 3],msg[msg.length - 2]}; 57 | byte[] checkData = ArrayUtils.subarray(msg, MiiMessage.DATA_INDEX, msg.length - 3); 58 | if(!ModbusCrc16Utils.getCrcString(checkData).equals(Hex.toHexString(checkcode))){ 59 | log.error(String.format("报文校验码校验错误:%s", Hex.toHexString(msg))); 60 | return; 61 | } 62 | int command = msg[MiiMessage.COMMAND_INDEX] & 0x7F; 63 | ReceiveDataEvent receiveDataEvent = handleData(command, datas); 64 | ApplicationContext getApplicationContext = SpringContextUtils.applicationContext; 65 | if(!JudgeEmptyUtils.isEmpty(receiveDataEvent) && !JudgeEmptyUtils.isEmpty(getApplicationContext)){ 66 | log.info("将数据发送给对接的指令监听器:"+ JSON.toJSONString(receiveDataEvent)); 67 | getApplicationContext.publishEvent(receiveDataEvent); 68 | } 69 | } 70 | 71 | /** 72 | * 数据处理工厂 73 | * @param command 74 | * @param datas 75 | * @return 76 | */ 77 | private ReceiveDataEvent handleData(int command, byte[] datas) { 78 | ReceiveDataEvent handleData = null; 79 | switch (command) { 80 | case MiiMessage.HEARTBEAT: 81 | handleData = new HeartBeatData(this, command, new MiiHeartBeatData(datas)); 82 | break; 83 | case MiiMessage.LOCK: 84 | handleData = new LockData(this, command, new MiiLockData(datas)); 85 | break; 86 | case MiiMessage.CARD: 87 | handleData = new CardData(this, command, new MiiCardData(datas)); 88 | break; 89 | case MiiMessage.BARCODE: 90 | handleData = new BarCodeData(this, command, new MiiBarcodeData(datas)); 91 | break; 92 | case MiiMessage.BACKLIGHT: 93 | handleData = new BackLightData(this, command, new MiiBackLightData(datas)); 94 | break; 95 | case MiiMessage.FINGER: 96 | handleData = new FingerData(this, command, new MiiFingerData(datas)); 97 | break; 98 | case MiiMessage.HM: 99 | handleData = new HumitureData(this, command, new MiiHumitureData(datas)); 100 | break; 101 | default: 102 | break; 103 | } 104 | return handleData; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/entity/ReceiveData.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.entity; 2 | 3 | import java.util.List; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class ReceiveData { 9 | 10 | /** 11 | * 获取起始符下标 12 | */ 13 | private int beginIndex; 14 | 15 | /** 16 | * 指令总长度 17 | */ 18 | private int instructLength; 19 | 20 | /** 21 | * 接收到的数据 22 | */ 23 | private List buffList; 24 | 25 | /** 26 | * 校验标识 27 | */ 28 | private boolean flag; 29 | } 30 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/enums/DatebitsEnum.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.enums; 2 | 3 | /** 4 | * 类功能说明:数据位枚举类
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public enum DatebitsEnum { 9 | 10 | FIVE(5, "FIVE"), 11 | SIX(6, "SIX"), 12 | SEVEN(7, "SEVEN"), 13 | EIGHT(8, "EIGHT"), 14 | ; 15 | 16 | // 成员变量 17 | private Integer key; 18 | private String value; 19 | 20 | // 构造方法 21 | private DatebitsEnum(Integer key, String value) { 22 | this.key = key; 23 | this.value = value; 24 | } 25 | 26 | // 普通方法 27 | public static String getName(Integer key) { 28 | for (DatebitsEnum c : DatebitsEnum.values()) { 29 | if (c.getKey().equals(key)) { 30 | return c.value; 31 | } 32 | } 33 | return null; 34 | } 35 | 36 | public Integer getKey() { 37 | return key; 38 | } 39 | 40 | public String getValue() { 41 | return value; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/enums/ParityEnum.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.enums; 2 | 3 | /** 4 | * 类功能说明:校验位枚举类
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public enum ParityEnum { 9 | 10 | ZERO(0, "ZERO"), 11 | ONE(1, "ONE"), 12 | TWO(2, "TWO"), 13 | THREE(3, "THREE"), 14 | FOUR(4, "FOUR"), 15 | ; 16 | 17 | // 成员变量 18 | private Integer key; 19 | private String value; 20 | 21 | // 构造方法 22 | private ParityEnum(Integer key, String value) { 23 | this.key = key; 24 | this.value = value; 25 | } 26 | 27 | // 普通方法 28 | public static String getName(Integer key) { 29 | for (ParityEnum c : ParityEnum.values()) { 30 | if (c.getKey().equals(key)) { 31 | return c.value; 32 | } 33 | } 34 | return null; 35 | } 36 | 37 | public Integer getKey() { 38 | return key; 39 | } 40 | 41 | public String getValue() { 42 | return value; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/enums/StopbitsEnum.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.enums; 2 | 3 | /** 4 | * 类功能说明:停止位枚举类
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public enum StopbitsEnum { 9 | 10 | ONE(1, "ONE"), 11 | TWO(2, "TWO"), 12 | THREE(3, "THREE"), 13 | ; 14 | 15 | // 成员变量 16 | private Integer key; 17 | private String value; 18 | 19 | // 构造方法 20 | private StopbitsEnum(Integer key, String value) { 21 | this.key = key; 22 | this.value = value; 23 | } 24 | 25 | // 普通方法 26 | public static String getName(Integer key) { 27 | for (StopbitsEnum c : StopbitsEnum.values()) { 28 | if (c.getKey().equals(key)) { 29 | return c.value; 30 | } 31 | } 32 | return null; 33 | } 34 | 35 | public Integer getKey() { 36 | return key; 37 | } 38 | 39 | public String getValue() { 40 | return value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/handler/NettyRxtxDecoderHandler.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.handler; 2 | 3 | import java.util.List; 4 | 5 | import com.takeoff.iot.modbus.serialport.data.factory.SerialportDataFactory; 6 | import org.bouncycastle.util.encoders.Hex; 7 | 8 | import io.netty.buffer.ByteBuf; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import io.netty.handler.codec.MessageToMessageDecoder; 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | /** 14 | * 类功能说明:netty接收串口数据进行拆包处理
15 | * 公司名称:TF(腾飞)开源
16 | * 作者:luorongxi
17 | */ 18 | @Slf4j 19 | public class NettyRxtxDecoderHandler extends MessageToMessageDecoder { 20 | 21 | private SerialportDataFactory serialportDataFactory; 22 | 23 | public NettyRxtxDecoderHandler(SerialportDataFactory serialportDataFactory) { 24 | this.serialportDataFactory = serialportDataFactory; 25 | } 26 | 27 | @Override 28 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 29 | try { 30 | byte[] msg = new byte[in.readableBytes()]; 31 | in.readBytes(msg); 32 | log.info("接收到待处理指令:"+Hex.toHexString(msg)); 33 | serialportDataFactory.buildData(msg); 34 | } catch (Exception e) { 35 | log.error(e.getMessage(), e); 36 | throw e; 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/handler/NettyRxtxFrameDecoder.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.handler; 2 | 3 | import java.nio.ByteOrder; 4 | 5 | import com.takeoff.iot.modbus.common.message.MiiMessage; 6 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 7 | 8 | /** 9 | * 类功能说明:netty接收串口数据自定义解码器
10 | * 公司名称:TF(腾飞)开源
11 | * 作者:luorongxi
12 | */ 13 | public class NettyRxtxFrameDecoder extends LengthFieldBasedFrameDecoder { 14 | 15 | public NettyRxtxFrameDecoder(){ 16 | super(ByteOrder.LITTLE_ENDIAN, Integer.MAX_VALUE, MiiMessage.DATA_INDEX, MiiMessage.DATA_SIZE, 3, 0, true); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/service/SerialportSendService.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.service; 2 | 3 | import com.takeoff.iot.modbus.common.entity.AlarmLampData; 4 | import com.takeoff.iot.modbus.common.entity.LcdData; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 类功能说明:串口发送指令接口
10 | * 公司名称:TF(腾飞)开源
11 | * 作者:luorongxi
12 | */ 13 | public interface SerialportSendService { 14 | 15 | /** 16 | * 发送控制单锁指令. 17 | * @param deviceGroup 设备组编码 18 | * @param device 设备号 19 | */ 20 | void unlock(String deviceGroup, int device); 21 | 22 | /** 23 | * 发送控制多开锁指令. 24 | * @param deviceGroup 设备组编码 25 | * @param device 设备号 26 | * @param arr 数组(门锁号、门锁状态、门锁号、门锁状态...) 27 | */ 28 | void unlock(String deviceGroup, int device, Integer... arr); 29 | 30 | /** 31 | * 发送设置扫码模式指令. 32 | * @param deviceGroup 设备组编码 33 | * @param device 设备号 34 | * @param mode 扫码模式 {@link com.takeoff.iot.modbus.common.data.MiiData} ONCE ALWAYS NEVER 35 | */ 36 | void barcode(String deviceGroup, int device, int mode); 37 | 38 | /** 39 | * 发送背光灯指令. 40 | * @param deviceGroup 设备组编码 41 | * @param device 设备号 42 | * @param status 开关状态 {@link com.takeoff.iot.modbus.common.data.MiiData} ON OFF 43 | */ 44 | void backlight(String deviceGroup, int device, int status); 45 | 46 | /** 47 | * 注册指静脉. 48 | * @param deviceGroup 设备组编码 49 | * @param device 设备号 50 | * @param fingerId 手指ID 51 | */ 52 | void registerFinger(String deviceGroup, int device, int fingerId); 53 | 54 | /** 55 | * 单条删除指静脉. 56 | * @param deviceGroup 设备组编码 57 | * @param device 设备号 58 | * @param fingerId 手指ID 59 | */ 60 | void deleteFinger(String deviceGroup, int device, int fingerId); 61 | 62 | /** 63 | * 批量删除指静脉. 64 | * @param deviceGroup 设备组编码 65 | * @param device 设备号 66 | */ 67 | void deleteAllFinger(String deviceGroup, int device); 68 | 69 | /** 70 | * 查询所有指静脉. 71 | * @param deviceGroup 设备组编码 72 | * @param device 设备号 73 | */ 74 | void getFingerList(String deviceGroup, int device); 75 | 76 | /** 77 | * 获取手指ID指静脉特征. 78 | * @param deviceGroup 设备组编码 79 | * @param device 设备号 80 | * @param fingerId 手指ID 81 | */ 82 | void getFingerFeature(String deviceGroup, int device, int fingerId); 83 | 84 | /** 85 | * 写入指静脉特征. 86 | * @param deviceGroup 设备组编码 87 | * @param device 设备号 88 | * @param fingerId 手指ID 89 | */ 90 | void writeFingerFeature(String deviceGroup, int device, int fingerId, byte[] feature); 91 | 92 | /** 93 | * 按柜体批量发送lCD数据 94 | * @param lcdDataList 下发lcd显示内容 95 | */ 96 | void lcdBatch(List lcdDataList); 97 | 98 | /** 99 | * 下发控制三色报警灯数据 100 | * 参数:@param alarmLampData 下发报警灯数据 101 | */ 102 | void alarmLamp(AlarmLampData alarmLampData); 103 | 104 | } 105 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/service/SerialportService.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.service; 2 | 3 | /** 4 | * 类功能说明:串口通讯接口
5 | * 公司名称:TF(腾飞)开源
6 | * 作者:luorongxi
7 | */ 8 | public interface SerialportService { 9 | 10 | /** 11 | * 连接串口 12 | * @param port 13 | * @param baudrate 14 | * @param timeout 15 | * @param thread 16 | * @param sleepTime 17 | */ 18 | void openComPort(String port, Integer baudrate, Integer timeout, Integer thread, int sleepTime); 19 | 20 | /** 21 | * netty连接串口 22 | * @param port 23 | * @param baudrate 24 | * @param thread 25 | */ 26 | void openComPort(String port, Integer baudrate, Integer thread); 27 | 28 | /** 29 | * 关闭串口 30 | */ 31 | void closeSerialPort(); 32 | 33 | /** 34 | * 发送数据到串口 35 | * @param bytes 36 | */ 37 | void serialportSendData(byte[] bytes); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/utils/NettyRxtxClientUtil.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.utils; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.concurrent.Executors; 6 | 7 | import com.takeoff.iot.modbus.serialport.data.factory.SerialportDataReceiveFactory; 8 | import com.takeoff.iot.modbus.serialport.handler.NettyRxtxDecoderHandler; 9 | import com.takeoff.iot.modbus.serialport.handler.NettyRxtxFrameDecoder; 10 | import org.bouncycastle.util.encoders.Hex; 11 | 12 | import io.netty.bootstrap.Bootstrap; 13 | import io.netty.buffer.ByteBuf; 14 | import io.netty.channel.ChannelFuture; 15 | import io.netty.channel.ChannelFutureListener; 16 | import io.netty.channel.ChannelInitializer; 17 | import io.netty.channel.EventLoopGroup; 18 | import io.netty.channel.oio.OioEventLoopGroup; 19 | import io.netty.channel.rxtx.RxtxChannel; 20 | import io.netty.channel.rxtx.RxtxChannelConfig; 21 | import io.netty.channel.rxtx.RxtxDeviceAddress; 22 | import io.netty.handler.codec.string.StringEncoder; 23 | import lombok.extern.slf4j.Slf4j; 24 | 25 | /** 26 | * 类功能说明:netty链接串口工具类
27 | * 公司名称:TF(腾飞)开源
28 | * 作者:luorongxi
29 | */ 30 | @Slf4j 31 | public class NettyRxtxClientUtil { 32 | 33 | public static RxtxChannel channel; 34 | 35 | public static void createRxtx(String portName, Integer baudrate, Integer thread) throws Exception { 36 | RxtxChannel rxtxChannel = new RxtxChannel(); 37 | //串口使用阻塞io 38 | EventLoopGroup group = new OioEventLoopGroup(thread); 39 | try { 40 | Bootstrap bootstrap = new Bootstrap(); 41 | bootstrap.group(group) 42 | .channelFactory(() -> { 43 | rxtxChannel.config() 44 | .setBaudrate(baudrate) 45 | .setDatabits(RxtxChannelConfig.Databits.DATABITS_8) 46 | .setParitybit(RxtxChannelConfig.Paritybit.NONE) 47 | .setStopbits(RxtxChannelConfig.Stopbits.STOPBITS_1); 48 | return rxtxChannel ; 49 | }) 50 | .handler(new ChannelInitializer() { 51 | @Override 52 | protected void initChannel(RxtxChannel rxtxChannel) { 53 | rxtxChannel.pipeline().addLast( 54 | new NettyRxtxFrameDecoder(), 55 | new StringEncoder(StandardCharsets.UTF_8), 56 | new NettyRxtxDecoderHandler(new SerialportDataReceiveFactory()) 57 | ); 58 | } 59 | }); 60 | channel = rxtxChannel; 61 | ChannelFuture f = bootstrap.connect(new RxtxDeviceAddress(portName)).sync(); 62 | f.channel().closeFuture().sync(); 63 | } finally { 64 | group.shutdownGracefully(); 65 | } 66 | } 67 | 68 | public static void start(String portName, Integer baudrate, Integer thread){ 69 | CompletableFuture.runAsync(()->{ 70 | try { 71 | //阻塞的函数 72 | createRxtx(portName, baudrate, thread); 73 | } catch (Exception e) { 74 | e.printStackTrace(); 75 | } 76 | }, Executors.newSingleThreadExecutor());//不传默认使用ForkJoinPool,都是守护线程 77 | } 78 | 79 | /** 80 | * 发送数据 81 | * @param bytes 82 | */ 83 | public static void writeAndFlush(byte[] bytes) { 84 | if(!channel.isActive() 85 | || !channel.isOpen() 86 | || !channel.isWritable()){ 87 | return; 88 | } 89 | ByteBuf buffer = channel.alloc().buffer(); 90 | ByteBuf byteBuf = buffer.writeBytes(bytes); 91 | channel.writeAndFlush(byteBuf).addListener((ChannelFutureListener) future -> { 92 | if (future.isSuccess()) { 93 | log.info("指令下发成功:" + Hex.toHexString(bytes)); 94 | } else { 95 | log.error("指令下发失败:" + Hex.toHexString(bytes)); 96 | } 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /iot-modbus-serialport/src/main/java/com/takeoff/iot/modbus/serialport/utils/SpringContextUtils.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.serialport.utils; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * 类功能说明:Spring Context 工具类
10 | * 公司名称:TF(腾飞)开源
11 | * 作者:luorongxi
12 | */ 13 | @Component 14 | public class SpringContextUtils implements ApplicationContextAware { 15 | 16 | public static ApplicationContext applicationContext; 17 | 18 | @Override 19 | public void setApplicationContext(ApplicationContext applicationContext) 20 | throws BeansException { 21 | this.applicationContext = applicationContext; 22 | } 23 | 24 | public static Object getBean(String name) { 25 | return applicationContext.getBean(name); 26 | } 27 | 28 | public static T getBean(String name, Class requiredType) { 29 | return applicationContext.getBean(name, requiredType); 30 | } 31 | 32 | public static boolean containsBean(String name) { 33 | return applicationContext.containsBean(name); 34 | } 35 | 36 | public static boolean isSingleton(String name) { 37 | return applicationContext.isSingleton(name); 38 | } 39 | 40 | public static Class getType(String name) { 41 | return applicationContext.getType(name); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /iot-modbus-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | iot-modbus 8 | com.takeoff.iot 9 | 3.2.9-SNAPSHOT 10 | 11 | iot-modbus-server 12 | jar 13 | iot-modbus-server 14 | 物联网网口通讯协议服务端模块 15 | 16 | 17 | 3.2.9-SNAPSHOT 18 | 19 | 20 | 21 | 22 | com.takeoff.iot 23 | iot-modbus-netty 24 | ${iot-modbus-netty.version} 25 | 26 | 27 | 28 | 29 | ${project.artifactId} 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-surefire-plugin 35 | 36 | true 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /iot-modbus-server/src/main/java/com/takeoff/iot/modbus/server/MiiServer.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.server; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import com.takeoff.iot.modbus.common.utils.CacheUtils; 7 | import com.takeoff.iot.modbus.netty.device.MiiDeviceChannel; 8 | import com.takeoff.iot.modbus.netty.device.MiiDeviceGroup; 9 | import com.takeoff.iot.modbus.netty.device.MiiControlCentre; 10 | import com.takeoff.iot.modbus.common.bytes.factory.MiiDataFactory; 11 | import com.takeoff.iot.modbus.common.data.MiiHeartBeatData; 12 | import com.takeoff.iot.modbus.common.message.MiiMessage; 13 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 14 | import com.takeoff.iot.modbus.netty.channel.MiiChannelGroup; 15 | import com.takeoff.iot.modbus.netty.data.factory.MiiServerDataFactory; 16 | import com.takeoff.iot.modbus.netty.handle.*; 17 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 18 | import com.takeoff.iot.modbus.server.connect.MiiServerConnect; 19 | import com.takeoff.iot.modbus.server.message.sender.MiiServerMessageSender; 20 | import com.takeoff.iot.modbus.server.message.sender.ServerMessageSender; 21 | import io.netty.bootstrap.ServerBootstrap; 22 | import io.netty.channel.ChannelFuture; 23 | import io.netty.channel.ChannelHandlerContext; 24 | import io.netty.channel.ChannelInboundHandlerAdapter; 25 | import io.netty.channel.ChannelInitializer; 26 | import io.netty.channel.ChannelPipeline; 27 | import io.netty.channel.EventLoopGroup; 28 | import io.netty.channel.nio.NioEventLoopGroup; 29 | import io.netty.channel.socket.SocketChannel; 30 | import io.netty.channel.socket.nio.NioServerSocketChannel; 31 | import io.netty.handler.logging.LogLevel; 32 | import io.netty.handler.logging.LoggingHandler; 33 | import io.netty.handler.timeout.IdleStateEvent; 34 | import io.netty.handler.timeout.IdleStateHandler; 35 | import lombok.Getter; 36 | import lombok.extern.slf4j.Slf4j; 37 | 38 | /** 39 | * 类功能说明:设备通讯服务端
40 | * 公司名称:TF(腾飞)开源
41 | * 作者:luorongxi
42 | */ 43 | @Slf4j 44 | public class MiiServer extends ChannelInitializer implements MiiControlCentre { 45 | 46 | private static int IDLE_TIMEOUT = 60000; 47 | 48 | private EventLoopGroup bossGroup; 49 | private EventLoopGroup workerGroup; 50 | private ChannelFuture future; 51 | private int port,nThread; 52 | @Getter 53 | private MiiChannelGroup groups; 54 | private MiiServerConnect connect; 55 | private ServerMessageSender sender; 56 | private MiiListenerHandler handler; 57 | private MiiDataFactory dataFactory; 58 | 59 | /** 60 | * 创建指定服务端口,默认线程数的服务端 61 | * @param port 服务端口 62 | */ 63 | public MiiServer(int port){ 64 | this(port, 0, IDLE_TIMEOUT); 65 | } 66 | 67 | /** 68 | * 创建指定服务端口,指定线程数的服务端 69 | * @param port 服务端口 70 | * @param nThread 执行线程池线程数 71 | * @param heartBeatTime 心跳检测超时时间(单位:毫秒) 72 | */ 73 | public MiiServer(int port, int nThread, int heartBeatTime){ 74 | this.port = port; 75 | this.nThread = nThread; 76 | this.IDLE_TIMEOUT = heartBeatTime; 77 | this.groups = new MiiChannelGroup(); 78 | this.connect = new MiiServerConnect(); 79 | this.sender = new MiiServerMessageSender(); 80 | this.handler = new MiiListenerHandler(this.groups); 81 | this.handler.addListener(MiiMessage.HEARTBEAT, new MiiListener() { 82 | 83 | @Override 84 | public void receive(MiiChannel channel, MiiMessage message) { 85 | //通讯通道绑定设备IP 86 | groups.get(channel.name()).name(message.deviceGroup()); 87 | log.info("Netty通讯已绑定设备IP:"+ message.deviceGroup()); 88 | } 89 | }); 90 | this.dataFactory = new MiiServerDataFactory(); 91 | } 92 | 93 | /** 94 | * 启动服务 95 | */ 96 | public void start(){ 97 | bossGroup = new NioEventLoopGroup(1); 98 | workerGroup = new NioEventLoopGroup(nThread); 99 | ServerBootstrap b = new ServerBootstrap(); 100 | b.group(bossGroup, workerGroup) 101 | .channel(NioServerSocketChannel.class) 102 | .handler(new LoggingHandler(LogLevel.INFO)) 103 | .childHandler(this); 104 | future = b.bind(port); 105 | } 106 | 107 | /** 108 | * 停止服务 109 | */ 110 | public void stop(){ 111 | future.channel().closeFuture(); 112 | workerGroup.shutdownGracefully(); 113 | bossGroup.shutdownGracefully(); 114 | } 115 | 116 | /** 117 | * 根据名称/地址找已连接设备组 118 | * 名称/地址不存在或者未连接时返回null值 119 | * @param name 名称/地址 120 | * @return 设备组 121 | */ 122 | public MiiChannel group(String name) { 123 | return get(name); 124 | } 125 | 126 | /** 127 | * 列出所有已连接设备组清单 128 | * @return 所有已连接身边组清单 129 | */ 130 | public List groups() { 131 | return groups.list(); 132 | } 133 | 134 | public ServerMessageSender sender(){ 135 | return sender; 136 | } 137 | 138 | /** 139 | * 添加接收指定指令的消息监听器 140 | * @param command 指令类型 {@link MiiMessage} 141 | * @param listener 消息监听器 142 | * @return 上一个消息监听器,如果没有返回null 143 | */ 144 | public MiiListener addListener(int command, MiiListener listener){ 145 | return handler.addListener(command, listener); 146 | } 147 | 148 | /** 149 | * 移除接收指定指令的消息监听器 150 | * @param command 指令类型 {@link MiiMessage} 151 | * @return 移除消息监听器,如果没有返回null 152 | */ 153 | public MiiListener removeListener(int command){ 154 | return handler.removeListener(command); 155 | } 156 | 157 | @Override 158 | protected void initChannel(SocketChannel ch) throws Exception { 159 | ChannelPipeline p = ch.pipeline(); 160 | MiiDeviceGroup group = new MiiDeviceChannel(ch); 161 | add(group); 162 | //服务端心跳检测超时时间,超时则主动断开链接 163 | p.addLast(new IdleStateHandler(0, 0, IDLE_TIMEOUT, TimeUnit.MILLISECONDS)); 164 | p.addLast(new ChannelInboundHandlerAdapter(){ 165 | 166 | @Override 167 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 168 | if(evt instanceof IdleStateEvent){ 169 | ctx.disconnect(); 170 | } else { 171 | super.userEventTriggered(ctx, evt); 172 | } 173 | } 174 | }); 175 | p.addLast(new MiiMessageEncoder()); 176 | p.addLast(new MiiBasedFrameDecoder()); 177 | p.addLast(new MiiMessageDecoder(dataFactory)); 178 | p.addLast(connect); 179 | p.addLast(handler); 180 | p.addLast(new MiiExceptionHandler()); 181 | } 182 | 183 | @Override 184 | public boolean add(MiiChannel channel) { 185 | return groups.add(channel); 186 | } 187 | 188 | @Override 189 | public MiiChannel remove(String name) { 190 | return groups.remove(name); 191 | } 192 | 193 | @Override 194 | public MiiChannel get(String name) { 195 | return groups.get(name); 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /iot-modbus-server/src/main/java/com/takeoff/iot/modbus/server/connect/MiiServerConnect.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.server.connect; 2 | 3 | import com.takeoff.iot.modbus.common.entity.ChannelConnectData; 4 | import com.takeoff.iot.modbus.common.enums.DeviceConnectEnum; 5 | import com.takeoff.iot.modbus.common.utils.CacheUtils; 6 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 7 | import com.takeoff.iot.modbus.common.utils.SpringContextUtil; 8 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 9 | import com.takeoff.iot.modbus.netty.device.MiiDeviceChannel; 10 | import io.netty.bootstrap.Bootstrap; 11 | import io.netty.channel.*; 12 | import io.netty.channel.ChannelHandler.Sharable; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.springframework.context.ApplicationContext; 15 | import org.springframework.util.ObjectUtils; 16 | 17 | import java.net.SocketAddress; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * 类功能说明:服务端链接管理器
24 | * 公司名称:TF(腾飞)开源
25 | * 作者:luorongxi
26 | */ 27 | @Slf4j 28 | @Sharable 29 | public class MiiServerConnect extends ChannelInboundHandlerAdapter { 30 | 31 | private ApplicationContext getApplicationContext = SpringContextUtil.applicationContext; 32 | 33 | /** 34 | * 连接成功次数 35 | */ 36 | private Map onLineMap = new HashMap<>(); 37 | 38 | /** 39 | * 连接断开次数 40 | */ 41 | private Map breakOffMap = new HashMap<>(); 42 | 43 | public MiiServerConnect(){ 44 | 45 | } 46 | 47 | 48 | @Override 49 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 50 | //成功后,重连失败次数清零 51 | Channel channel = ctx.channel(); 52 | ctx.fireChannelActive(); 53 | if(!JudgeEmptyUtils.isEmpty(channel.remoteAddress())){ 54 | String address = channel.remoteAddress().toString().substring(1,channel.remoteAddress().toString().length()); 55 | MiiChannel miiChannel = new MiiDeviceChannel(channel); 56 | Integer onLine = (ObjectUtils.isEmpty(onLineMap.get(miiChannel.name())) ? 0 : onLineMap.get(miiChannel.name())) + 1; 57 | onLineMap.put(miiChannel.name(), onLine); 58 | ChannelConnectData connectServerData = new ChannelConnectData(this, DeviceConnectEnum.ON_LINE.getKey(), address, onLine); 59 | if(!JudgeEmptyUtils.isEmpty(connectServerData) && !JudgeEmptyUtils.isEmpty(getApplicationContext)){ 60 | getApplicationContext.publishEvent(connectServerData); 61 | //将柜地址与通讯管道的绑定关系写入缓存 62 | CacheUtils.put(miiChannel.name(), miiChannel); 63 | } 64 | } 65 | } 66 | 67 | @Override 68 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 69 | ctx.fireChannelInactive(); 70 | Channel channel = ctx.channel(); 71 | if(!JudgeEmptyUtils.isEmpty(channel) && !JudgeEmptyUtils.isEmpty(channel.remoteAddress())){ 72 | String address = channel.remoteAddress().toString().substring(1,channel.remoteAddress().toString().length()); 73 | MiiChannel miiChannel = new MiiDeviceChannel(channel); 74 | Integer breakOff = (ObjectUtils.isEmpty(breakOffMap.get(miiChannel.name())) ? 0 : breakOffMap.get(miiChannel.name())) + 1; 75 | breakOffMap.put(miiChannel.name(), breakOff); 76 | ChannelConnectData connectServerData = new ChannelConnectData(this, DeviceConnectEnum.BREAK_OFF.getKey(), address, breakOff); 77 | if(!JudgeEmptyUtils.isEmpty(connectServerData) && !JudgeEmptyUtils.isEmpty(getApplicationContext)){ 78 | getApplicationContext.publishEvent(connectServerData); 79 | } 80 | //将通讯管道的绑定关系从缓存中删除 81 | CacheUtils.remove(miiChannel.name()); 82 | //连接断开后的最后处理 83 | ctx.pipeline().remove(ctx.handler()); 84 | ctx.deregister(); 85 | ctx.close(); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /iot-modbus-server/src/main/java/com/takeoff/iot/modbus/server/message/sender/ServerMessageSender.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.server.message.sender; 2 | 3 | import com.takeoff.iot.modbus.common.entity.AlarmLampData; 4 | import com.takeoff.iot.modbus.common.entity.LcdData; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 类功能说明:指令下发接口
10 | * 公司名称:TF(腾飞)开源
11 | * 作者:luorongxi
12 | */ 13 | public interface ServerMessageSender { 14 | 15 | /** 16 | * 发送控制单锁指令. 17 | * @param deviceIp 设备IP 18 | * @param device 设备号 19 | */ 20 | void unlock(String deviceIp, int device); 21 | 22 | /** 23 | * 发送控制多开锁指令. 24 | * @param deviceIp 设备IP 25 | * @param device 设备号 26 | * @param arr 数组(门锁号、门锁状态、门锁号、门锁状态...) 27 | */ 28 | void unlock(String deviceIp, int device, Integer... arr); 29 | 30 | /** 31 | * 发送设置扫码模式指令. 32 | * @param deviceIp 设备IP 33 | * @param device 设备号 34 | * @param mode 扫码模式 {@link com.takeoff.iot.modbus.common.data.MiiData} ONCE ALWAYS NEVER 35 | */ 36 | void barcode(String deviceIp, int device, int mode); 37 | 38 | /** 39 | * 发送背光灯指令. 40 | * @param deviceIp 设备IP 41 | * @param device 设备号 42 | * @param status 开关状态 {@link com.takeoff.iot.modbus.common.data.MiiData} ON OFF 43 | */ 44 | void backlight(String deviceIp, int device, int status); 45 | 46 | /** 47 | * 注册指静脉. 48 | * @param deviceIp 设备IP 49 | * @param device 设备号 50 | * @param fingerId 手指ID 51 | */ 52 | void registerFinger(String deviceIp, int device, int fingerId); 53 | 54 | /** 55 | * 单条删除指静脉. 56 | * @param deviceIp 设备IP 57 | * @param device 设备号 58 | * @param fingerId 手指ID 59 | */ 60 | void deleteFinger(String deviceIp, int device, int fingerId); 61 | 62 | /** 63 | * 批量删除指静脉. 64 | * @param deviceIp 设备IP 65 | * @param device 设备号 66 | */ 67 | void deleteAllFinger(String deviceIp, int device); 68 | 69 | /** 70 | * 查询所有指静脉. 71 | * @param deviceIp 设备IP 72 | * @param device 设备号 73 | */ 74 | void getFingerList(String deviceIp, int device); 75 | 76 | /** 77 | * 获取手指ID指静脉特征. 78 | * @param deviceIp 设备IP 79 | * @param device 设备号 80 | * @param fingerId 手指ID 81 | */ 82 | void getFingerFeature(String deviceIp, int device, int fingerId); 83 | 84 | /** 85 | * 写入指静脉特征. 86 | * @param deviceIp 设备IP 87 | * @param device 设备号 88 | * @param fingerId 手指ID 89 | */ 90 | void writeFingerFeature(String deviceIp, int device, int fingerId, byte[] feature); 91 | 92 | /** 93 | * 按柜体批量发送lCD数据 94 | * 参数:@param lcdDataList 下发lcd显示内容 95 | */ 96 | void lcdBatch(List lcdDataList); 97 | 98 | /** 99 | * 下发控制三色报警灯数据 100 | * 参数:@param alarmLampData 下发报警灯数据 101 | */ 102 | void alarmLamp(AlarmLampData alarmLampData); 103 | } 104 | -------------------------------------------------------------------------------- /iot-modbus-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.takeoff.iot 8 | iot-modbus 9 | 3.2.9-SNAPSHOT 10 | 11 | iot-modbus-test 12 | jar 13 | iot-modbus-test 14 | 物联网通讯协议测试 15 | 16 | 17 | 5.6.2 18 | 2.6 19 | 2.9.9 20 | 3.2.9-SNAPSHOT 21 | 3.2.9-SNAPSHOT 22 | 3.2.9-SNAPSHOT 23 | 24 | 25 | 26 | 27 | cn.hutool 28 | hutool-all 29 | ${hutool.version} 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-thymeleaf 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-aop 47 | 48 | 49 | org.springframework 50 | spring-context-support 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-data-redis 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-configuration-processor 59 | true 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-devtools 64 | true 65 | 66 | 67 | commons-lang 68 | commons-lang 69 | ${commons.lang.version} 70 | 71 | 72 | joda-time 73 | joda-time 74 | ${joda.time.version} 75 | 76 | 77 | com.takeoff.iot 78 | iot-modbus-server 79 | ${iot-modbus-server.version} 80 | 81 | 82 | com.takeoff.iot 83 | iot-modbus-client 84 | ${iot-modbus-client.version} 85 | 86 | 87 | com.takeoff.iot 88 | iot-modbus-serialport 89 | ${iot-modbus-serialport.version} 90 | 91 | 92 | 93 | 94 | ${project.artifactId} 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-maven-plugin 99 | 100 | true 101 | 102 | 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-surefire-plugin 107 | 108 | true 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/App.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | public static void main(String[] args) { 9 | SpringApplication.run(App.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/config/IotModbusClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.config; 2 | 3 | import com.takeoff.iot.modbus.client.MiiClient; 4 | import com.takeoff.iot.modbus.common.message.MiiMessage; 5 | import com.takeoff.iot.modbus.test.listener.LockListener; 6 | import com.takeoff.iot.modbus.test.properties.IotModbusClientProperties; 7 | import lombok.Getter; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.boot.ApplicationArguments; 10 | import org.springframework.boot.ApplicationRunner; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | import javax.annotation.Resource; 14 | 15 | /** 16 | * 类功能说明:通讯协议客户端配置注册
17 | * 公司名称:TF(腾飞)开源
18 | * 作者:luorongxi
19 | */ 20 | @Slf4j 21 | @Configuration 22 | public class IotModbusClientConfig implements ApplicationRunner { 23 | 24 | @Resource 25 | private IotModbusClientProperties iotModbusClientProperties; 26 | 27 | @Resource 28 | private LockListener lockListener; 29 | 30 | @Getter 31 | private MiiClient miiClient; 32 | 33 | 34 | @Override 35 | public void run(ApplicationArguments args) throws Exception { 36 | if(iotModbusClientProperties.getOpen()){ 37 | miiClient = new MiiClient(iotModbusClientProperties.getDeviceGroup(), iotModbusClientProperties.getThread(), iotModbusClientProperties.getHeartBeatTime()); 38 | String[] ips = new String[1]; 39 | if(iotModbusClientProperties.getIps().contains(",")){ 40 | ips = iotModbusClientProperties.getIps().split(","); 41 | }else{ 42 | ips[0] = iotModbusClientProperties.getIps(); 43 | } 44 | miiClient.addListener(MiiMessage.LOCK, lockListener); 45 | for(int i=0;i 14 | * 公司名称:TF(腾飞)开源
15 | * 作者:luorongxi
16 | */ 17 | @Slf4j 18 | @Configuration 19 | public class IotModbusSerialportConfig implements ApplicationRunner { 20 | 21 | @Resource 22 | private IotModbusSerialportProperties iotModbusSerialportProperties; 23 | 24 | @Resource 25 | private SerialportService serialportService; 26 | 27 | @Override 28 | public void run(ApplicationArguments args) throws Exception { 29 | if(iotModbusSerialportProperties.getOpen()){ 30 | if(iotModbusSerialportProperties.getNetty()){ 31 | serialportService.openComPort(iotModbusSerialportProperties.getPort(), iotModbusSerialportProperties.getBaudrate(), iotModbusSerialportProperties.getThread()); 32 | }else{ 33 | serialportService.openComPort(iotModbusSerialportProperties.getPort(), iotModbusSerialportProperties.getBaudrate(), iotModbusSerialportProperties.getTimeout(), iotModbusSerialportProperties.getThread(), iotModbusSerialportProperties.getSleepTime()); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/config/IotModbusServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.config; 2 | import com.takeoff.iot.modbus.common.message.MiiMessage; 3 | import com.takeoff.iot.modbus.server.MiiServer; 4 | import com.takeoff.iot.modbus.test.listener.*; 5 | import com.takeoff.iot.modbus.test.properties.IotModbusServerProperties; 6 | import org.springframework.boot.ApplicationArguments; 7 | import org.springframework.boot.ApplicationRunner; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | import lombok.Getter; 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | import javax.annotation.Resource; 14 | 15 | /** 16 | * 类功能说明:通讯协议服务端配置注册
17 | * 公司名称:TF(腾飞)开源
18 | * 作者:luorongxi
19 | */ 20 | @Slf4j 21 | @Configuration 22 | public class IotModbusServerConfig implements ApplicationRunner { 23 | 24 | @Resource 25 | private IotModbusServerProperties iotModbusServerProperties; 26 | 27 | @Resource 28 | private CardListener cardListener; 29 | 30 | @Resource 31 | private BarCodeListener barCodeListener; 32 | 33 | @Resource 34 | private BackLightListener backLightListener; 35 | 36 | @Resource 37 | private LockListener lockListener; 38 | 39 | @Resource 40 | private FingerListener fingerListener; 41 | 42 | @Resource 43 | private HumitureListener humitureListener; 44 | 45 | @Getter 46 | private MiiServer miiServer; 47 | 48 | @Override 49 | public void run(ApplicationArguments args) throws Exception { 50 | if(iotModbusServerProperties.getOpen()){ 51 | miiServer = new MiiServer(iotModbusServerProperties.getPort(), iotModbusServerProperties.getThread(), iotModbusServerProperties.getHeartBeatTime()); 52 | miiServer.addListener(MiiMessage.BACKLIGHT, backLightListener); 53 | miiServer.addListener(MiiMessage.LOCK, lockListener); 54 | miiServer.addListener(MiiMessage.CARD, cardListener); 55 | miiServer.addListener(MiiMessage.BARCODE, barCodeListener); 56 | miiServer.addListener(MiiMessage.FINGER, fingerListener); 57 | miiServer.addListener(MiiMessage.HM, humitureListener); 58 | log.info("IOT通讯协议已开启Socket服务,占用端口: " + iotModbusServerProperties.getPort() + ",执行线程池线程数:" + iotModbusServerProperties.getThread()); 59 | miiServer.start(); 60 | }else{ 61 | log.info("IOT通讯协议未开启Socket服务"); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/listener/BackLightListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.listener; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiBackLightData; 4 | import com.takeoff.iot.modbus.common.message.MiiMessage; 5 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 6 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 7 | import com.takeoff.iot.modbus.serialport.data.BackLightData; 8 | import com.takeoff.iot.modbus.serialport.data.BarCodeData; 9 | import org.springframework.context.event.EventListener; 10 | import org.springframework.stereotype.Component; 11 | 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | /** 15 | * 类功能说明:背光灯指令监听器
16 | * 公司名称:TF(腾飞)开源
17 | * 作者:luorongxi
18 | */ 19 | @Slf4j 20 | @Component 21 | public class BackLightListener implements MiiListener { 22 | 23 | @Override 24 | public void receive(MiiChannel channel, MiiMessage message) { 25 | if(message.command() == MiiMessage.BACKLIGHT){ 26 | log.info("监听到背光灯指令: "+ message.command()); 27 | MiiBackLightData data = (MiiBackLightData) message.data(); 28 | log.info("背光灯状态码:: "+ data.statusCode()); 29 | } 30 | } 31 | 32 | @EventListener 33 | public void handleReceiveDataEvent(BackLightData data) { 34 | log.info("监听到背光灯指令: "+ data.getCommand()); 35 | log.info("背光灯状态码: "+ data.getStatusCode()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/listener/BarCodeListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.listener; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiBarcodeData; 4 | import com.takeoff.iot.modbus.common.message.MiiMessage; 5 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 6 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 7 | import com.takeoff.iot.modbus.serialport.data.BarCodeData; 8 | import com.takeoff.iot.modbus.serialport.data.ReceiveDataEvent; 9 | import org.springframework.context.event.EventListener; 10 | import org.springframework.stereotype.Component; 11 | 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | /** 15 | * 类功能说明:扫码指令监听器
16 | * 公司名称:TF(腾飞)开源
17 | * 作者:luorongxi
18 | */ 19 | @Slf4j 20 | @Component 21 | public class BarCodeListener implements MiiListener { 22 | 23 | @Override 24 | public void receive(MiiChannel channel, MiiMessage message) { 25 | if(message.command() == MiiMessage.BARCODE){ 26 | log.info("监听到扫码指令: "+ message.command()); 27 | MiiBarcodeData data = (MiiBarcodeData) message.data(); 28 | log.info("条形码: "+ data.content()); 29 | } 30 | } 31 | 32 | @EventListener 33 | public void handleReceiveDataEvent(BarCodeData data) { 34 | log.info("监听到扫码指令: "+ data.getCommand()); 35 | log.info("条形码: "+ data.getBarCode()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/listener/CardListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.listener; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiCardData; 4 | import com.takeoff.iot.modbus.common.message.MiiMessage; 5 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 6 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 7 | import com.takeoff.iot.modbus.serialport.data.CardData; 8 | import com.takeoff.iot.modbus.serialport.data.ReceiveDataEvent; 9 | import org.springframework.context.event.EventListener; 10 | import org.springframework.stereotype.Component; 11 | 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | /** 15 | * 类功能说明:刷卡指令监听器
16 | * 公司名称:TF(腾飞)开源
17 | * 作者:luorongxi
18 | */ 19 | @Slf4j 20 | @Component 21 | public class CardListener implements MiiListener { 22 | 23 | @Override 24 | public void receive(MiiChannel channel, MiiMessage message) { 25 | if(message.command() == MiiMessage.CARD){ 26 | log.info("监听到刷卡指令: "+ message.command()); 27 | MiiCardData data = (MiiCardData) message.data(); 28 | String cardCode = data.content(); 29 | log.info("卡号: "+ cardCode); 30 | } 31 | } 32 | 33 | @EventListener 34 | public void handleReceiveDataEvent(CardData data) { 35 | log.info("监听到刷卡指令: "+ data.getCommand()); 36 | log.info("卡号: "+ data.getCardCode()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/listener/ChannelConnectListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.listener; 2 | 3 | import com.takeoff.iot.modbus.common.entity.ChannelConnectData; 4 | import com.takeoff.iot.modbus.common.utils.JudgeEmptyUtils; 5 | import org.springframework.context.event.EventListener; 6 | import org.springframework.stereotype.Component; 7 | 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | @Slf4j 11 | @Component 12 | public class ChannelConnectListener { 13 | 14 | @EventListener 15 | public void handleReceiveDataEvent(ChannelConnectData data) { 16 | if(JudgeEmptyUtils.isEmpty(data.getDeviceConnect())){ 17 | log.info("设备连接状态码:"+data.getDeviceConnect()+" ---> "+data.getConnectMsg()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/listener/FingerListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.listener; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiFingerData; 4 | import com.takeoff.iot.modbus.common.message.MiiMessage; 5 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 6 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 7 | import com.takeoff.iot.modbus.serialport.data.FingerData; 8 | import org.springframework.context.event.EventListener; 9 | import org.springframework.stereotype.Component; 10 | 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | /** 14 | * 类功能说明:指静脉指令监听器
15 | * 公司名称:TF(腾飞)开源
16 | * 作者:luorongxi
17 | */ 18 | @Slf4j 19 | @Component 20 | public class FingerListener implements MiiListener { 21 | 22 | @Override 23 | public void receive(MiiChannel channel, MiiMessage message) { 24 | if(message.command() == MiiMessage.FINGER){ 25 | log.info("监听到指静脉指令: "+ message.command()); 26 | MiiFingerData data = (MiiFingerData) message.data(); 27 | int fingerType = data.fingerType(); 28 | log.info("指纹状态: "+ fingerType); 29 | int fingerId = data.fingerId(); 30 | log.info("指纹特征码:"+fingerId); 31 | } 32 | } 33 | 34 | @EventListener 35 | public void handleReceiveDataEvent(FingerData data) { 36 | log.info("监听到指静脉指令: "+ data.getCommand()); 37 | log.info("指纹状态: "+ data.getFingerType()); 38 | log.info("指纹特征码:"+data.getFingerId()); 39 | } 40 | } -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/listener/HeartBeatListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.listener; 2 | 3 | import com.takeoff.iot.modbus.serialport.data.BackLightData; 4 | import com.takeoff.iot.modbus.serialport.data.HeartBeatData; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.context.event.EventListener; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | public class HeartBeatListener { 12 | 13 | @EventListener 14 | public void handleReceiveDataEvent(HeartBeatData data) { 15 | log.info("监听到心跳指令: "+ data.getCommand()); 16 | log.info("设备号: "+ data.getDeviceGroup()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/listener/HumitureListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.listener; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiHumitureData; 4 | import com.takeoff.iot.modbus.common.message.MiiMessage; 5 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 6 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 7 | import com.takeoff.iot.modbus.serialport.data.HumitureData; 8 | import org.springframework.context.event.EventListener; 9 | import org.springframework.stereotype.Component; 10 | 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | /** 14 | * 类功能说明:温湿度指令监听器
15 | * 公司名称:TF(腾飞)开源
16 | * 作者:luorongxi
17 | */ 18 | @Slf4j 19 | @Component 20 | public class HumitureListener implements MiiListener { 21 | 22 | @Override 23 | public void receive(MiiChannel channel, MiiMessage message) { 24 | if(message.command() == MiiMessage.HM){ 25 | log.info("监听到温湿度指令: "+ message.command()); 26 | MiiHumitureData data = (MiiHumitureData) message.data(); 27 | log.info("设备号: "+ data.device()); 28 | log.info("温度: "+ data.temperature()+",湿度:"+data.humidity()); 29 | } 30 | } 31 | 32 | @EventListener 33 | public void handleReceiveDataEvent(HumitureData data) { 34 | log.info("监听到温湿度指令: "+ data.getCommand()); 35 | log.info("设备号: "+ data.getDevice()); 36 | log.info("温度: "+ data.getTemperature()+",湿度:"+data.getHumidity()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/listener/LockListener.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.listener; 2 | 3 | import com.takeoff.iot.modbus.common.data.MiiLockData; 4 | import com.takeoff.iot.modbus.common.message.MiiMessage; 5 | import com.takeoff.iot.modbus.netty.channel.MiiChannel; 6 | import com.takeoff.iot.modbus.netty.listener.MiiListener; 7 | import com.takeoff.iot.modbus.serialport.data.LockData; 8 | import org.springframework.context.event.EventListener; 9 | import org.springframework.stereotype.Component; 10 | 11 | import com.alibaba.fastjson.JSON; 12 | 13 | import lombok.extern.slf4j.Slf4j; 14 | 15 | /** 16 | * 类功能说明:开门状态指令监听器
17 | * 公司名称:TF(腾飞)开源
18 | * 作者:luorongxi
19 | */ 20 | @Slf4j 21 | @Component 22 | public class LockListener implements MiiListener { 23 | 24 | @Override 25 | public void receive(MiiChannel channel, MiiMessage message) { 26 | if(message.command() == MiiMessage.LOCK){ 27 | MiiLockData data = (MiiLockData) message.data(); 28 | log.info("监听到服务端:"+channel.name()+",门锁状态指令: "+ message.command()); 29 | //判断是否为多锁 30 | if(data.list().size() == 1){ 31 | //单锁处理逻辑 32 | log.info("监听到服务端:"+channel.name()+",进入单锁处理流程,门锁状态: "+ JSON.toJSONString(data.list())); 33 | }else{ 34 | //多锁处理逻辑 35 | log.info("监听到服务端:"+channel.name()+",进入多锁处理流程,门锁状态: "+ JSON.toJSONString(data.list())); 36 | } 37 | } 38 | } 39 | 40 | @EventListener 41 | public void handleReceiveDataEvent(LockData data) { 42 | log.info("监听到单开门状态指令: "+ data.getCommand()); 43 | log.info("设备号: "+ data.getDevice()); 44 | //判断是否为多锁 45 | if(data.getList().size() == 1){ 46 | //单锁处理逻辑 47 | log.info("已进入单锁处理流程,门锁状态: "+ JSON.toJSONString(data.getList())); 48 | }else{ 49 | //多锁处理逻辑 50 | log.info("已进入多锁处理流程,门锁状态: "+ JSON.toJSONString(data.getList())); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/properties/IotModbusClientProperties.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 类功能说明:netty通讯客户端参数配置
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | @Data 13 | @Component 14 | @ConfigurationProperties(prefix = "iot.netty.client") 15 | public class IotModbusClientProperties { 16 | 17 | /** 18 | * 是否开启Socket服务 19 | */ 20 | private Boolean open; 21 | 22 | /** 23 | * 服务IP,多个使用英文逗号隔开 24 | */ 25 | private String ips; 26 | 27 | /** 28 | * 服务端口 29 | */ 30 | private Integer port; 31 | 32 | /** 33 | * Socket服务执行线程数 34 | */ 35 | private Integer thread; 36 | 37 | /** 38 | * 设备组编码 39 | */ 40 | private String deviceGroup; 41 | 42 | /** 43 | * 心跳发送间隔时间(单位:毫秒) 44 | */ 45 | private Integer heartBeatTime; 46 | 47 | /** 48 | * 断线重连间隔时间(单位:毫秒) 49 | */ 50 | private Integer reconnectTime; 51 | } 52 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/properties/IotModbusSerialportProperties.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 类功能说明:netty通讯客户端参数配置
9 | * 公司名称:TF(腾飞)开源
10 | * 作者:luorongxi
11 | */ 12 | @Data 13 | @Component 14 | @ConfigurationProperties(prefix = "iot.serialport") 15 | public class IotModbusSerialportProperties { 16 | 17 | /** 18 | * 是否开启串口服务 19 | */ 20 | private Boolean open; 21 | 22 | /** 23 | * 串口号 24 | */ 25 | private String port; 26 | 27 | /** 28 | * 是否使用netty对数据进行拆包处理 29 | */ 30 | private Boolean netty; 31 | 32 | /** 33 | * 链接超时时间,不使用netty对数据进行拆包处理时必填 34 | */ 35 | private Integer timeout; 36 | 37 | /** 38 | * 波特率 39 | */ 40 | private Integer baudrate; 41 | 42 | /** 43 | * 设置通讯服务执行线程数 44 | */ 45 | private Integer thread; 46 | 47 | /** 48 | * 接收数据休眠时间,串口方式必填 49 | */ 50 | private Integer sleepTime; 51 | } 52 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/properties/IotModbusServerProperties.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.stereotype.Component; 5 | 6 | import lombok.Data; 7 | 8 | /** 9 | * 类功能说明:netty通讯服务端参数配置
10 | * 公司名称:TF(腾飞)开源
11 | * 作者:luorongxi
12 | */ 13 | @Data 14 | @Component 15 | @ConfigurationProperties(prefix = "iot.netty.server") 16 | public class IotModbusServerProperties { 17 | 18 | /** 19 | * 是否开启Socket服务 20 | */ 21 | private Boolean open; 22 | 23 | /** 24 | * Socket服务端口 25 | */ 26 | private Integer port; 27 | 28 | /** 29 | * Socket服务执行线程数 30 | */ 31 | private Integer thread; 32 | 33 | /** 34 | * 心跳检测超时时间(单位:毫秒) 35 | */ 36 | private Integer heartBeatTime; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/java/com/takeoff/iot/modbus/test/utils/R.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import cn.hutool.http.HttpStatus; 7 | 8 | /** 9 | * 类功能说明:返回数据
10 | * 公司名称:TF(腾飞)开源
11 | * 作者:luorongxi
12 | */ 13 | public class R extends HashMap { 14 | 15 | private static final long serialVersionUID = 1L; 16 | 17 | private static final String CODE_TAG = "code"; //返回码 18 | 19 | private static final String Type_TAG = "type"; //操作类型 20 | 21 | private static final String MSG_TAG = "msg"; //返回提示 22 | 23 | private static final String DATA_TAG = "data"; //返回数据 24 | 25 | public R() { 26 | put(CODE_TAG, HttpStatus.HTTP_OK); 27 | put(MSG_TAG, "success"); 28 | } 29 | 30 | public static R error() { 31 | return error(HttpStatus.HTTP_INTERNAL_ERROR, "未知异常,请联系管理员"); 32 | } 33 | 34 | public static R error(String msg) { 35 | return error(HttpStatus.HTTP_INTERNAL_ERROR, msg); 36 | } 37 | 38 | public static R error(int code, String msg) { 39 | R r = new R(); 40 | r.put(CODE_TAG, code); 41 | r.put(MSG_TAG, msg); 42 | return r; 43 | } 44 | 45 | public static R ok(String msg) { 46 | R r = new R(); 47 | r.put(MSG_TAG, msg); 48 | return r; 49 | } 50 | 51 | public static R ok(Object data) { 52 | R r = new R(); 53 | r.put(DATA_TAG, data); 54 | return r; 55 | } 56 | 57 | public static R ok(String type, Object data, String msg) { 58 | R r = new R(); 59 | r.put(Type_TAG, type); 60 | r.put(DATA_TAG, data); 61 | r.put(MSG_TAG, msg); 62 | return r; 63 | } 64 | 65 | public static R ok(Map map) { 66 | R r = new R(); 67 | r.putAll(map); 68 | return r; 69 | } 70 | 71 | public static R ok() { 72 | return new R(); 73 | } 74 | 75 | @Override 76 | public R put(String key, Object value) { 77 | super.put(key, value); 78 | return this; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # Tomcat 2 | server: 3 | port: 8080 4 | tomcat: 5 | uri-encoding: UTF-8 6 | max-threads: 1000 7 | min-spare-threads: 30 8 | servlet: 9 | context-path: /iot-modbus-test 10 | 11 | devtools: 12 | restart: 13 | enabled: true #是否支持热部署 14 | 15 | iot: 16 | netty: # netty通讯 17 | server: 18 | open: true 19 | port: 4000 20 | thread: 10 21 | heart-beat-time: 30000 #心跳检测超时时间(单位:毫秒) 22 | client: 23 | open: false 24 | ips: 127.0.0.1,192.168.1.198 #设置读写器IP,多个使用英文逗号隔开 25 | port: 5000 26 | thread: 10 27 | device-group: 1 28 | heart-beat-time: 3000 #心跳发送间隔时间(单位:毫秒) 29 | reconnect-time: 5000 #断线重连间隔时间(单位:毫秒) 30 | serialport: #串口通讯 31 | open: false 32 | port: COM1 33 | netty: true #是否使用netty对数据进行拆包处理 34 | timeout: 5000 #链接超时时间,不使用netty对数据进行拆包处理时必填 35 | baudrate: 115200 36 | thread: 10 37 | sleep-time: 2 #接收数据休眠时间(单位:毫秒),串口方式必填 -------------------------------------------------------------------------------- /iot-modbus-test/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.BRIGHT_YELLOW} 2 | 3 | ::::: ::::::::::: ::::::::::::: 4 | :+: :+: :+: :+: 5 | +:+ +:+ +:+ +:+ 6 | :#: :#: :#: :#: 7 | +#+ +#+ +#+ +#+ 8 | #+# #+# #+# #+# 9 | ##### ########### ### 10 | 11 | www.takeoff.org 12 | 13 | TF(腾飞)开源 Microservice Architecture 14 | 15 | 16 | -------------------------------------------------------------------------------- /iot-modbus-test/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | ${CONSOLE_LOG_PATTERN} 19 | UTF-8 20 | 21 | 22 | INFO 23 | 24 | 25 | 26 | 27 | 28 | ${log.path}/info.log 29 | 30 | ${log.path}/%d{yyyy-MM, aux}/info.%d{yyyy-MM-dd}.%i.log.gz 31 | 64MB 32 | 90 33 | 34 | 35 | %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n 36 | UTF-8 37 | 38 | 39 | INFO 40 | 41 | 42 | 43 | 44 | 45 | ${log.path}/error.log 46 | 47 | ${log.path}/%d{yyyy-MM, aux}/error.%d{yyyy-MM-dd}.%i.log.gz 48 | 64MB 49 | 90 50 | 51 | 52 | %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n 53 | UTF-8 54 | 55 | 56 | ERROR 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 0 87 | 88 | 256 89 | 90 | 91 | 92 | 93 | 94 | 0 95 | 96 | 256 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /iot-modbus-test/src/test/java/com/takeoff/iot/modbus/test/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.takeoff.iot.modbus.test; 2 | 3 | import com.takeoff.iot.modbus.test.config.IotModbusServerConfig; 4 | import junit.framework.TestCase; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | import javax.annotation.Resource; 12 | 13 | @Slf4j 14 | @RunWith(SpringRunner.class) 15 | @SpringBootTest 16 | public class AppTest extends TestCase { 17 | 18 | private static final String DEVICE_IP = "192.168.1.198"; 19 | 20 | private static final Integer DEVICE = 1; 21 | 22 | @Resource 23 | private IotModbusServerConfig iotModbusServerConfig; 24 | 25 | @Test 26 | public void openLockTest(){ 27 | iotModbusServerConfig.getMiiServer().sender().unlock(DEVICE_IP, DEVICE); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.takeoff.iot 6 | iot-modbus 7 | 3.2.9-SNAPSHOT 8 | pom 9 | 10 | iot-modbus 11 | https://www.takeoff.org/ 12 | 物联网通讯协议 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.1.8.RELEASE 18 | 19 | 20 | 21 | UTF-8 22 | UTF-8 23 | 3.1.1 24 | 3.6.2 25 | 2.1 26 | 1.8 27 | 4.12 28 | 1.16.20 29 | 1.7.32 30 | 1.7.32 31 | 1.2.7 32 | 1.68 33 | 34 | 35 | 36 | 37 | junit 38 | junit 39 | ${junit.version} 40 | test 41 | 42 | 43 | org.projectlombok 44 | lombok 45 | ${lombok.version} 46 | provided 47 | 48 | 49 | org.slf4j 50 | slf4j-api 51 | ${slf4j-api.version} 52 | 53 | 54 | org.slf4j 55 | slf4j-simple 56 | ${slf4j-simple.version} 57 | test 58 | 59 | 60 | 61 | com.alibaba 62 | fastjson 63 | ${fastjson.version} 64 | 65 | 66 | org.bouncycastle 67 | bcprov-jdk15on 68 | ${bcprov-jdk15on.version} 69 | 70 | 71 | 72 | 73 | 74 | 75 | sonatype-nexus-releases 76 | Sonatype Nexus Release Repository 77 | http://192.168.1.40:8006/nexus/content/repositories/releases/ 78 | 79 | 80 | sonatype-nexus-snapshots 81 | Sonatype Nexus Snapshot Repository 82 | http://192.168.1.40:8006/nexus/content/repositories/snapshots/ 83 | 84 | 85 | 86 | 87 | iot-modbus-common 88 | iot-modbus-netty 89 | iot-modbus-server 90 | iot-modbus-client 91 | iot-modbus-serialport 92 | iot-modbus-test 93 | 94 | -------------------------------------------------------------------------------- /tools/NetAssist.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/tools/NetAssist.exe -------------------------------------------------------------------------------- /tools/UartAssist.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/tools/UartAssist.exe -------------------------------------------------------------------------------- /tools/vspd7.2.308(串口模拟工具).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luorongxi/iot-modbus/ebd6d00d29eb744a046e0593d9613a53dd8eaa07/tools/vspd7.2.308(串口模拟工具).zip --------------------------------------------------------------------------------