From 34ea26c4b633413096532369165c96dea82c44e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E9=B1=BC=E5=B9=B2?= <1810377322@163.com>
Date: Fri, 6 Jun 2025 18:04:03 +0800
Subject: [PATCH] Initial commit
---
.gitee/ISSUE_TEMPLATE.zh-CN.md | 13 +
.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md | 53 ++
.gitignore | 33 ++
DLT645-plugin/pom.xml | 87 +++
.../cc/iotkit/plugins/dlt645/Application.java | 21 +
.../dlt645/analysis/DLT645Analysis.java | 323 +++++++++++
.../dlt645/analysis/DLT645Converter.java | 44 ++
.../plugins/dlt645/analysis/DLT645Data.java | 92 ++++
.../dlt645/analysis/DLT645DataFormat.java | 361 +++++++++++++
.../dlt645/analysis/DLT645FunCode.java | 302 +++++++++++
.../dlt645/analysis/DLT645V1997Data.java | 63 +++
.../dlt645/analysis/DLT645V2007Data.java | 66 +++
.../plugins/dlt645/conf/BeanConfig.java | 39 ++
.../plugins/dlt645/conf/TcpClientConfig.java | 21 +
.../dlt645/constants/DLT645Constant.java | 10 +
.../dlt645/load/DLT645v1997CsvLoader.java | 93 ++++
.../dlt645/load/DLT645v2007CsvLoader.java | 95 ++++
.../plugins/dlt645/service/DLT645Device.java | 54 ++
.../plugins/dlt645/service/Dlt645Plugin.java | 88 +++
.../dlt645/service/FakeThingService.java | 46 ++
.../dlt645/service/TcpClientVerticle.java | 184 +++++++
.../iotkit/plugins/dlt645/utils/ByteRef.java | 15 +
.../plugins/dlt645/utils/ByteUtils.java | 78 +++
.../iotkit/plugins/dlt645/utils/BytesRef.java | 15 +
.../plugins/dlt645/utils/ContainerUtils.java | 424 +++++++++++++++
.../src/main/resources/DLT645-1997.csv | 143 +++++
.../src/main/resources/DLT645-2007.csv | 425 +++++++++++++++
.../src/main/resources/application.yml | 8 +
DLT645-plugin/src/main/resources/config.json | 23 +
LICENSE | 201 +++++++
README.en.md | 36 ++
README.md | 37 ++
emqx-plugin/pom.xml | 86 +++
.../cc/iotkit/plugins/emqx/Application.java | 19 +
.../plugins/emqx/conf/BCDClockGenerator.java | 39 ++
.../iotkit/plugins/emqx/conf/BeanConfig.java | 28 +
.../plugins/emqx/conf/CRC16ModbusUtil.java | 64 +++
.../plugins/emqx/conf/DeviceStatusParser.java | 99 ++++
.../plugins/emqx/conf/EventTypeMapper.java | 53 ++
.../plugins/emqx/conf/GasAlarmDataParser.java | 201 +++++++
.../plugins/emqx/conf/IoTConfigProtocol.java | 197 +++++++
.../iotkit/plugins/emqx/conf/MqttConfig.java | 30 ++
.../plugins/emqx/conf/ProtocolTest.java | 56 ++
.../plugins/emqx/conf/ResponseParserDemo.java | 27 +
.../plugins/emqx/handler/IMsgHandler.java | 12 +
.../plugins/emqx/service/AuthVerticle.java | 175 ++++++
.../plugins/emqx/service/EmqxPlugin.java | 507 ++++++++++++++++++
.../emqx/service/FakeThingService.java | 70 +++
.../plugins/emqx/service/MqttDevice.java | 186 +++++++
.../src/main/resources/application.yml | 9 +
emqx-plugin/src/main/resources/config.json | 30 ++
http-plugin/pom.xml | 81 +++
.../cc/iotkit/plugins/http/Application.java | 19 +
.../iotkit/plugins/http/conf/BeanConfig.java | 29 +
.../iotkit/plugins/http/conf/HttpConfig.java | 28 +
.../http/service/FakeThingService.java | 48 ++
.../plugins/http/service/HttpDevice.java | 43 ++
.../plugins/http/service/HttpPlugin.java | 91 ++++
.../plugins/http/service/HttpVerticle.java | 206 +++++++
.../src/main/resources/application.yml | 6 +
http-plugin/src/main/resources/config.json | 26 +
http-plugin/src/main/resources/script.js | 0
.../java/cc/iotkit/test/http/HttpTest.java | 36 ++
hydrovalve-plugin/pom.xml | 64 +++
.../plugins/hydrovalve/Application.java | 22 +
.../hydrovalve/analysis/ModBusAnalysis.java | 23 +
.../hydrovalve/analysis/ModBusConstants.java | 34 ++
.../hydrovalve/analysis/ModBusEntity.java | 42 ++
.../hydrovalve/analysis/ModBusError.java | 50 ++
.../analysis/ModBusRtuAnalysis.java | 171 ++++++
.../plugins/hydrovalve/conf/BeanConfig.java | 37 ++
.../hydrovalve/conf/HydrovalveConfig.java | 21 +
.../hydrovalve/service/FakeThingService.java | 46 ++
.../hydrovalve/service/ModBusDevice.java | 62 +++
.../hydrovalve/service/ModbusPlugin.java | 99 ++++
.../hydrovalve/service/ModbusVerticle.java | 182 +++++++
.../plugins/hydrovalve/utils/ByteUtils.java | 58 ++
.../src/main/resources/application.yml | 8 +
.../src/main/resources/config.json | 23 +
modbus-plugin/pom.xml | 74 +++
.../cc/iotkit/plugins/modbus/Application.java | 21 +
.../plugins/modbus/conf/BeanConfig.java | 36 ++
.../modbus/service/FakeThingService.java | 47 ++
.../plugins/modbus/service/ModbusPlugin.java | 132 +++++
.../src/main/resources/application.yml | 5 +
modbus-plugin/src/main/resources/script.js | 24 +
mqtt-plugin/pom.xml | 85 +++
.../cc/iotkit/plugins/mqtt/Application.java | 19 +
.../iotkit/plugins/mqtt/conf/BeanConfig.java | 28 +
.../iotkit/plugins/mqtt/conf/MqttConfig.java | 31 ++
.../mqtt/service/FakeThingService.java | 70 +++
.../plugins/mqtt/service/MqttDevice.java | 88 +++
.../plugins/mqtt/service/MqttPlugin.java | 94 ++++
.../plugins/mqtt/service/MqttVerticle.java | 443 +++++++++++++++
.../mqtt/service/MyChannelInitializer.java | 14 +
.../src/main/resources/application.yml | 6 +
mqtt-plugin/src/main/resources/config.json | 9 +
mqtt-test-client/pom.xml | 87 +++
.../java/cc/iotkit/test/mqtt/Application.java | 10 +
.../iotkit/test/mqtt/config/BeanConfig.java | 24 +
.../iotkit/test/mqtt/config/MqttConfig.java | 26 +
.../cc/iotkit/test/mqtt/model/Request.java | 26 +
.../cc/iotkit/test/mqtt/model/Response.java | 31 ++
.../test/mqtt/performance/ConnectionTest.java | 69 +++
.../test/mqtt/performance/ReportTest.java | 88 +++
.../cc/iotkit/test/mqtt/service/Device.java | 28 +
.../cc/iotkit/test/mqtt/service/Gateway.java | 164 ++++++
.../test/mqtt/service/MessageHandler.java | 100 ++++
.../iotkit/test/mqtt/service/ReportTask.java | 67 +++
.../cc/iotkit/test/mqtt/service/Vertxs.java | 21 +
.../src/main/resources/application.yml | 8 +
pom.xml | 96 ++++
tcp-plugin/pom.xml | 87 +++
.../cc/iotkit/plugins/tcp/Application.java | 21 +
.../plugins/tcp/cilent/VertxTcpClient.java | 95 ++++
.../iotkit/plugins/tcp/conf/BeanConfig.java | 38 ++
.../plugins/tcp/conf/FakeThingService.java | 47 ++
.../plugins/tcp/conf/TcpServerConfig.java | 34 ++
.../plugins/tcp/parser/DataDecoder.java | 23 +
.../plugins/tcp/parser/DataEncoder.java | 24 +
.../plugins/tcp/parser/DataPackage.java | 61 +++
.../iotkit/plugins/tcp/parser/DataReader.java | 44 ++
.../iotkit/plugins/tcp/server/TcpDevice.java | 88 +++
.../iotkit/plugins/tcp/server/TcpPlugin.java | 97 ++++
.../plugins/tcp/server/TcpServerVerticle.java | 266 +++++++++
tcp-plugin/src/main/resources/application.yml | 7 +
tcp-plugin/src/main/resources/config.json | 16 +
tcp-plugin/src/main/resources/script.js | 33 ++
.../test/java/cc/iotkit/test/ScriptTest.java | 26 +
.../java/cc/iotkit/test/TcpClientTest.java | 21 +
.../cc/iotkit/test/TcpClientVerticle.java | 132 +++++
tcp-plugin/src/test/resources/script.js | 33 ++
websocket-plugin/pom.xml | 74 +++
.../iotkit/plugins/websocket/Application.java | 19 +
.../websocket/analysis/DataAnalysis.java | 38 ++
.../iotkit/plugins/websocket/beans/Event.java | 34 ++
.../plugins/websocket/conf/BeanConfig.java | 28 +
.../websocket/conf/WebsocketConfig.java | 33 ++
.../websocket/service/FakeThingService.java | 53 ++
.../websocket/service/WebsocketDevice.java | 85 +++
.../websocket/service/WebsocketPlugin.java | 89 +++
.../websocket/service/WebsocketVerticle.java | 301 +++++++++++
.../src/main/resources/application.yml | 8 +
.../src/main/resources/config.json | 23 +
144 files changed, 10985 insertions(+)
create mode 100644 .gitee/ISSUE_TEMPLATE.zh-CN.md
create mode 100644 .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
create mode 100644 .gitignore
create mode 100644 DLT645-plugin/pom.xml
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/Application.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Analysis.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Converter.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Data.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645DataFormat.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645FunCode.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645V1997Data.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645V2007Data.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/conf/BeanConfig.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/conf/TcpClientConfig.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/constants/DLT645Constant.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/load/DLT645v1997CsvLoader.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/load/DLT645v2007CsvLoader.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/DLT645Device.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/Dlt645Plugin.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/FakeThingService.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/TcpClientVerticle.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ByteRef.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ByteUtils.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/BytesRef.java
create mode 100644 DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ContainerUtils.java
create mode 100644 DLT645-plugin/src/main/resources/DLT645-1997.csv
create mode 100644 DLT645-plugin/src/main/resources/DLT645-2007.csv
create mode 100644 DLT645-plugin/src/main/resources/application.yml
create mode 100644 DLT645-plugin/src/main/resources/config.json
create mode 100644 LICENSE
create mode 100644 README.en.md
create mode 100644 README.md
create mode 100644 emqx-plugin/pom.xml
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/Application.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/BCDClockGenerator.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/BeanConfig.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/CRC16ModbusUtil.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/DeviceStatusParser.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/EventTypeMapper.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/GasAlarmDataParser.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/IoTConfigProtocol.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/MqttConfig.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/ProtocolTest.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/conf/ResponseParserDemo.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/handler/IMsgHandler.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/service/AuthVerticle.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/service/EmqxPlugin.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/service/FakeThingService.java
create mode 100644 emqx-plugin/src/main/java/cc/iotkit/plugins/emqx/service/MqttDevice.java
create mode 100644 emqx-plugin/src/main/resources/application.yml
create mode 100644 emqx-plugin/src/main/resources/config.json
create mode 100644 http-plugin/pom.xml
create mode 100644 http-plugin/src/main/java/cc/iotkit/plugins/http/Application.java
create mode 100644 http-plugin/src/main/java/cc/iotkit/plugins/http/conf/BeanConfig.java
create mode 100644 http-plugin/src/main/java/cc/iotkit/plugins/http/conf/HttpConfig.java
create mode 100644 http-plugin/src/main/java/cc/iotkit/plugins/http/service/FakeThingService.java
create mode 100644 http-plugin/src/main/java/cc/iotkit/plugins/http/service/HttpDevice.java
create mode 100644 http-plugin/src/main/java/cc/iotkit/plugins/http/service/HttpPlugin.java
create mode 100644 http-plugin/src/main/java/cc/iotkit/plugins/http/service/HttpVerticle.java
create mode 100644 http-plugin/src/main/resources/application.yml
create mode 100644 http-plugin/src/main/resources/config.json
create mode 100644 http-plugin/src/main/resources/script.js
create mode 100644 http-plugin/src/test/java/cc/iotkit/test/http/HttpTest.java
create mode 100644 hydrovalve-plugin/pom.xml
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/Application.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/analysis/ModBusAnalysis.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/analysis/ModBusConstants.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/analysis/ModBusEntity.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/analysis/ModBusError.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/analysis/ModBusRtuAnalysis.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/conf/BeanConfig.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/conf/HydrovalveConfig.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/service/FakeThingService.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/service/ModBusDevice.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/service/ModbusPlugin.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/service/ModbusVerticle.java
create mode 100644 hydrovalve-plugin/src/main/java/cc/iotkit/plugins/hydrovalve/utils/ByteUtils.java
create mode 100644 hydrovalve-plugin/src/main/resources/application.yml
create mode 100644 hydrovalve-plugin/src/main/resources/config.json
create mode 100644 modbus-plugin/pom.xml
create mode 100644 modbus-plugin/src/main/java/cc/iotkit/plugins/modbus/Application.java
create mode 100644 modbus-plugin/src/main/java/cc/iotkit/plugins/modbus/conf/BeanConfig.java
create mode 100644 modbus-plugin/src/main/java/cc/iotkit/plugins/modbus/service/FakeThingService.java
create mode 100644 modbus-plugin/src/main/java/cc/iotkit/plugins/modbus/service/ModbusPlugin.java
create mode 100644 modbus-plugin/src/main/resources/application.yml
create mode 100644 modbus-plugin/src/main/resources/script.js
create mode 100644 mqtt-plugin/pom.xml
create mode 100644 mqtt-plugin/src/main/java/cc/iotkit/plugins/mqtt/Application.java
create mode 100644 mqtt-plugin/src/main/java/cc/iotkit/plugins/mqtt/conf/BeanConfig.java
create mode 100644 mqtt-plugin/src/main/java/cc/iotkit/plugins/mqtt/conf/MqttConfig.java
create mode 100644 mqtt-plugin/src/main/java/cc/iotkit/plugins/mqtt/service/FakeThingService.java
create mode 100644 mqtt-plugin/src/main/java/cc/iotkit/plugins/mqtt/service/MqttDevice.java
create mode 100644 mqtt-plugin/src/main/java/cc/iotkit/plugins/mqtt/service/MqttPlugin.java
create mode 100644 mqtt-plugin/src/main/java/cc/iotkit/plugins/mqtt/service/MqttVerticle.java
create mode 100644 mqtt-plugin/src/main/java/cc/iotkit/plugins/mqtt/service/MyChannelInitializer.java
create mode 100644 mqtt-plugin/src/main/resources/application.yml
create mode 100644 mqtt-plugin/src/main/resources/config.json
create mode 100644 mqtt-test-client/pom.xml
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/Application.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/config/BeanConfig.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/config/MqttConfig.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/model/Request.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/model/Response.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/performance/ConnectionTest.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/performance/ReportTest.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/service/Device.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/service/Gateway.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/service/MessageHandler.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/service/ReportTask.java
create mode 100644 mqtt-test-client/src/main/java/cc/iotkit/test/mqtt/service/Vertxs.java
create mode 100644 mqtt-test-client/src/main/resources/application.yml
create mode 100644 pom.xml
create mode 100644 tcp-plugin/pom.xml
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/Application.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/cilent/VertxTcpClient.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/conf/BeanConfig.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/conf/FakeThingService.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/conf/TcpServerConfig.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/parser/DataDecoder.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/parser/DataEncoder.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/parser/DataPackage.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/parser/DataReader.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/server/TcpDevice.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/server/TcpPlugin.java
create mode 100644 tcp-plugin/src/main/java/cc/iotkit/plugins/tcp/server/TcpServerVerticle.java
create mode 100644 tcp-plugin/src/main/resources/application.yml
create mode 100644 tcp-plugin/src/main/resources/config.json
create mode 100644 tcp-plugin/src/main/resources/script.js
create mode 100644 tcp-plugin/src/test/java/cc/iotkit/test/ScriptTest.java
create mode 100644 tcp-plugin/src/test/java/cc/iotkit/test/TcpClientTest.java
create mode 100644 tcp-plugin/src/test/java/cc/iotkit/test/TcpClientVerticle.java
create mode 100644 tcp-plugin/src/test/resources/script.js
create mode 100644 websocket-plugin/pom.xml
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/Application.java
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/analysis/DataAnalysis.java
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/beans/Event.java
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/conf/BeanConfig.java
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/conf/WebsocketConfig.java
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/service/FakeThingService.java
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/service/WebsocketDevice.java
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/service/WebsocketPlugin.java
create mode 100644 websocket-plugin/src/main/java/cc/iotkit/plugins/websocket/service/WebsocketVerticle.java
create mode 100644 websocket-plugin/src/main/resources/application.yml
create mode 100644 websocket-plugin/src/main/resources/config.json
diff --git a/.gitee/ISSUE_TEMPLATE.zh-CN.md b/.gitee/ISSUE_TEMPLATE.zh-CN.md
new file mode 100644
index 0000000..f09d98d
--- /dev/null
+++ b/.gitee/ISSUE_TEMPLATE.zh-CN.md
@@ -0,0 +1,13 @@
+### 该问题是怎么引起的?
+
+
+
+### 重现步骤
+
+
+
+### 报错信息
+
+
+
+
diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
new file mode 100644
index 0000000..0ed1c31
--- /dev/null
+++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
@@ -0,0 +1,53 @@
+### 一、内容说明(相关的Issue)
+
+
+
+### 二、建议测试周期和提测地址
+ 建议测试完成时间:xxxx.xx.xx
+ 投产上线时间:xxxx.xx.xx
+ 提测地址:CI环境/压测环境
+ 测试账号:
+
+### 三、变更内容
+ * 3.1 关联PR列表
+
+ * 3.2 数据库和部署说明
+ 1. 常规更新
+ 2. 重启unicorn
+ 3. 重启sidekiq
+ 4. 迁移任务:是否有迁移任务,没有写 "无"
+ 5. rake脚本:`bundle exec xxx RAILS_ENV = production`;没有写 "无"
+
+ * 3.4 其他技术优化内容(做了什么,变更了什么)
+ - 重构了 xxxx 代码
+ - xxxx 算法优化
+
+
+ * 3.5 废弃通知(什么字段、方法弃用?)
+
+
+
+ * 3.6 后向不兼容变更(是否有无法向后兼容的变更?)
+
+
+
+### 四、研发自测点(自测哪些?冒烟用例全部自测?)
+ 自测测试结论:
+
+
+### 五、测试关注点(需要提醒QA重点关注的、可能会忽略的地方)
+ 检查点:
+
+| 需求名称 | 是否影响xx公共模块 | 是否需要xx功能 | 需求升级是否依赖其他子产品 |
+|------|------------|----------|---------------|
+| xxx | 否 | 需要 | 不需要 |
+| | | | |
+
+ 接口测试:
+
+ 性能测试:
+
+ 并发测试:
+
+ 其他:
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4e2ac05
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+# Compiled class file
+*.class
+
+# Log file
+log
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+.idea
+target
+*.iml
+data/elasticsearch
+.init
+*.db
+.flattened-pom.xml
+
+.DS_Store
+dependency-reduced-pom.xml
diff --git a/DLT645-plugin/pom.xml b/DLT645-plugin/pom.xml
new file mode 100644
index 0000000..7489642
--- /dev/null
+++ b/DLT645-plugin/pom.xml
@@ -0,0 +1,87 @@
+
+
+
+ iot-iita-plugins
+ cc.iotkit.plugins
+ 2.0.19
+
+ 4.0.0
+
+ DLT645-plugin
+
+
+
+
+ io.vertx
+ vertx-core
+ ${vertx.version}
+
+
+
+ io.netty
+ netty-transport
+
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+
+
+
+
+ dev
+
+ true
+
+
+ dev
+
+
+
+
+ prod
+
+ prod
+
+
+
+
+
+
+
+ com.gitee.starblues
+ spring-brick-maven-packager
+ ${spring-brick.version}
+
+ ${plugin.build.mode}
+
+ DLT645-plugin
+ cc.iotkit.plugins.dlt645.Application
+ ${project.version}
+ iita
+ DLT645示例插件
+ application.yml
+
+
+ jar
+
+
+
+
+
+ repackage
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/Application.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/Application.java
new file mode 100644
index 0000000..1a07d65
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/Application.java
@@ -0,0 +1,21 @@
+package cc.iotkit.plugins.dlt645;
+
+import com.gitee.starblues.bootstrap.SpringPluginBootstrap;
+import com.gitee.starblues.bootstrap.annotation.OneselfConfig;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/14 16:25
+ */
+@SpringBootApplication(scanBasePackages = "cc.iotkit.plugins.dlt645")
+@OneselfConfig(mainConfigFileName = {"application.yml"})
+@EnableConfigurationProperties
+@EnableScheduling
+public class Application extends SpringPluginBootstrap {
+ public static void main(String[] args) {
+ new Application().run(Application.class, args);
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Analysis.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Analysis.java
new file mode 100644
index 0000000..5e10fd7
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Analysis.java
@@ -0,0 +1,323 @@
+package cc.iotkit.plugins.dlt645.analysis;
+
+import cc.iotkit.plugins.dlt645.constants.DLT645Constant;
+import cc.iotkit.plugins.dlt645.load.DLT645v1997CsvLoader;
+import cc.iotkit.plugins.dlt645.load.DLT645v2007CsvLoader;
+import cc.iotkit.plugins.dlt645.utils.ByteRef;
+import cc.iotkit.plugins.dlt645.utils.BytesRef;
+import cc.iotkit.plugins.dlt645.utils.ContainerUtils;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:51
+ * DL/T 645-1997 通讯规约通信规约
+ * 帧起始符 地址域 帧起始符 控制码 数据长度域 数据域 校验码 结束符
+ * 1 6 1 1 1 N 1 1
+ */
+public class DLT645Analysis {
+ /**
+ * 静态实例
+ */
+ private static final DLT645Analysis DLT645Analysis = new DLT645Analysis();
+
+ public static DLT645Analysis inst() {
+ return DLT645Analysis;
+ }
+ /**
+ * 设备地址:4字节的byte[]
+ */
+ public static final String ADR = "ADR";
+ /**
+ * 功能码:1字节的byte
+ */
+ public static final String FUN = "FUN";
+ /**
+ * 数据报:不定长的byte[]
+ */
+ public static final String DAT = "DAT";
+
+ /**
+ * 索引表
+ */
+ private Map name2entity;
+ public static Map din2entity;
+
+
+ /**
+ * 验证码
+ *
+ * @param arrCmd
+ * @param iOffSet
+ * @return
+ */
+ private static int GetVfy(byte[] arrCmd, int iOffSet) {
+ int iSize = arrCmd.length - 2 - iOffSet;
+ if (iSize < 0) {
+ return 0;
+ }
+
+ int bySum = 0x00;
+
+ int index = iOffSet;
+ for (int i = 0; i < iSize; i++) {
+ bySum += arrCmd[index++];
+ bySum &= 0xff;
+ }
+
+ return bySum & 0xff;
+ }
+
+ /**
+ * 打包
+ *
+ * @param arrAddr 6字节的地址码
+ * @param byCmd 命令字
+ * @param arrData 数据段
+ * @return 是否成功
+ */
+ public static byte[] packCmd(byte[] arrAddr, byte byCmd, byte[] arrData) {
+ // 检查:数据块的大小
+ int iDataSize = arrData.length;
+ if (iDataSize > 255) {
+ return new byte[0];
+ }
+ if (arrAddr.length != 6) {
+ return new byte[0];
+ }
+
+ // 初始化数组大小
+ byte[] arrCmd = new byte[iDataSize + 13];
+
+
+ int index = 0;
+
+
+ // 前导字符(在发送帧信息之前,先发送1个或多个字节FEH,以唤醒接收方)
+ arrCmd[index++] = (byte) 0xFE;
+
+ // 帧起始符
+ arrCmd[index++] = (byte) 0x68;
+
+ // 地址码
+ System.arraycopy(arrAddr, 0, arrCmd, index, arrAddr.length);
+ index += arrAddr.length;
+
+ // 帧起始符
+ arrCmd[index++] = (byte) 0x68;
+
+ // 控制码
+ arrCmd[index++] = byCmd;
+
+ // 帧长度
+ arrCmd[index++] = (byte) iDataSize;
+
+ // 数据域
+ System.arraycopy(arrData, 0, arrCmd, index, iDataSize);
+ // 每个字节加上0x33
+ for (int i = 0; i < arrData.length; i++) {
+ arrCmd[index + i] = (byte) ((arrCmd[index + i] & 0xff) + 0x33);
+ }
+ index += iDataSize;
+
+
+ // 校验码
+ arrCmd[index++] = (byte) GetVfy(arrCmd, 1);
+
+ // 结束符
+ arrCmd[index++] = 0x16;
+
+ return arrCmd;
+ }
+
+ /**
+ * 默认打包
+ *
+ * @param byCmd
+ * @param arrData
+ * @return
+ */
+ public static byte[] packCmd(byte byCmd, byte[] arrData) {
+ byte[] arrAddr = new byte[6];
+
+ arrAddr[0] = 0x01;
+ arrAddr[1] = 0x00;
+ arrAddr[2] = 0x00;
+ arrAddr[3] = 0x00;
+ arrAddr[4] = 0x00;
+ arrAddr[5] = 0x00;
+
+ return packCmd(arrAddr, byCmd, arrData);
+ }
+
+ /**
+ * 解包
+ *
+ * @param arrCmd 报文,前面有不确定的唤醒字符
+ * @param arrAddrRef 地址码
+ * @param byCmd 命令字
+ * @param arrDataRef 数据
+ * @return 是否成功
+ */
+ private static boolean unPackCmd2Map(byte[] arrCmd, BytesRef arrAddrRef, ByteRef byCmd, BytesRef arrDataRef) {
+ int iSize = arrCmd.length;
+
+ // 查找偏移量:DLT645电表前面会被塞入不定长的乱码数据,被用来激活电表,直到0x68字符出现
+ int iOffSet = 0;
+ int index = 0;
+ for (iOffSet = 0; iOffSet < iSize; iOffSet++) {
+ if ((arrCmd[index++] & 0xff) == 0x68) {
+ break;
+ }
+ }
+ if (iOffSet == iSize) {
+ return false;
+ }
+
+ // 检查:数据包大小
+ if (iSize < 12 + iOffSet) {
+ return false;
+ }
+
+//==============================================================================
+// 中国电力总局的DL/T 645-1997 多功能电能表通信规约
+// 引导码 起始符 地址码 起始符 功能码 帧长度 数据域 校验和 结束符
+// N 1 6 1 1 1 N 1 1
+//==============================================================================
+
+ // 检查:起始符1
+ if (arrCmd[iOffSet + 0] != 0x68) {
+ return false;
+ }
+ // 检查:起始符2
+ if (arrCmd[iOffSet + 7] != 0x68) {
+ return false;
+ }
+ // 检查:结束符
+ if (arrCmd[iSize - 1] != 0x16) {
+ return false;
+ }
+
+ // 地址码
+ byte[] arrAddr = new byte[6];
+ System.arraycopy(arrCmd, iOffSet + 1, arrAddr, 0, 6);
+ arrAddrRef.setValue(arrAddr);
+
+
+ // 功能码
+ byCmd.setValue(arrCmd[iOffSet + 8]);
+
+
+ // 检查:帧长度
+ int iDataSize = arrCmd[iOffSet + 9];
+ if ((iDataSize + 12 + iOffSet) != iSize) {
+ return false;
+ }
+
+ // 数据域
+ byte[] arrData = new byte[iDataSize];
+ System.arraycopy(arrCmd, iOffSet + 10, arrData, 0, iDataSize);
+ // 每个字节先减去0x33
+ for (int i = 0; i < arrData.length; i++) {
+ arrData[i] = (byte) ((arrData[i] & 0xff) - 0x33);
+ }
+ arrDataRef.setValue(arrData);
+
+
+ // 检查:校验码
+ byte byVfyOK = (byte) (GetVfy(arrCmd, iOffSet) & 0xff);
+ return byVfyOK == arrCmd[iSize - 2];
+ }
+
+ public static boolean unPackCmd2Map(byte[] arrCmd, ByteRef byCmd, BytesRef arrData) {
+ BytesRef arrAddr = new BytesRef();
+ return unPackCmd2Map(arrCmd, arrAddr, byCmd, arrData);
+ }
+
+ /**
+ * 只有数据标识的DI0和DI1的请求命令
+ *
+ * @param DI0 数据标识
+ * @param DI1 数据标识
+ * @return
+ */
+ public static byte[] packCmdGetData(int DI0, int DI1) {
+ byte[] arrData = new byte[2];
+ arrData[0] = (byte) DI0;
+ arrData[1] = (byte) DI1;
+
+ return packCmd((byte) 0x01, arrData);
+ }
+
+ public static boolean unPackCmdGetData(byte[] arrCmd, BytesRef arrData) {
+ ByteRef byCmd = new ByteRef();
+ if (!unPackCmd2Map(arrCmd, byCmd, arrData)) {
+ return false;
+ }
+
+ return byCmd.getValue() == 0x81;
+ }
+
+ /**
+ * 包装成另一种格式
+ *
+ * @param arrCmd
+ * @return
+ */
+ public static Map unPackCmd2Map(byte[] arrCmd) {
+ ByteRef byFun = new ByteRef();
+ BytesRef byAddr = new BytesRef();
+ BytesRef arrData = new BytesRef();
+ if (!unPackCmd2Map(arrCmd, byAddr, byFun, arrData)) {
+ return Collections.emptyMap();
+ }
+
+ Map value = new HashMap<>();
+ value.put(ADR, byAddr.getValue());
+ value.put(FUN, byFun.getValue());
+ value.put(DAT, arrData.getValue());
+ return value;
+ }
+
+ /**
+ * 获得对象信息
+ *
+ * @return 对象副本
+ */
+ public synchronized Map getTemplateByName() {
+ if (this.name2entity == null) {
+ DLT645v1997CsvLoader loader = new DLT645v1997CsvLoader();
+ List entityList = loader.loadCsvFile();
+ Map nameMap = ContainerUtils.buildMapByKey(entityList, DLT645V1997Data::getName);
+ this.name2entity = new ConcurrentHashMap<>();
+ this.name2entity.putAll(nameMap);
+ }
+
+ return this.name2entity;
+ }
+
+ public synchronized Map getTemplateByDIn(String ver) {
+ if (this.din2entity == null) {
+ if(DLT645Constant.PRO_VER_1997.equals(ver)){
+ DLT645v1997CsvLoader loader = new DLT645v1997CsvLoader();
+ List entityList = loader.loadCsvFile();
+ Map keyMap = ContainerUtils.buildMapByKey(entityList, DLT645V1997Data::getKey);
+ this.din2entity = new ConcurrentHashMap<>();
+ this.din2entity.putAll(keyMap);
+ }else{
+ DLT645v2007CsvLoader loader = new DLT645v2007CsvLoader();
+ List entityList = loader.loadCsvFile();
+ Map keyMap = ContainerUtils.buildMapByKey(entityList, DLT645V2007Data::getKey);
+ this.din2entity = new ConcurrentHashMap<>();
+ this.din2entity.putAll(keyMap);
+ }
+ }
+
+ return this.din2entity;
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Converter.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Converter.java
new file mode 100644
index 0000000..dd98e76
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Converter.java
@@ -0,0 +1,44 @@
+package cc.iotkit.plugins.dlt645.analysis;
+
+import cc.iotkit.plugins.dlt645.constants.DLT645Constant;
+import cc.iotkit.plugins.dlt645.utils.ByteUtils;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:54
+ */
+@Slf4j
+@Data
+public class DLT645Converter {
+
+ public static String packData(String deviceAddress,String funCode,String dataIdentifier){
+ // 对设备地址进行编码
+ byte[] tmp = ByteUtils.hexStringToByteArray(deviceAddress);
+ byte[] adrr = new byte[6];
+ ByteUtils.byteInvertedOrder(tmp,adrr);
+
+ // 根据对象名获取对象格式信息,这个格式信息,记录在CSV文件中
+ DLT645Data dataEntity = DLT645Analysis.inst().getTemplateByDIn(DLT645Constant.PRO_VER_2007).get(dataIdentifier);
+ if (dataEntity == null) {
+ log.info("CSV模板文件中未定义对象:" + dataIdentifier + " ,你需要在模板中添加该对象信息");
+ }
+ byte byFun = Byte.decode(String.valueOf(DLT645FunCode.getCode(funCode,DLT645Constant.PRO_VER_2007)));
+
+ // 使用DLT645协议框架编码
+ byte[] pack = DLT645Analysis.packCmd(adrr,byFun,dataEntity.getDIn());
+
+ // 将报文按要求的16进制格式的String对象返回
+ return ByteUtils.byteArrayToHexString(pack,false);
+ }
+ @Data
+ public static class ReportData{
+ private String type;
+ private String identifier;
+ private Long occur;
+ private Long time;
+ private Object data;
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Data.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Data.java
new file mode 100644
index 0000000..44b5063
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645Data.java
@@ -0,0 +1,92 @@
+package cc.iotkit.plugins.dlt645.analysis;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Map;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:55
+ */
+@Slf4j
+@Data
+public abstract class DLT645Data {
+ /**
+ * 名称
+ */
+ private String name;
+ /**
+ * 格式
+ */
+ private DLT645DataFormat format = new DLT645DataFormat();
+ /**
+ * 长度
+ */
+ private int length;
+ /**
+ * 单位
+ */
+ private String unit;
+ /**
+ * 可读
+ */
+ private boolean read;
+ /**
+ * 可写
+ */
+ private boolean write;
+ /**
+ * 数值
+ */
+ private Object value = 0.0;
+
+ /**
+ * 数值
+ */
+ private Object value2nd;
+
+ public abstract String getKey();
+
+ public abstract byte[] getDIn();
+
+ public abstract void setDIn(byte[] value);
+
+ public abstract int getDInLen();
+
+ public String toString() {
+ if (this.value2nd == null) {
+ return this.name + ":" + this.value + this.unit;
+ }
+
+ return this.name + ":" + this.value + this.unit + " " + this.value2nd;
+ }
+
+ public void decodeValue(byte[] data, Map dinMap) {
+
+ // DI值
+ this.setDIn(data);
+
+ // 获取字典信息
+ DLT645Data dict = dinMap.get(this.getKey());
+ if (dict == null) {
+ log.info("DIn info err,please configure:" + this.getKey());
+ }
+
+ this.format = dict.format;
+ this.name = dict.name;
+ this.read = dict.read;
+ this.write = dict.write;
+ this.length = dict.length;
+ this.unit = dict.unit;
+
+
+ // 基本值
+ this.value = this.format.decodeValue(data, this.format.getFormat(), this.getDInLen(), this.format.getLength());
+
+ // 组合值
+ if (this.format.getFormat2nd() != null && !this.format.getFormat2nd().isEmpty()) {
+ this.value2nd = this.format.decodeValue(data, this.format.getFormat2nd(), this.getDInLen() + this.format.getLength(), this.format.getLength2nd());
+ }
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645DataFormat.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645DataFormat.java
new file mode 100644
index 0000000..3715947
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645DataFormat.java
@@ -0,0 +1,361 @@
+package cc.iotkit.plugins.dlt645.analysis;
+
+import cc.iotkit.plugins.dlt645.utils.ByteUtils;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:56
+ */
+@Slf4j
+@Data
+public class DLT645DataFormat {
+ // 数值格式
+ public static final String FORMAT_X = "X";
+ public static final String FORMAT_N = "N";
+
+ // 时间格式
+ public static final String FORMAT_YYMMDDWW = "YYMMDDWW";
+ public static final String FORMAT_hhmmss = "hhmmss";
+ public static final String FORMAT_YYMMDDhhmm = "YYMMDDhhmm";
+ public static final String FORMAT_MMDDhhmm = "MMDDhhmm";
+ public static final String FORMAT_DDhh = "DDhh";
+ public static final String FORMAT_hhmm = "hhmm";
+ public static final String FORMAT_mmmm = "mmmm";
+
+ // 编码格式
+ public static final String FORMAT_NN___NN = "NN...NN";
+ public static final String FORMAT_XX___XX = "XX...XX";
+
+ // 状态格式
+ public static final String FORMAT_STATUS_WEEK = "周休日状态字";
+ public static final String FORMAT_STATUS_METER = "电表运行状态字";
+ public static final String FORMAT_STATUS_NETWORK = "电网状态字";
+
+
+ /**
+ * 格式类型
+ */
+ private String format = "";
+ /**
+ * 组合格式:第二个格式
+ */
+ private String format2nd = "";
+ /**
+ * 长度
+ */
+ private int length = 0;
+ /**
+ * 组合格式:第二个长度
+ */
+ private int length2nd = 0;
+ /**
+ * 缩小比例
+ */
+ private double ratio = 1.0;
+
+ public Object decodeValue(byte[] data, String format, int start, int length) throws RuntimeException {
+ // 前面4个字节是DI0~DI3
+ if (data.length < length + start) {
+ log.info("DATA LENGTH ERROR");
+ }
+
+ // 各种XX.XX格式
+ if (format.equals(FORMAT_X)) {
+ return this.getValue(data, start, length, this.ratio);
+ }
+ // 各种NN.NN格式
+ if (format.equals(FORMAT_N)) {
+ return this.getValue(data, start, length, this.ratio);
+ }
+ if (format.equals(FORMAT_NN___NN)) {
+ return this.getString(data, start, length);
+ }
+
+ // 时间格式
+ if (format.equals(FORMAT_hhmm) || format.equals(FORMAT_DDhh) || format.equals(FORMAT_YYMMDDWW) || format.equals(FORMAT_hhmmss) || format.equals(FORMAT_YYMMDDhhmm) || format.equals(FORMAT_MMDDhhmm)) {
+ return this.getDataTime(data, format, start, length);
+ }
+
+
+ if (format.equals(FORMAT_XX___XX)) {
+ this.format = FORMAT_XX___XX;
+ this.ratio = 1.0;
+ return true;
+ }
+
+
+ return false;
+ }
+
+ /**
+ * 解码格式:固定长度格式和可变长度格式
+ * 固定长度格式:根据XX.XX它格式本身长度进行判定
+ *
+ * @param format 格式名称
+ * @param length 可变格式的长度
+ * @return 是否成功
+ */
+ public boolean decodeFormat(String format, int length) {
+ // 统计字符种类的数量
+ Map charCount = charCount(format);
+
+ // 组合格式:XX.XXXX|YYMMDDhhmm
+ if (charCount.containsKey('|') && charCount.get('|').equals(1)) {
+ String format1 = format.substring(0, format.indexOf("|"));
+ String format2 = format.substring(format.indexOf("|") + 1);
+ this.decodeFormat(format2, -1);
+ this.format2nd = this.format;
+ this.length2nd = this.length;
+ this.decodeFormat(format1, -1);
+ return true;
+ }
+
+ // 各种XX.XX格式
+ if (charCount.containsKey('X') && charCount.containsKey('.') && charCount.get('.').equals(1)) {
+ this.format = FORMAT_X;
+ int point = format.length() - format.indexOf(".") - 1;
+ for (int i = 0; i < point; i++) {
+ this.ratio *= 10.0;
+ }
+ this.length = (format.length() - 1) / 2;
+ return true;
+ }
+ // XXXX格式
+ if (charCount.containsKey('X') && charCount.size() == 1) {
+ this.format = FORMAT_X;
+ this.ratio = 1.0;
+ this.length = length;
+ return true;
+ }
+ // 各种NN.NN格式
+ if (charCount.containsKey('N') && charCount.containsKey('.') && charCount.get('.').equals(1)) {
+ this.format = FORMAT_N;
+ int point = format.length() - format.indexOf(".") - 1;
+ for (int i = 0; i < point; i++) {
+ this.ratio *= 10.0;
+ }
+ this.length = (format.length() - 1) / 2;
+ return true;
+ }
+ // NNN格式
+ if (charCount.containsKey('N') && charCount.size() == 1) {
+ this.format = FORMAT_N;
+ this.ratio = 1.0;
+ this.length = length;
+ return true;
+ }
+
+
+ // 固定长度
+ if (this.isFixedLength(format)) {
+ this.format = format;
+ this.ratio = 1.0;
+ this.length = format.length() / 2;
+ return true;
+ }
+
+ // 可变长度
+ if (this.isVariableLength(format)) {
+ this.format = format;
+ this.ratio = 1.0;
+ this.length = length;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 是否为固定长度:它的长度是直接通过格式就能确定
+ *
+ * @param format
+ * @return
+ */
+ private boolean isFixedLength(String format) {
+ if (format.equalsIgnoreCase(FORMAT_hhmm)) {
+ return true;
+ }
+ if (format.equalsIgnoreCase(FORMAT_DDhh)) {
+ return true;
+ }
+ if (format.equalsIgnoreCase(FORMAT_MMDDhhmm)) {
+ return true;
+ }
+ if (format.equalsIgnoreCase(FORMAT_YYMMDDhhmm)) {
+ return true;
+ }
+ if (format.equalsIgnoreCase(FORMAT_hhmmss)) {
+ return true;
+ }
+ if (format.equalsIgnoreCase(FORMAT_mmmm)) {
+ return true;
+ }
+
+ return format.equalsIgnoreCase(FORMAT_YYMMDDWW);
+ }
+
+ /**
+ * 是否为可变长度:它的长度是通过用户在CSV文件中告知
+ *
+ * @param format
+ * @return
+ */
+ private boolean isVariableLength(String format) {
+ if (format.equalsIgnoreCase(FORMAT_NN___NN)) {
+ return true;
+ }
+ if (format.equalsIgnoreCase(FORMAT_XX___XX)) {
+ return true;
+ }
+ if (format.equalsIgnoreCase(FORMAT_STATUS_METER)) {
+ return true;
+ }
+ if (format.equalsIgnoreCase(FORMAT_STATUS_NETWORK)) {
+ return true;
+ }
+
+ return format.equalsIgnoreCase(FORMAT_STATUS_WEEK);
+ }
+
+ /**
+ * 统计字符数量,方面后面判定格式
+ *
+ * @param format DLT645规约中定义的XXX.XXX之类的各种数据格式文本
+ * @return 各字符数量
+ */
+ private Map charCount(String format) {
+
+ Map charSet = new HashMap<>();
+ for (int i = 0; i < format.length(); i++) {
+ Character ch = format.charAt(i);
+ Integer count = charSet.get(ch);
+ if (count == null) {
+ count = 0;
+ }
+
+ count++;
+ charSet.put(ch, count);
+ }
+
+ return charSet;
+ }
+
+ /**
+ * 4字节长度的double型数值
+ *
+ * @param data data数组
+ * @param start 数据在数组中的起始位置
+ * @param ratio 倍率,比如缩小100倍数,那么填0.01
+ * @return 返回值
+ */
+ private Object getValue(byte[] data, int start, int length, double ratio) {
+ long sum = 0;
+ double rd = 1.0;
+ for (int i = 0; i < length; i++) {
+ long l = data[start + i] & 0x0f;
+ long h = (data[start + i] & 0xf0) >> 4;
+
+ l = (long) (l * rd);
+ sum += l;
+ rd = rd * 10.0;
+
+
+ h = (long) (h * rd);
+ sum += h;
+ rd = rd * 10.0;
+
+ }
+
+ if (ratio < 1.1 && ratio > 0.0) {
+ // 如果ratio==1
+ return sum;
+ } else {
+ return sum / ratio;
+ }
+ }
+
+ /**
+ * 日期格式的解码
+ *
+ * @param data data数组
+ * @param format 日期格式
+ * @param start 数据在数组中的起始位置
+ * @param length 格式长度
+ * @return 返回值
+ */
+ private String getDataTime(byte[] data, String format, int start, int length) {
+ // 拆解成个位数列表
+ List list = new ArrayList<>();
+ for (int i = 0; i < length; i++) {
+ int l = data[start + i] & 0x0f;
+ int h = (data[start + i] & 0xf0) >> 4;
+
+ list.add(Integer.toString(l));
+ list.add(Integer.toString(h));
+ }
+
+ // 格式1
+ if (format.equals(FORMAT_YYMMDDhhmm)) {
+ String result = "20" + list.get(9) + list.get(8) + "年" + list.get(7) + list.get(6) + "月" + list.get(5) + list.get(4) + "日";
+ result += " " + list.get(3) + list.get(2) + "点" + list.get(1) + list.get(0) + "分";
+ return result;
+ }
+
+ if (format.equals(FORMAT_YYMMDDWW)) {
+ String result = "20" + list.get(7) + list.get(6) + "年" + list.get(5) + list.get(4) + "月" + list.get(3) + list.get(2) + "日";
+ result += " 星期:" + list.get(1) + list.get(0);
+ return result;
+ }
+ if (format.equals(FORMAT_hhmmss)) {
+ return list.get(5) + list.get(4) + "点" + list.get(3) + list.get(2) + "分" + list.get(1) + list.get(0) + "秒";
+ }
+ if (format.equals(FORMAT_mmmm)) {
+ return list.get(3) + list.get(2) + list.get(1) + list.get(0) + "分";
+ }
+
+
+ if (format.equals(FORMAT_MMDDhhmm)) {
+ String result = list.get(7) + list.get(6) + "月" + list.get(5) + list.get(4) + "日 ";
+ result += list.get(3) + list.get(2) + "点" + list.get(1) + list.get(0) + "分";
+ return result;
+ }
+ if (format.equals(FORMAT_DDhh)) {
+ return list.get(3) + list.get(2) + "号 " + list.get(1) + list.get(0) + "点";
+ }
+ if (format.equals(FORMAT_hhmm)) {
+ return list.get(3) + list.get(2) + "点 " + list.get(1) + list.get(0) + "分";
+ }
+
+
+ return "";
+ }
+
+ private byte encodeBCD(byte a) {
+ return (byte) ((a / 10) * 16 + (a % 10));
+ }
+
+ private byte decodeBCD(byte a) {
+ return (byte) ((a / 16) * 10 + (a % 16));
+ }
+
+ private Object getString(byte[] data, int start, int length) {
+ byte[] tmp = new byte[length];
+
+ for (int i = 0; i < length; i++) {
+ tmp[i] = data[start + i];
+ }
+ for (int i = 0; i < length / 2; i++) {
+ byte by = tmp[i];
+ tmp[i] = tmp[length - i - 1];
+ tmp[length - i - 1] = by;
+ }
+ return ByteUtils.byteArrayToHexString(tmp, true).replace(" ", "");
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645FunCode.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645FunCode.java
new file mode 100644
index 0000000..825998c
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645FunCode.java
@@ -0,0 +1,302 @@
+package cc.iotkit.plugins.dlt645.analysis;
+
+import cc.iotkit.plugins.dlt645.constants.DLT645Constant;
+import lombok.Data;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:58
+ */
+@Data
+public class DLT645FunCode {
+
+ /* 2007 */
+ private static final String func_v07_00000 = "保留";
+ private static final String func_v07_01000 = "广播校时";
+ private static final String func_v07_10001 = "读数据";
+ private static final String func_v07_10010 = "读后续数据";
+ private static final String func_v07_10011 = "读通信地址";
+ private static final String func_v07_10100 = "写数据";
+ private static final String func_v07_10101 = "写通信地址";
+ private static final String func_v07_10110 = "冻结";
+ private static final String func_v07_10111 = "更改通信速率";
+ private static final String func_v07_11000 = "修改密码";
+ private static final String func_v07_11001 = "最大需量清零";
+ private static final String func_v07_11010 = "电表清零";
+ private static final String func_v07_11011 = "事件清零";
+
+ /* 1997 */
+ public static final String func_v97_00000 = "保留";
+ public static final String func_v97_00001 = "读数据";
+ public static final String func_v97_00010 = "读后续数据";
+ public static final String func_v97_00011 = "重读数据";
+ public static final String func_v97_00100 = "写数据";
+ public static final String func_v97_01000 = "广播校时";
+ public static final String func_v97_01010 = "写设备地址";
+ public static final String func_v97_01100 = "更改通信速率";
+ public static final String func_v97_01111 = "修改密码";
+ public static final String func_v97_10000 = "最大需量清零";
+ /**
+ * 方向:主站发出=false,从站应答=true
+ */
+ private boolean direct = false;
+ /**
+ * 从站是否异常应答
+ */
+ private boolean error = false;
+ /**
+ * 功能代码
+ */
+ private byte code = 0;
+ /**
+ * 是否最后的尾部
+ */
+ private boolean next = false;
+
+ public static DLT645FunCode decodeEntity(byte func) {
+ DLT645FunCode dlt645FunCode = new DLT645FunCode();
+ dlt645FunCode.decode(func);
+ return dlt645FunCode;
+ }
+
+ public static int getCodev2007(String text) {
+ if (func_v07_00000.equals(text)) {
+ return 0b00000;
+ }
+ if (func_v07_01000.equals(text)) {
+ return 0b01000;
+ }
+ if (func_v07_10001.equals(text)) {
+ return 0b10001;
+ }
+ if (func_v07_10010.equals(text)) {
+ return 0b10010;
+ }
+ if (func_v07_10011.equals(text)) {
+ return 0b10011;
+ }
+ if (func_v07_10100.equals(text)) {
+ return 0b10100;
+ }
+ if (func_v07_10101.equals(text)) {
+ return 0b10101;
+ }
+ if (func_v07_10110.equals(text)) {
+ return 0b10110;
+ }
+ if (func_v07_10111.equals(text)) {
+ return 0b10111;
+ }
+ if (func_v07_11000.equals(text)) {
+ return 0b11000;
+ }
+ if (func_v07_11001.equals(text)) {
+ return 0b11001;
+ }
+ if (func_v07_11010.equals(text)) {
+ return 0b11010;
+ }
+ if (func_v07_11011.equals(text)) {
+ return 0b11011;
+ }
+ return 0b00000;
+ }
+
+ public static int getCodev1997(String text) {
+ if (func_v97_00000.equals(text)) {//
+ return 0b00000;
+ }
+ if (func_v97_00001.equals(text)) {
+ return 0b00001;
+ }
+ if (func_v97_00010.equals(text)) {
+ return 0b00010;
+ }
+ if (func_v97_00011.equals(text)) {
+ return 0b00011;
+ }
+ if (func_v97_00100.equals(text)) {
+ return 0b00100;
+ }
+ if (func_v97_01000.equals(text)) {
+ return 0b01000;
+ }
+ if (func_v97_01010.equals(text)) {
+ return 0b01010;
+ }
+ if (func_v97_01100.equals(text)) {
+ return 0b01100;
+ }
+ if (func_v97_01111.equals(text)) {
+ return 0b01111;
+ }
+ if (func_v97_10000.equals(text)) {
+ return 0b10000;
+ }
+ return 0b00000;
+ }
+
+ /**
+ * 编码
+ *
+ * @return 功能码
+ */
+ public byte encode() {
+ int func = 0;
+ if (this.direct) {
+ func |= 0x80;
+ }
+ if (this.error) {
+ func |= 0x40;
+ }
+ if (this.next) {
+ func |= 0x20;
+ }
+ func |= this.code & 0x1F;
+
+ return (byte) func;
+ }
+
+ /**
+ * 生成功能码
+ *
+ * @param dlt645FunCode
+ * @return
+ */
+ public byte encodeFunCode(DLT645FunCode dlt645FunCode) {
+ return dlt645FunCode.encode();
+ }
+
+ /**
+ * 解码
+ *
+ * @param func
+ */
+ public void decode(byte func) {
+ this.direct = (func & 0x80) > 0;
+ this.error = (func & 0x40) > 0;
+ this.next = (func & 0x20) > 0;
+ this.code = (byte) (func & 0x1F);
+ }
+
+ public static int getCode(String text,String ver){
+ if(DLT645Constant.PRO_VER_1997.equals(ver)){
+ return getCodev1997(text);
+ }else{
+ return getCodev2007(text);
+ }
+ }
+
+ public String getCodeTextV1997() {
+ if (this.code == 0b00000) {
+ return func_v97_00000;
+ }
+ if (this.code == 0b01000) {
+ return func_v97_01000;
+ }
+ if (this.code == 0b00001) {
+ return func_v97_00001;
+ }
+ if (this.code == 0b00010) {
+ return func_v97_00010;
+ }
+ if (this.code == 0b00100) {
+ return func_v97_00100;
+ }
+ if (this.code == 0b01010) {
+ return func_v97_01010;
+ }
+ if (this.code == 0b01100) {
+ return func_v97_01100;
+ }
+ if (this.code == 0b01111) {
+ return func_v97_01111;
+ }
+ if (this.code == 0b10000) {
+ return func_v97_10000;
+ }
+
+ return "";
+ }
+
+ public String getCodeTextV2007() {
+ if (this.code == 0b00000) {
+ return func_v07_00000;
+ }
+ if (this.code == 0b01000) {
+ return func_v07_01000;
+ }
+ if (this.code == 0b10001) {
+ return func_v07_10001;
+ }
+ if (this.code == 0b10010) {
+ return func_v07_10010;
+ }
+ if (this.code == 0b10011) {
+ return func_v07_10011;
+ }
+ if (this.code == 0b10100) {
+ return func_v07_10100;
+ }
+ if (this.code == 0b10101) {
+ return func_v07_10101;
+ }
+ if (this.code == 0b10110) {
+ return func_v07_10110;
+ }
+ if (this.code == 0b10111) {
+ return func_v07_10111;
+ }
+ if (this.code == 0b11000) {
+ return func_v07_11000;
+ }
+ if (this.code == 0b11001) {
+ return func_v07_11001;
+ }
+ if (this.code == 0b11010) {
+ return func_v07_11010;
+ }
+ if (this.code == 0b11011) {
+ return func_v07_11011;
+ }
+
+ return "";
+ }
+
+ /**
+ * 获取文本描述
+ *
+ * @return 文本描述
+ */
+ public String getMessage(String ver) {
+ String message = "";
+ if (this.direct) {
+ message += "从站发出:";
+ } else {
+ message += "主站发出:";
+ }
+
+ if (ver.equalsIgnoreCase(DLT645Constant.PRO_VER_1997)) {
+ message += this.getCodeTextV1997();
+ }
+ if (ver.equalsIgnoreCase(DLT645Constant.PRO_VER_2007)) {
+ message += this.getCodeTextV2007();
+ }
+ message += this.getCodeTextV1997();
+
+ if (this.error) {
+ message += ":异常";
+ } else {
+ message += ":正常";
+ }
+
+
+ if (this.next) {
+ message += ":还有后续帧";
+ } else {
+ message += ":这是末尾帧";
+ }
+
+ return message;
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645V1997Data.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645V1997Data.java
new file mode 100644
index 0000000..931496c
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645V1997Data.java
@@ -0,0 +1,63 @@
+package cc.iotkit.plugins.dlt645.analysis;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:58
+ */
+@Slf4j
+@Setter
+@Getter
+public class DLT645V1997Data extends DLT645Data {
+ /**
+ * DI1/DI0
+ */
+ private byte di0l = 0;
+ private byte di0h = 0;
+ private byte di1l = 0;
+ private byte di1h = 0;
+
+ @Override
+ public String getKey() {
+ String key = "";
+ key += Integer.toString(this.di1h, 16);
+ key += Integer.toString(this.di1l, 16);
+ key += Integer.toString(this.di0h, 16);
+ key += Integer.toString(this.di0l, 16);
+ return key.toUpperCase();
+ }
+
+ @Override
+ public byte[] getDIn() {
+ byte[] value = new byte[2];
+ value[0] = (byte) (this.di0l + (this.di0h << 4));
+ value[1] = (byte) (this.di1l + (this.di1h << 4));
+ return value;
+ }
+
+ @Override
+ public void setDIn(byte[] value) {
+ if (value.length < 2) {
+ log.info("DATA LENGTH ERROR");
+ }
+
+ // DI值
+ this.di1h = (byte) ((value[1] & 0xf0) >> 4);
+ this.di1l = (byte) (value[1] & 0x0f);
+ this.di0h = (byte) ((value[0] & 0xf0) >> 4);
+ this.di0l = (byte) (value[0] & 0x0f);
+ }
+
+ /**
+ * 1997版的DIn2字节
+ *
+ * @return
+ */
+ @Override
+ public int getDInLen() {
+ return 2;
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645V2007Data.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645V2007Data.java
new file mode 100644
index 0000000..3ad35eb
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/analysis/DLT645V2007Data.java
@@ -0,0 +1,66 @@
+package cc.iotkit.plugins.dlt645.analysis;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:58
+ */
+@Slf4j
+@Setter
+@Getter
+public class DLT645V2007Data extends DLT645Data {
+ /**
+ * DI1/DI0
+ */
+ private byte di0 = 0;
+ private byte di1 = 0;
+ private byte di2 = 0;
+ private byte di3 = 0;
+
+
+ public String getKey() {
+ String key = "";
+ key += StringUtils.leftPad(Integer.toHexString(this.di3 & 0xFF),2,"0");
+ key += StringUtils.leftPad(Integer.toHexString(this.di2 & 0xFF),2,"0");
+ key += StringUtils.leftPad(Integer.toHexString(this.di1 & 0xFF),2,"0");
+ key += StringUtils.leftPad(Integer.toHexString(this.di0 & 0xFF),2,"0") + "";
+ return key.toUpperCase();
+ }
+
+ @Override
+ public byte[] getDIn() {
+ byte[] value = new byte[4];
+ value[0] = this.di0;
+ value[1] = this.di1;
+ value[2] = this.di2;
+ value[3] = this.di3;
+
+ return value;
+ }
+
+ @Override
+ public void setDIn(byte[] value) {
+ if (value.length < 4) {
+ log.info("DATA LENGTH ERROR");
+ }
+
+ this.di0 = value[0];
+ this.di1 = value[1];
+ this.di2 = value[2];
+ this.di3 = value[3];
+ }
+
+ /**
+ * 2007版的DIn 4字节
+ *
+ * @return
+ */
+ @Override
+ public int getDInLen() {
+ return 4;
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/conf/BeanConfig.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/conf/BeanConfig.java
new file mode 100644
index 0000000..9ff3013
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/conf/BeanConfig.java
@@ -0,0 +1,39 @@
+package cc.iotkit.plugins.dlt645.conf;
+
+import cc.iotkit.plugin.core.IPluginConfig;
+import cc.iotkit.plugin.core.IPluginScript;
+import cc.iotkit.plugin.core.LocalPluginConfig;
+import cc.iotkit.plugin.core.LocalPluginScript;
+import cc.iotkit.plugin.core.thing.IThingService;
+import cc.iotkit.plugins.dlt645.service.FakeThingService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 16:54
+ */
+@Slf4j
+@Component
+public class BeanConfig {
+ @Bean
+ @ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
+ IThingService getThingService() {
+ return new FakeThingService();
+ }
+
+ @Bean
+ @ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
+ IPluginScript getPluginScript() {
+ log.info("init LocalPluginScript");
+ return new LocalPluginScript("script.js");
+ }
+
+ @Bean
+ @ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
+ IPluginConfig getPluginConfig() {
+ return new LocalPluginConfig();
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/conf/TcpClientConfig.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/conf/TcpClientConfig.java
new file mode 100644
index 0000000..413a4c5
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/conf/TcpClientConfig.java
@@ -0,0 +1,21 @@
+package cc.iotkit.plugins.dlt645.conf;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:01
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "tcp")
+public class TcpClientConfig {
+
+ private String host;
+
+ private int port;
+
+ private int interval;
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/constants/DLT645Constant.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/constants/DLT645Constant.java
new file mode 100644
index 0000000..f623531
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/constants/DLT645Constant.java
@@ -0,0 +1,10 @@
+package cc.iotkit.plugins.dlt645.constants;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/22 10:54
+ */
+public class DLT645Constant {
+ public static final String PRO_VER_1997 = "v1997";
+ public static final String PRO_VER_2007 = "v2007";
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/load/DLT645v1997CsvLoader.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/load/DLT645v1997CsvLoader.java
new file mode 100644
index 0000000..83e9106
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/load/DLT645v1997CsvLoader.java
@@ -0,0 +1,93 @@
+package cc.iotkit.plugins.dlt645.load;
+
+import cc.iotkit.plugins.dlt645.analysis.DLT645Data;
+import cc.iotkit.plugins.dlt645.analysis.DLT645DataFormat;
+import cc.iotkit.plugins.dlt645.analysis.DLT645V1997Data;
+import cn.hutool.core.text.csv.CsvReader;
+import cn.hutool.core.text.csv.CsvUtil;
+import cn.hutool.core.util.CharsetUtil;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.InputStreamReader;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:59
+ */
+@Slf4j
+public class DLT645v1997CsvLoader {
+ /**
+ * 从CSV文件中装载映射表
+ *
+ */
+ public List loadCsvFile() {
+ CsvReader csvReader = CsvUtil.getReader();
+ InputStreamReader dataReader=new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("DLT645-1997.csv"),CharsetUtil.CHARSET_GBK);
+ List rows = csvReader.read(dataReader, JDecoderValueParam.class);
+ List list = new ArrayList<>();
+ for (JDecoderValueParam jDecoderValueParam : rows) {
+ try {
+ DLT645V1997Data entity = new DLT645V1997Data();
+ entity.setName(jDecoderValueParam.getName());
+ entity.setDi1h((byte) Integer.parseInt(jDecoderValueParam.di1h, 16));
+ entity.setDi1l((byte) Integer.parseInt(jDecoderValueParam.di1l, 16));
+ entity.setDi0h((byte) Integer.parseInt(jDecoderValueParam.di0h, 16));
+ entity.setDi0l((byte) Integer.parseInt(jDecoderValueParam.di0l, 16));
+ entity.setLength(jDecoderValueParam.length);
+ entity.setUnit(jDecoderValueParam.unit);
+ entity.setRead(Boolean.parseBoolean(jDecoderValueParam.read));
+ entity.setWrite(Boolean.parseBoolean(jDecoderValueParam.write));
+
+ DLT645DataFormat format = new DLT645DataFormat();
+ if (format.decodeFormat(jDecoderValueParam.format, jDecoderValueParam.length)) {
+ entity.setFormat(format);
+ } else {
+ log.info("DLT645 CSV记录的格式错误:" + jDecoderValueParam.getName() + ":" + jDecoderValueParam.getFormat());
+ continue;
+ }
+ list.add(entity);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return list;
+ }
+
+
+ @Data
+ public static class JDecoderValueParam implements Serializable {
+ private String di1h;
+ private String di1l;
+ private String di0h;
+ private String di0l;
+ /**
+ * 编码格式
+ */
+ private String format;
+ /**
+ * 长度
+ */
+ private Integer length;
+ /**
+ * 单位
+ */
+ private String unit;
+
+ /**
+ * 是否可读
+ */
+ private String read;
+ /**
+ * 是否可写
+ */
+ private String write;
+ /**
+ * 名称
+ */
+ private String name;
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/load/DLT645v2007CsvLoader.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/load/DLT645v2007CsvLoader.java
new file mode 100644
index 0000000..8d2e1fc
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/load/DLT645v2007CsvLoader.java
@@ -0,0 +1,95 @@
+package cc.iotkit.plugins.dlt645.load;
+
+import cc.iotkit.plugins.dlt645.analysis.DLT645Data;
+import cc.iotkit.plugins.dlt645.analysis.DLT645DataFormat;
+import cc.iotkit.plugins.dlt645.analysis.DLT645V2007Data;
+import cn.hutool.core.text.csv.CsvReader;
+import cn.hutool.core.text.csv.CsvUtil;
+import cn.hutool.core.util.CharsetUtil;
+import io.vertx.core.AbstractVerticle;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.InputStreamReader;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:59
+ */
+@Slf4j
+public class DLT645v2007CsvLoader extends AbstractVerticle {
+
+ /**
+ * 从CSV文件中装载映射表
+ *
+ */
+ public List loadCsvFile() {
+ CsvReader csvReader = CsvUtil.getReader();
+ InputStreamReader dataReader=new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("DLT645-2007.csv"),CharsetUtil.CHARSET_GBK);
+ List rows = csvReader.read(dataReader, JDecoderValueParam.class);
+ List list = new ArrayList<>();
+ for (JDecoderValueParam jDecoderValueParam : rows) {
+ try {
+ DLT645V2007Data entity = new DLT645V2007Data();
+ entity.setName(jDecoderValueParam.getName());
+ entity.setDi0((byte) Integer.parseInt(jDecoderValueParam.di0, 16));
+ entity.setDi1((byte) Integer.parseInt(jDecoderValueParam.di1, 16));
+ entity.setDi2((byte) Integer.parseInt(jDecoderValueParam.di2, 16));
+ entity.setDi3((byte) Integer.parseInt(jDecoderValueParam.di3, 16));
+ entity.setLength(jDecoderValueParam.length);
+ entity.setUnit(jDecoderValueParam.unit);
+ entity.setRead(Boolean.parseBoolean(jDecoderValueParam.read));
+ entity.setWrite(Boolean.parseBoolean(jDecoderValueParam.write));
+
+ DLT645DataFormat format = new DLT645DataFormat();
+ if (format.decodeFormat(jDecoderValueParam.format, jDecoderValueParam.length)) {
+ entity.setFormat(format);
+ } else {
+ log.info("DLT645 CSV记录的格式错误:" + jDecoderValueParam.getName() + ":" + jDecoderValueParam.getFormat());
+ continue;
+ }
+ list.add(entity);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return list;
+ }
+
+
+ @Data
+ public static class JDecoderValueParam implements Serializable {
+ private String di0;
+ private String di1;
+ private String di2;
+ private String di3;
+ /**
+ * 编码格式
+ */
+ private String format;
+ /**
+ * 长度
+ */
+ private Integer length;
+ /**
+ * 单位
+ */
+ private String unit;
+
+ /**
+ * 是否可读
+ */
+ private String read;
+ /**
+ * 是否可写
+ */
+ private String write;
+ /**
+ * 名称
+ */
+ private String name;
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/DLT645Device.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/DLT645Device.java
new file mode 100644
index 0000000..8999ffe
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/DLT645Device.java
@@ -0,0 +1,54 @@
+package cc.iotkit.plugins.dlt645.service;
+
+import cc.iotkit.plugin.core.thing.IDevice;
+import cc.iotkit.plugin.core.thing.actions.ActionResult;
+import cc.iotkit.plugin.core.thing.actions.down.DeviceConfig;
+import cc.iotkit.plugin.core.thing.actions.down.PropertyGet;
+import cc.iotkit.plugin.core.thing.actions.down.PropertySet;
+import cc.iotkit.plugin.core.thing.actions.down.ServiceInvoke;
+import cc.iotkit.plugins.dlt645.analysis.DLT645Converter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/14 16:22
+ */
+@Service
+public class DLT645Device implements IDevice {
+
+ @Autowired
+ private TcpClientVerticle dlt645Verticle;
+
+ @Override
+ public ActionResult config(DeviceConfig action) {
+ return ActionResult.builder().code(0).reason("").build();
+ }
+
+ public static void main(String[] args) {
+ String topic = "/JN10202310300068/s/event/property/post";
+ String s = Arrays.asList(topic.split("/")).get(1);
+ topic = "/sys/*/"+s+"/c/event/property/post";
+ System.out.println(topic);
+ }
+ @Override
+ public ActionResult propertyGet(PropertyGet action) {
+ for (String key:action.getKeys()){
+ String msg=DLT645Converter.packData(action.getDeviceName(),"读数据",key.replaceFirst("p",""));
+ dlt645Verticle.sendMsg(msg);
+ }
+ return ActionResult.builder().code(0).reason("success").build();
+ }
+
+ @Override
+ public ActionResult propertySet(PropertySet action) {
+ return null;
+ }
+
+ @Override
+ public ActionResult serviceInvoke(ServiceInvoke action) {
+ return null;
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/Dlt645Plugin.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/Dlt645Plugin.java
new file mode 100644
index 0000000..17aa3cc
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/Dlt645Plugin.java
@@ -0,0 +1,88 @@
+package cc.iotkit.plugins.dlt645.service;
+
+import cc.iotkit.common.utils.JsonUtils;
+import cc.iotkit.plugin.core.IPluginConfig;
+import cc.iotkit.plugins.dlt645.conf.TcpClientConfig;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.copier.CopyOptions;
+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
+import com.gitee.starblues.bootstrap.realize.PluginCloseListener;
+import com.gitee.starblues.core.PluginCloseType;
+import com.gitee.starblues.core.PluginInfo;
+import io.vertx.core.Future;
+import io.vertx.core.Vertx;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 16:58
+ */
+@Slf4j
+@Service
+public class Dlt645Plugin implements PluginCloseListener {
+
+ @Autowired
+ private PluginInfo pluginInfo;
+
+ @Autowired
+ private TcpClientVerticle tcpClientVerticle;
+
+ @Autowired
+ private TcpClientConfig tcpConfig;
+
+ @Autowired
+ @AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
+ private IPluginConfig pluginConfig;
+
+ private Vertx vertx;
+ private String deployedId;
+
+ @PostConstruct
+ public void init() {
+ vertx = Vertx.vertx();
+ try {
+ //获取插件最新配置替换当前配置
+ Map config = pluginConfig.getConfig(pluginInfo.getPluginId());
+ BeanUtil.copyProperties(config, tcpConfig, CopyOptions.create().ignoreNullValue());
+ tcpClientVerticle.setConfig(tcpConfig);
+
+ Future future = vertx.deployVerticle(tcpClientVerticle);
+ future.onSuccess((s -> {
+ deployedId = s;
+ log.info("dlt645 client plugin started success,config:"+ JsonUtils.toJsonString(tcpConfig));
+ }));
+ future.onFailure(Throwable::printStackTrace);
+ } catch (Throwable e) {
+ log.error("init dlt645 client plugin error", e);
+ }
+ }
+
+ @SneakyThrows
+ @Override
+ public void close(GenericApplicationContext applicationContext, PluginInfo pluginInfo, PluginCloseType closeType) {
+ log.info("plugin close,type:{},pluginId:{}", closeType, pluginInfo.getPluginId());
+ if (deployedId != null) {
+ CountDownLatch wait = new CountDownLatch(1);
+ Future future = vertx.undeploy(deployedId);
+ future.onSuccess(unused -> {
+ log.info("dlt645 client plugin stopped success");
+ wait.countDown();
+ });
+ future.onFailure(h -> {
+ log.info("dlt645 client plugin stopped failed");
+ h.printStackTrace();
+ wait.countDown();
+ });
+ wait.await(5, TimeUnit.SECONDS);
+ }
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/FakeThingService.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/FakeThingService.java
new file mode 100644
index 0000000..303f859
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/FakeThingService.java
@@ -0,0 +1,46 @@
+package cc.iotkit.plugins.dlt645.service;
+
+import cc.iotkit.plugin.core.thing.IThingService;
+import cc.iotkit.plugin.core.thing.actions.ActionResult;
+import cc.iotkit.plugin.core.thing.actions.IDeviceAction;
+import cc.iotkit.plugin.core.thing.model.ThingDevice;
+import cc.iotkit.plugin.core.thing.model.ThingProduct;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 16:56
+ */
+@Slf4j
+public class FakeThingService implements IThingService {
+
+ @Override
+ public ActionResult post(String pluginId, IDeviceAction action) {
+ log.info("post action:{}", action);
+ return ActionResult.builder().code(0).build();
+ }
+
+ @Override
+ public ThingProduct getProduct(String pk) {
+ return ThingProduct.builder()
+ .productKey("PjmkANSTDt85bZPj")
+ .productSecret("aaaaaaaa")
+ .build();
+ }
+
+ @Override
+ public ThingDevice getDevice(String dn) {
+ return ThingDevice.builder()
+ .productKey("PjmkANSTDt85bZPj")
+ .deviceName(dn)
+ .build();
+ }
+
+ @Override
+ public Map getProperty(String dn) {
+ return new HashMap<>(0);
+ }
+}
\ No newline at end of file
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/TcpClientVerticle.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/TcpClientVerticle.java
new file mode 100644
index 0000000..b3132bc
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/service/TcpClientVerticle.java
@@ -0,0 +1,184 @@
+package cc.iotkit.plugins.dlt645.service;
+
+import cc.iotkit.plugin.core.thing.IThingService;
+import cc.iotkit.plugin.core.thing.actions.DeviceState;
+import cc.iotkit.plugin.core.thing.actions.IDeviceAction;
+import cc.iotkit.plugin.core.thing.actions.up.DeviceStateChange;
+import cc.iotkit.plugin.core.thing.actions.up.PropertyReport;
+import cc.iotkit.plugins.dlt645.analysis.DLT645Analysis;
+import cc.iotkit.plugins.dlt645.analysis.DLT645Converter;
+import cc.iotkit.plugins.dlt645.analysis.DLT645FunCode;
+import cc.iotkit.plugins.dlt645.analysis.DLT645V2007Data;
+import cc.iotkit.plugins.dlt645.conf.TcpClientConfig;
+import cc.iotkit.plugins.dlt645.constants.DLT645Constant;
+import cc.iotkit.plugins.dlt645.utils.ByteUtils;
+import cn.hutool.core.util.HexUtil;
+import cn.hutool.core.util.IdUtil;
+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
+import com.gitee.starblues.core.PluginInfo;
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.net.NetClient;
+import io.vertx.core.net.NetClientOptions;
+import io.vertx.core.net.NetSocket;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:00
+ */
+@Slf4j
+@Service
+public class TcpClientVerticle extends AbstractVerticle {
+ @Getter
+ @Setter
+ private TcpClientConfig config;
+
+ private NetClient netClient;
+
+ private NetSocket socket;
+
+ @Autowired
+ @AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
+ private IThingService thingService;
+
+ @Autowired
+ private PluginInfo pluginInfo;
+
+ private int connectState = 0;
+
+ private long timerID;
+
+ @Override
+ public void start() {
+ log.info("init start");
+ }
+
+ @Scheduled(initialDelay = 2, fixedRate = 5, timeUnit = TimeUnit.SECONDS)
+ public void initClient() {
+ if (connectState > 0) {
+ return;
+ }
+ connectState = 1;
+
+ DLT645Analysis.inst().getTemplateByDIn(DLT645Constant.PRO_VER_2007);
+ NetClientOptions options = new NetClientOptions();
+ options.setReconnectAttempts(Integer.MAX_VALUE);
+ options.setReconnectInterval(20000L);
+ netClient = vertx.createNetClient(options);
+ netClient.connect(config.getPort(), config.getHost())
+ .onComplete(result -> {
+ if (result.succeeded()) {
+ connectState = 2;
+ log.info("connect dlt645 server success");
+ socket = result.result();
+ stateChange(DeviceState.ONLINE);
+ socket.handler(data -> {
+ String hexStr = ByteUtils.byteArrayToHexString(data.getBytes(), false);
+ log.info("Received message:{}", hexStr);
+ Map ret = DLT645Analysis.unPackCmd2Map(ByteUtils.hexStringToByteArray(hexStr));
+ //获取功能码
+ Object func = ret.get(DLT645Analysis.FUN);
+ DLT645FunCode funCode = DLT645FunCode.decodeEntity((byte) func);
+ if (funCode.isError()) {
+ log.info("message erroe:{}", hexStr);
+ return;
+ }
+ //获取设备地址
+ byte[] adrrTmp = (byte[]) ret.get(DLT645Analysis.ADR);
+ byte[] addr = new byte[6];
+ ByteUtils.byteInvertedOrder(adrrTmp, addr);
+ //获取数据
+ byte[] dat = (byte[]) ret.get(DLT645Analysis.DAT);
+ String strAddr=ByteUtils.byteArrayToHexString(addr,false);
+ DLT645V2007Data dataEntity = new DLT645V2007Data();
+ dataEntity.decodeValue(dat, DLT645Analysis.din2entity);
+ Map params = new HashMap<>();
+ params.put("p" + dataEntity.getKey(), dataEntity.getValue());//数据标识
+ thingService.post(pluginInfo.getPluginId(),
+ PropertyReport.builder().deviceName(strAddr).productKey("PwMfpXmp4ZWkGahn")
+ .params(params)
+ .build()
+ );
+ }).closeHandler(res -> {
+ connectState = 0;
+ vertx.cancelTimer(timerID);
+ log.info("dlt645 tcp connection closed!");
+ stateChange(DeviceState.OFFLINE);
+ }
+ ).exceptionHandler(res -> {
+ connectState = 0;
+ vertx.cancelTimer(timerID);
+ log.info("dlt645 tcp connection exce!");
+ stateChange(DeviceState.OFFLINE);
+ });
+ timerID = vertx.setPeriodic(config.getInterval(), t -> {
+ readDataTask();
+ });
+ } else {
+ connectState = 0;
+ log.info("connect dlt645 tcp error", result.cause());
+ }
+ })
+ .onFailure(e -> {
+ log.error("connect failed", e);
+ connectState = 0;
+ })
+ ;
+ }
+
+ private void readDataTask() {
+ log.info("readData:" + socket);
+ if (socket != null) {
+ String msg = DLT645Converter.packData("000023092701", "读数据", "00000000");
+ sendMsg(msg);
+ }
+ }
+
+ @Override
+ public void stop() throws Exception {
+ if (netClient != null) {
+ netClient.close();
+ }
+ vertx.cancelTimer(timerID);
+ connectState = 0;
+ super.stop();
+ }
+
+ private void stateChange(DeviceState state) {
+ thingService.post(pluginInfo.getPluginId(),
+ applyPkDn(DeviceStateChange.builder()
+ .id(IdUtil.simpleUUID())
+ .state(state)
+ .time(System.currentTimeMillis())
+ .build()));
+ }
+
+ private IDeviceAction applyPkDn(IDeviceAction action) {
+ action.setProductKey("BRD3x4fkKxkaxXFt");
+ action.setDeviceName("WG123456");
+ return action;
+ }
+
+ public void sendMsg(String msg) {
+ log.info("send msg data:{}", msg);
+ Buffer data = Buffer.buffer(HexUtil.decodeHex(msg));
+ socket.write(data, r -> {
+ if (r.succeeded()) {
+ log.info("msg send success:{}", msg);
+ } else {
+ log.error("msg send failed", r.cause());
+ }
+ });
+ }
+
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ByteRef.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ByteRef.java
new file mode 100644
index 0000000..7d24120
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ByteRef.java
@@ -0,0 +1,15 @@
+package cc.iotkit.plugins.dlt645.utils;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:49
+ */
+@Getter(value = AccessLevel.PUBLIC)
+@Setter(value = AccessLevel.PUBLIC)
+public class ByteRef {
+ private byte value = 0;
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ByteUtils.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ByteUtils.java
new file mode 100644
index 0000000..fd58b51
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ByteUtils.java
@@ -0,0 +1,78 @@
+package cc.iotkit.plugins.dlt645.utils;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:48
+ */
+public class ByteUtils {
+ /**
+ * 根据十六进制生成byte
+ *
+ * @param hex 16进制的字符串 比如"FF"
+ * @return byte数字比如-1
+ */
+ public static byte hex2byte(String hex) {
+ return Integer.valueOf(hex, 16).byteValue();
+ }
+
+ /**
+ * 16进制的字符串表示转成字节数组
+ *
+ * @param hexString 16进制格式的字符串
+ * @return 转换后的字节数组
+ **/
+ public static byte[] hexStringToByteArray(String hexString) {
+ String string = hexString.replaceAll(" ", "");
+ final byte[] byteArray = new byte[string.length() / 2];
+ int pos = 0;
+ for (int i = 0; i < byteArray.length; i++) {
+ // 因为是16进制,最多只会占用4位,转换成字节需要两个16进制的字符,高位在先
+ byte high = (byte) (Character.digit(string.charAt(pos), 16) & 0xff);
+ byte low = (byte) (Character.digit(string.charAt(pos + 1), 16) & 0xff);
+ byteArray[i] = (byte) (high << 4 | low);
+ pos += 2;
+ }
+
+ return byteArray;
+ }
+
+ /**
+ * 字节数组转成16进制表示格式的字符串
+ *
+ * @param byteArray 要转换的字节数组
+ * @return 16进制表示格式的字符串
+ **/
+ public static String byteArrayToHexString(byte[] byteArray) {
+ return byteArrayToHexString(byteArray, true);
+ }
+
+ public static String byteArrayToHexString(byte[] byteArray, boolean blankz) {
+ final StringBuilder hexString = new StringBuilder();
+ for (int i = 0; i < byteArray.length; i++) {
+ if ((byteArray[i] & 0xff) < 0x10) {
+ // 0~F前面不零
+ hexString.append("0");
+ }
+
+ hexString.append(Integer.toHexString(0xFF & byteArray[i]));
+
+ if (blankz) {
+ hexString.append(" ");
+ }
+ }
+ return hexString.toString();
+ }
+
+ /**
+ * 字节逆序
+ *
+ **/
+ public static void byteInvertedOrder(byte[] tmp,byte[] retData) {
+ System.arraycopy(tmp, 0, retData, 0, Math.min(tmp.length, retData.length));
+ for (int i = 0; i < retData.length / 2; i++) {
+ byte by = retData[i];
+ retData[i] = retData[5 - i];
+ retData[5 - i] = by;
+ }
+ }
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/BytesRef.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/BytesRef.java
new file mode 100644
index 0000000..a93589f
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/BytesRef.java
@@ -0,0 +1,15 @@
+package cc.iotkit.plugins.dlt645.utils;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:49
+ */
+@Getter(value = AccessLevel.PUBLIC)
+@Setter(value = AccessLevel.PUBLIC)
+public class BytesRef {
+ private byte[] value = new byte[] {};
+}
diff --git a/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ContainerUtils.java b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ContainerUtils.java
new file mode 100644
index 0000000..6579315
--- /dev/null
+++ b/DLT645-plugin/src/main/java/cc/iotkit/plugins/dlt645/utils/ContainerUtils.java
@@ -0,0 +1,424 @@
+package cc.iotkit.plugins.dlt645.utils;
+
+import java.io.Serializable;
+import java.lang.invoke.SerializedLambda;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.function.Function;
+
+/**
+ * @Author:tfd
+ * @Date:2023/12/13 17:50
+ * 集合操作的工具类
+ */
+public class ContainerUtils {
+ public ContainerUtils() {
+ }
+
+ /**
+ * 获取类函数的名称
+ * 例如:getMethodName(Integer.toHexString),返回的就是toHexString
+ *
+ * @param function
+ * @param
+ * @param
+ * @return
+ * @throws NoSuchMethodException
+ * @throws SecurityException
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ * @throws InvocationTargetException
+ */
+ private static String getMethodName(SerializableFunction function) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ Method method = function.getClass().getDeclaredMethod("writeReplace");
+ method.setAccessible(Boolean.TRUE);
+ SerializedLambda serializedLambda = (SerializedLambda) method.invoke(function);
+ String implMethodName = serializedLambda.getImplMethodName();
+
+ return implMethodName;
+ }
+
+ /**
+ * 获取类的函数
+ *
+ * @param clazz
+ * @param function
+ * @param
+ * @param
+ * @param
+ * @return
+ * @throws NoSuchMethodException
+ * @throws SecurityException
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ * @throws InvocationTargetException
+ */
+ private static Method getMethod(Class clazz, SerializableFunction function) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ String methodName = ContainerUtils.getMethodName(function);
+ return clazz.getMethod(methodName);
+ }
+
+ /**
+ * 根据对象列表中的对象的getXxxx()函数,取出成员
+ *
+ * @param objList
+ * @param clazz
+ * @param method method是clazz的成员函数
+ * @param
+ * @param
+ * @return
+ */
+ private static List buildListByGetField(List objList, Class clazz, Method method) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ List keyList = new ArrayList();
+ for (T obj : objList) {
+ // 接下来就该执行该方法了,第一个参数是具体调用该方法的对象, 第二个参数是执行该方法的具体参数
+ Object keyObject = method.invoke(obj);
+ if (clazz.isInstance(keyObject)) {
+ K key = clazz.cast(keyObject);
+ keyList.add(key);
+ }
+ }
+
+ return keyList;
+ }
+
+ public static List> buildKeyList(List objList, Method method) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ List keyList = new ArrayList();
+ Class> returnType = method.getReturnType();
+ for (T obj : objList) {
+ // 接下来就该执行该方法了,第一个参数是具体调用该方法的对象, 第二个参数是执行该方法的具体参数
+ Object keyObject = method.invoke(obj);
+ if (returnType.isInstance(keyObject)) {
+ keyList.add((K) returnType.cast(keyObject));
+ }
+ }
+
+ return keyList;
+ }
+
+ /**
+ * 根据对象的getXxxx(),取出类型列表中的数据
+ *
+ * @param objList AClass对象列表
+ * @param clazz TClass AClass::getXxxx()中,TClass这样的成员
+ * @param function AClass::getXxxx 这样的函数
+ * @param
+ * @param
+ * @param
+ * @param
+ * @return
+ */
+ public static List buildListByGetField(List objList, SerializableFunction function, Class clazz) {
+ if (objList.isEmpty()) {
+ return new ArrayList();
+ }
+
+ try {
+ // 取得函数对应的方法
+ Method method = ContainerUtils.getMethod(objList.get(0).getClass(), function);
+
+ // 使用方法返回对应的数组
+ return ContainerUtils.buildListByGetField(objList, clazz, method);
+ } catch (NoSuchMethodException e) {
+ return new ArrayList();
+ } catch (SecurityException e) {
+ return new ArrayList();
+ } catch (IllegalAccessException e) {
+ return new ArrayList();
+ } catch (IllegalArgumentException e) {
+ return new ArrayList();
+ } catch (InvocationTargetException e) {
+ return new ArrayList();
+ }
+ }
+
+ /**
+ * 根据Key生成Map,该方法是是具体类的函数,(不具备多态能力,不是反射,速度很快)
+ *
+ * @param objList
+ * @param clazz
+ * @param method 使用obj.getClass().getMethod("getTnlKey", new Class[0])获取Method
+ * @return
+ */
+ public static Map buildMapByKeyAndFinalMethod(List objList, Class clazz, Method method) {
+ try {
+ Map uid2deviceMap = new HashMap();
+ for (T obj : objList) {
+ // 接下来就该执行该方法了,第一个参数是具体调用该方法的对象, 第二个参数是执行该方法的具体参数
+ Object keyObject = method.invoke(obj);
+ if (clazz.isInstance(keyObject)) {
+ K key = clazz.cast(keyObject);
+ uid2deviceMap.put(key, obj);
+ }
+ }
+
+ return uid2deviceMap;
+ } catch (IllegalAccessException e) {
+ return Collections.emptyMap();
+ } catch (InvocationTargetException e) {
+ return Collections.emptyMap();
+ }
+ }
+
+
+ public static Map buildMapByKey(List objList, SerializableFunction function) {
+ if (objList.isEmpty()) {
+ return new HashMap<>();
+ }
+
+ try {
+ // 取得函数对应的方法
+ Method method = ContainerUtils.getMethod(objList.get(0).getClass(), function);
+
+ // 使用方法返回对应的数组
+ return ContainerUtils.buildMapByKey(objList, method);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | IllegalArgumentException |
+ SecurityException e) {
+ return new HashMap();
+ }
+ }
+
+ public static Map buildMapByKey(List objList, Method method) {
+ try {
+ Map uid2deviceMap = new HashMap();
+ for (T obj : objList) {
+ // 接下来就该执行该方法了,第一个参数是具体调用该方法的对象, 第二个参数是执行该方法的具体参数
+ Object keyObject = method.invoke(obj);
+ uid2deviceMap.put((K) keyObject, obj);
+ }
+
+ return uid2deviceMap;
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ return Collections.emptyMap();
+ }
+ }
+
+ /**
+ * 分类
+ *
+ * @param objList
+ * @param clazz
+ * @param method
+ * @param
+ * @param
+ * @return
+ */
+ public static Map> buildMapByTypeAndFinalMethod(List objList, Class clazz, Method method) {
+ try {
+ Map> uid2deviceMap = new HashMap<>();
+ for (T obj : objList) {
+ // 接下来就该执行该方法了,第一个参数是具体调用该方法的对象, 第二个参数是执行该方法的具体参数
+ Object keyObject = method.invoke(obj);
+ if (clazz.isInstance(keyObject)) {
+ K key = clazz.cast(keyObject);
+ List list = uid2deviceMap.computeIfAbsent(key, k -> new ArrayList<>());
+ list.add(obj);
+ }
+ }
+ return uid2deviceMap;
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ return Collections.emptyMap();
+ }
+ }
+
+ /**
+ * 根据Key生成Map,该方法是是具体类的函数,(不具备多态能力,不是反射,速度很快)
+ *
+ * @param objList 类型AClass的列表容器
+ * @param function 类型AClass中的某个成员getxxxx()
+ * @param clazz 类型AClass中的某个成员BClass getxxxx()中的BClass
+ * @param
+ * @param
+ * @param
+ * @param
+ * @return
+ */
+ public static Map buildMapByKeyAndFinalMethod(List objList, SerializableFunction function, Class clazz) {
+ if (objList.isEmpty()) {
+ return new HashMap<>();
+ }
+
+ try {
+ // 取得函数对应的方法
+ Method method = ContainerUtils.getMethod(objList.get(0).getClass(), function);
+
+ // 使用方法返回对应的数组
+ return ContainerUtils.buildMapByKeyAndFinalMethod(objList, clazz, method);
+ } catch (NoSuchMethodException e) {
+ return new HashMap();
+ } catch (SecurityException e) {
+ return new HashMap();
+ } catch (IllegalAccessException e) {
+ return new HashMap();
+ } catch (IllegalArgumentException e) {
+ return new HashMap();
+ } catch (InvocationTargetException e) {
+ return new HashMap();
+ }
+ }
+
+ public static Map> buildMapByTypeAndFinalMethod(List objList, SerializableFunction function, Class clazz) {
+ if (objList.isEmpty()) {
+ return new HashMap<>();
+ }
+
+ try {
+ // 取得函数对应的方法
+ Method method = ContainerUtils.getMethod(objList.get(0).getClass(), function);
+
+ // 使用方法返回对应的数组
+ return ContainerUtils.buildMapByTypeAndFinalMethod(objList, clazz, method);
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalArgumentException | IllegalAccessException |
+ SecurityException e) {
+ return new HashMap<>();
+ }
+ }
+
+ /**
+ * 根据map中的某个元素,将列表转换成以这个元素为key的map
+ *
+ * @param objList
+ * @param mapKey
+ * @param clazz
+ * @param
+ * @param
+ * @return
+ */
+ public static Map> buildMapByMapAt(List