├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── LICENSE
├── README.md
├── README_zh.md
├── docs
├── gb32960
│ └── GBT 32960.3-2016 电动汽车远程服务与管理系统技术规范 第3部分:通讯协议及数据格式.pdf
└── images
│ ├── img_1.png
│ ├── img_2.png
│ ├── img_3.png
│ ├── img_4.png
│ ├── img_5.png
│ ├── img_6.png
│ ├── img_7.png
│ ├── img_8.png
│ └── logo.png
├── example-extension-define
├── README.md
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── mqttsnet
│ └── thinglinks
│ └── open
│ └── exp
│ └── adapter
│ └── springboot3
│ └── example
│ └── UserService.java
├── example-plugin-demo
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── assembly
│ └── zip.xml
│ ├── java
│ └── com
│ │ └── mqttsnet
│ │ └── thinglinks
│ │ └── open
│ │ └── exp
│ │ └── example
│ │ └── a
│ │ ├── Boot.java
│ │ ├── MyComponent.java
│ │ ├── MyUserServicePluginImpl.java
│ │ └── TestController.java
│ └── resources
│ ├── extension.properties
│ └── pluginMeta.properties
├── example-plugin-tcpserver
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── mqttsnet
│ │ └── thinglinks
│ │ └── open
│ │ └── exp
│ │ └── example
│ │ └── tcpserver
│ │ ├── Boot.java
│ │ ├── MyComponent.java
│ │ ├── MyUserServicePluginImpl.java
│ │ ├── TcpServer.java
│ │ ├── decoder
│ │ ├── BaseProtocolDecoder.java
│ │ ├── CustomProtocolDecoder.java
│ │ └── GB32960Decoder.java
│ │ ├── dispatcher
│ │ └── MessageDispatcher.java
│ │ ├── encoder
│ │ └── EncoderHandler.java
│ │ ├── entity
│ │ └── custom
│ │ │ └── CustomMessage.java
│ │ ├── gb32960
│ │ ├── entity
│ │ │ ├── GB32960MessageParser.java
│ │ │ └── dao
│ │ │ │ ├── GB32960Alert.java
│ │ │ │ ├── GB32960AlertStatus.java
│ │ │ │ ├── GB32960BaseDTO.java
│ │ │ │ ├── GB32960CustomData.java
│ │ │ │ ├── GB32960DriveMotorStatus.java
│ │ │ │ ├── GB32960DriveMotors.java
│ │ │ │ ├── GB32960EnergyStorageTemperatureStatus.java
│ │ │ │ ├── GB32960EnergyStorageTemperatures.java
│ │ │ │ ├── GB32960EnergyStorageVoltageStatus.java
│ │ │ │ ├── GB32960EnergyStorageVoltages.java
│ │ │ │ ├── GB32960ExtremeStatus.java
│ │ │ │ ├── GB32960LocateStatus.java
│ │ │ │ ├── GB32960LocationStatus.java
│ │ │ │ ├── GB32960MessageData.java
│ │ │ │ ├── GB32960TransmissionStatus.java
│ │ │ │ └── GB32960VehicleStatus.java
│ │ ├── handler
│ │ │ └── RealTimeDataHandler.java
│ │ └── service
│ │ │ ├── DataParseService.java
│ │ │ └── impl
│ │ │ └── DataParseServiceImpl.java
│ │ ├── handler
│ │ ├── ExceptionHandler.java
│ │ └── MyServerHandler.java
│ │ ├── initializer
│ │ └── MyServerInitializer.java
│ │ └── utils
│ │ ├── AesUtils.java
│ │ ├── HexUtils.java
│ │ ├── Sm4Utils.java
│ │ ├── SpringUtils.java
│ │ ├── SubStringUtil.java
│ │ └── bytes
│ │ ├── ByteArrayUtil.java
│ │ ├── ByteCastUtil.java
│ │ └── ByteUtil.java
│ └── resources
│ ├── extension.properties
│ └── pluginMeta.properties
├── example-plugin-tcptomqtt
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── mqttsnet
│ │ └── thinglinks
│ │ └── open
│ │ └── exp
│ │ └── example
│ │ └── tcptomqtt
│ │ ├── Boot.java
│ │ ├── MyComponent.java
│ │ ├── MyUserServicePluginImpl.java
│ │ ├── TcpServer.java
│ │ ├── custom
│ │ └── CustomMessage.java
│ │ ├── decoder
│ │ ├── BaseProtocolDecoder.java
│ │ ├── CustomProtocolDecoder.java
│ │ └── GB32960Decoder.java
│ │ ├── dispatcher
│ │ └── MessageDispatcher.java
│ │ ├── encoder
│ │ └── EncoderHandler.java
│ │ ├── gb32960
│ │ ├── entity
│ │ │ ├── GB32960MessageParser.java
│ │ │ └── dao
│ │ │ │ ├── GB32960Alert.java
│ │ │ │ ├── GB32960AlertStatus.java
│ │ │ │ ├── GB32960BaseDTO.java
│ │ │ │ ├── GB32960CustomData.java
│ │ │ │ ├── GB32960DriveMotorStatus.java
│ │ │ │ ├── GB32960DriveMotors.java
│ │ │ │ ├── GB32960EnergyStorageTemperatureStatus.java
│ │ │ │ ├── GB32960EnergyStorageTemperatures.java
│ │ │ │ ├── GB32960EnergyStorageVoltageStatus.java
│ │ │ │ ├── GB32960EnergyStorageVoltages.java
│ │ │ │ ├── GB32960ExtremeStatus.java
│ │ │ │ ├── GB32960LocateStatus.java
│ │ │ │ ├── GB32960LocationStatus.java
│ │ │ │ ├── GB32960MessageData.java
│ │ │ │ ├── GB32960TransmissionStatus.java
│ │ │ │ └── GB32960VehicleStatus.java
│ │ └── service
│ │ │ ├── DataParseService.java
│ │ │ └── impl
│ │ │ └── DataParseServiceImpl.java
│ │ ├── handler
│ │ └── ExceptionHandler.java
│ │ ├── initializer
│ │ └── MyServerInitializer.java
│ │ ├── mqtt
│ │ └── event
│ │ │ ├── MqttConnectionStatusEvent.java
│ │ │ ├── MqttMessageReceiveEvent.java
│ │ │ ├── MqttMessageSendEvent.java
│ │ │ ├── MqttPublishMessageEvent.java
│ │ │ ├── listener
│ │ │ ├── MqttConnectionStatusListener.java
│ │ │ ├── MqttMessageReceiveEventListener.java
│ │ │ ├── MqttMessageSendEventListener.java
│ │ │ └── MqttPublishMessageEventListener.java
│ │ │ └── publisher
│ │ │ └── MqttEventPublisher.java
│ │ └── utils
│ │ ├── AesUtils.java
│ │ ├── HexUtils.java
│ │ ├── Sm4Utils.java
│ │ ├── SpringUtils.java
│ │ ├── SubStringUtil.java
│ │ └── bytes
│ │ ├── ByteArrayUtil.java
│ │ ├── ByteCastUtil.java
│ │ └── ByteUtil.java
│ └── resources
│ ├── extension.properties
│ └── pluginMeta.properties
├── example-plugin-udpserver
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── mqttsnet
│ │ └── thinglinks
│ │ └── open
│ │ └── exp
│ │ └── example
│ │ └── udpserver
│ │ ├── Boot.java
│ │ ├── BootNettyUdpAbstractBootstrapServer.java
│ │ ├── BootNettyUdpBootstrap.java
│ │ ├── BootNettyUdpBootstrapServer.java
│ │ ├── BootNettyUdpBootstrapThread.java
│ │ ├── BootNettyUdpData.java
│ │ ├── BootNettyUdpDataCache.java
│ │ ├── BootNettyUdpSimpleChannelInboundHandler.java
│ │ ├── MyComponent.java
│ │ ├── MyUserServicePluginImpl.java
│ │ ├── UdpClient.java
│ │ └── UdpServer.java
│ └── resources
│ ├── extension.properties
│ └── pluginMeta.properties
├── example-plugin-udptomqtt
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── mqttsnet
│ │ └── thinglinks
│ │ └── open
│ │ └── exp
│ │ └── example
│ │ └── udptomqtt
│ │ ├── Boot.java
│ │ ├── BootNettyUdpAbstractBootstrapServer.java
│ │ ├── BootNettyUdpBootstrap.java
│ │ ├── BootNettyUdpBootstrapServer.java
│ │ ├── BootNettyUdpBootstrapThread.java
│ │ ├── BootNettyUdpData.java
│ │ ├── BootNettyUdpDataCache.java
│ │ ├── BootNettyUdpSimpleChannelInboundHandler.java
│ │ ├── MyComponent.java
│ │ ├── MyUserServicePluginImpl.java
│ │ ├── UdpClient.java
│ │ └── UdpServer.java
│ └── resources
│ ├── extension.properties
│ └── pluginMeta.properties
├── example-springboot3
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── mqttsnet
│ │ └── thinglinks
│ │ └── open
│ │ └── exp
│ │ └── adapter
│ │ └── springboot3
│ │ ├── BaseController.java
│ │ ├── HttpFileDownloader.java
│ │ ├── Main.java
│ │ ├── PluginConfigImpl.java
│ │ ├── ResModel.java
│ │ └── User.java
│ └── resources
│ ├── META-INF
│ └── services
│ │ └── com.mqttsnet.thinglinks.open.exp.client.PluginConfig
│ └── application.yml
└── pom.xml
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | thinglinks授权许可协议
2 |
3 | 一、 知识产权:
4 | thinglinks系列产品知识产权归MqttsNet团队独立所有
5 |
6 | 二、 许可:
7 | 1. 在您完全接受并遵守本协议的基础上,本协议授予您使用thinglinks的某些权利和非独占性许可。
8 | 2. 本协议中,将本产品使用用途分为"个人版用途"和"商业版用途"。
9 | 3. "个人版用途"定义:指个人在非团体机构中出于任何目的使用本产品。(任何目的包括非商业目的或非盈利目的)。
10 | 4. "企业版用途"定义:指团体机构(例如公司企业、政府、学校、军队、医院、社会团体等各类组织)出于任何目的使用本产品(任何目的包括商业目的或非盈利目的) 或者 个人基于商业或盈利目的使用本产品。
11 |
12 | 三、 约束和限制:
13 | 1. 本产品只能由您为本协议许可的目的而使用,您不得透露、转卖、售卖、赠送、授予任何第三方;
14 | 2. 从本产品取得的任何信息、软件、产品或服务,您不得对其进行修改、改编或基于以上内容创建同种类别的衍生产品(如:快速开发平台、开发框架、脚手架)进行源码售卖。
15 | 3. 个人购买企业版,只可用于学习使用,不可通过赠送、出售、分享或跳槽等方式,给其他公司使用;但若跳槽后推荐公司购买企业版,可获得推荐奖励。
16 |
17 | 四、您享有的权利:
18 | 1. 允许个人、个人所在团队或企业、商业化使用。可以任意修改源码、项目名、包名等,并且复制或者直接使用到多个项目!
19 | 2. 一次购买终身受用,且支持后续所有代码、文档、视频持续更新!
20 | 3. 您可以基于此项目二次开发【二次开发指结合甲方/最终客户方个性化需求,为实现项目交付所进行的定制化修改/功能性扩展等开发方式】后申请专利和软著,但严禁使用本项目中的源码、页面截图等,请使用自己二次开发部分的代码和页面截图!
21 | 4.源代码授权公对公/一对一,仅允许被授权方以项目形式,进行二次开发做成解决方案并整体封装后交付,不得单独交付thinglinks源代码或发布成同类型物联网基础平台销售。
22 | 5.被授权方不得替换乙方提供Java源代码中含thinglinks&mqttsnet的相关标识:Java类中package代码包名、Maven中的groupId。【不影响项目使用】
23 |
24 | 五、 不得用于非法或禁止的用途:
25 | 您在使用本产品或服务时,不得将本产品或服务用于任何违反法律的用途或本协议条款、条件和声明禁止的用途。
26 |
27 | 六、 免责声明:
28 | 1. 本产品按"现状"授予许可,您须自行承担使用本产品的风险。MqttsNet团队不对此提供任何明示、暗示或任何其它形式的担保和表示。在任何情况下,对于因使用或无法使用本软件而导致的任何损失,MqttsNet团队无需向您或任何第三方负责,即使MqttsNet团队已被告知可能会造成此类损失。在任何情况下,MqttsNet团队均不就任何直接的、间接的、附带的、后果性的、特别的、惩戒性的和处罚性的损害赔偿承担任何责任,无论该主张是基于保证、合同、侵权(包括疏忽)或是基于其他原因作出。
29 | 2. 本产品可能内置有第三方服务,您应自行评估使用这些第三方服务的风险,由使用此类第三方服务而产生的纠纷,全部责任由您自行承担。
30 | 3. MqttsNet团队可能会经常提供产品更新或升级,但MqttsNet团队没有为根据本协议许可的产品提供维护或更新的责任,但会尽最大努力提供升级日志。
31 | 4. MqttsNet团队不对使用本产品构建的网站中任何信息内容以及导致的任何版权纠纷、法律争议和后果承担任何责任,全部责任由您自行承担。
32 | 5. MqttsNet团队可能会按照官方制定的答疑规则为您进行答疑,但MqttsNet团队没有为根据本协议许可的产品提供技术支持的义务或责任。
33 |
34 | 七、 权利和所有权的保留:
35 | MqttsNet团队保留所有未在本协议中明确授予您的所有权利。MqttsNet团队保留随时更新本协议的权利,并只需公示于对应产品项目的LICENSE文件,无需征得您的事先同意且无需另行通知,更新后的内容应于公示即时生效。您可以随时访问产品地址并查阅最新版许可条款,在更新生效后您继续使用本产品则被视作您已接受了新的条款。
36 |
37 | 七、 协议终止
38 | 1. 您一旦开始复制、下载、安装或者使用本产品,即被视为完全理解并接受本协议的各项条款,在享有上述条款授予的许可权力同时,也受到相关的约束和限制,本协议许可范围以外的行为,将直接违反本协议并构成侵权。
39 | 2. 一旦您违反本协议的条款,MqttsNet团队随时可能终止本协议、收回许可和授权,并要求您承担相应法律和经济责任。
40 |
41 | 违约追责,侵权必究,一经发现,永不授权!
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](http://www.mqttsnet.com)
4 |
5 |
6 |
7 | ## ThingLinks Open Exp Plugin | [中文文档](README_zh.md)
8 |
9 | # Open Exp Plugin Overview
10 |
11 | **Open-Exp-Plugin Sample Marketplace** is a plugin repository based on the ThingLinks Open EXP extension point plugin system. It is designed to demonstrate how to develop, extend, and integrate functionalities within the ThingLinks platform. This marketplace provides a variety of plugin examples to help developers quickly get started and understand the powerful capabilities and flexibility of the plugin system.
12 |
13 | ## Features
14 |
15 | - **Plugin Architecture**: Demonstrates the ThingLinks plugin architecture, supporting various plugin development models.
16 | - **Hot-Swappable Support**: Plugins support dynamic installation and uninstallation at runtime without restarting the main application.
17 | - **Multi-Tenant Support**: Supports loading different plugins based on tenant ID for customized functionality.
18 | - **Modular Design**: Plugins and the main application adopt a modular design, supporting the isolation and integration of extension points and plugins.
19 | - **Classloader Isolation**: Provides Parent First and Self First classloader isolation mechanisms to ensure independence between plugins.
20 |
21 | ## Usage Example
22 |
23 | ### How to Use the Plugin Sample Marketplace
24 |
25 | 1. **Enable Dependencies and Select Plugins**: Enable the `example` related dependencies in the `pom.xml` file of the `open-exp-plugin` module.
26 |
27 | 
28 |
29 | 2. **Reload Projects**: Reload all Maven projects to ensure the dependencies are correctly loaded.
30 |
31 | 3. **Clean and Package**: Clean and package the `all-package` module.
32 |
33 | 
34 |
35 | 4. **After Packaging**: The plugin packages are by default generated in the `exp-plugins` directory.
36 |
37 | 
38 |
39 | 5. **Start the Main Application**: Run the `Main` class in the `example-springboot3` module.
40 |
41 | The main application will automatically load and install the packaged plugins(That is, plugins in the exp-plugins directory). If you need to reinstall or uninstall plugins, simply call the relevant API.
42 |
43 | ### Notes
44 |
45 | 1. **Configuration Definition**: Plugin configurations should be defined in the `Boot` class.
46 | 
47 | Configuration usage:
48 | 
49 |
50 | 2. **MQTT Configuration**: In the `example-plugin-tcptomqtt` and `example-plugin-udptomqtt` plugins, MQTT server configurations should be adjusted according to the actual environment.
51 | 
52 |
53 | 3. **Annotation Import**: Ensure that the packages imported by the `@PostConstruct` and `@PreDestroy` annotations in the plugin's entry point are correct.
54 | 
55 |
56 | ## Core Features
57 |
58 | - **Extension Point Interface**: Defines multiple extension point interfaces for plugins to implement.
59 | - **Multi-Tenant Support**: Different tenants can use different plugin implementations, with support for tenant priority sorting and filtering.
60 | - **Hot-Swappable Mechanism**: Supports dynamic loading and unloading of plugins, enhancing system extensibility and flexibility.
61 | - **Classloader Isolation**: Ensures isolation between the plugin and the main application classloader, maintaining independence and security.
62 |
63 | ## License
64 |
65 | [Apache License, Version 2.0](LICENSE)
66 |
67 | ## Contact
68 |
69 | If you have any questions or need support, please contact the community team at mqttsnet@163.com.
70 |
71 | ## Source Code
72 |
73 | The source code for this project is available at: [GitHub Repository](https://github.com/mqttsnet/open-exp-plugin)
74 |
75 | ## Join Us
76 |
77 | We welcome you to join the **MQTTSNET Community**, where you can explore and promote IoT technology development together with developers from around the world. Through the community, you can access the latest technical information, rich development resources, and opportunities to communicate with other developers.
78 |
79 | Visit the [ThingLinks Official Website](https://www.mqttsnet.com) for more information and to join our developer community!
80 |
--------------------------------------------------------------------------------
/README_zh.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](http://www.mqttsnet.com)
4 |
5 |
6 |
7 | ## ThingLinks Open Exp Plugin | [English Documentation](README.md)
8 |
9 | # Open Exp Plugin 简介
10 |
11 | **Open-Exp-Plugin 插件示例市场** 是一个基于ThingLinks Open EXP 扩展点插件系统的插件仓库,旨在展示如何开发、扩展和集成
12 | ThingLinks 平台的功能。该市场提供了多种插件示例,帮助开发者快速上手并了解插件系统的强大功能和灵活性。
13 |
14 | ## 特性
15 |
16 | - **插件架构**:展示了 ThingLinks 的插件架构,支持多种插件开发模式。
17 | - **热插拔支持**:插件支持运行时的动态安装和卸载,无需重启主应用。
18 | - **多租户支持**:支持根据租户 ID 加载不同的插件,实现定制化功能。
19 | - **模块化设计**:插件和主应用采用模块化设计,支持扩展点和插件的隔离与集成。
20 | - **类加载隔离**:提供 Parent First 和 Self First 两种类加载隔离机制,确保插件间的相互独立性。
21 |
22 | ## 使用示例
23 |
24 | ### 如何使用插件示例市场
25 |
26 | 1. **启用依赖并选择插件**:在 `open-exp-plugin` 模块的 `pom.xml` 文件中启用 `example` 的相关依赖。
27 |
28 | 
29 |
30 | 2. **刷新项目**:重新加载所有 Maven 项目,确保依赖正确加载。
31 |
32 | 3. **清理打包**:对 `all-package` 进行清理打包操作。
33 |
34 | 
35 |
36 | 4. **打包完成后**,插件包默认生成在 `exp-plugins` 目录下。
37 |
38 | 
39 |
40 | 5. **启动主应用**:运行 `example-springboot3` 模块中的 `Main` 类。
41 |
42 | 主应用会自动加载并安装打包好的插件( 即`exp-plugins` 目录下插件),如果需要重新安装及卸载插件,调用其API即可。
43 |
44 | ### 注意事项
45 |
46 | 1. **配置定义**:插件内的配置需要在 `Boot` 类中定义。
47 | 
48 | 配置的使用方式:
49 | 
50 |
51 | 2. **MQTT 配置**:在 `example-plugin-tcptomqtt` 和 `example-plugin-udptomqtt` 插件中,MQTT 服务端的配置需要根据实际环境进行调整。
52 | 
53 |
54 | 3. **注解导入**:确保插件启动入口的 `@PostConstruct` 和 `@PreDestroy` 注解导入的包是否正确。
55 | 
56 |
57 | ## 核心功能
58 |
59 | - **扩展点接口**:定义了多个扩展点接口,供插件实现。
60 | - **多租户支持**:不同租户可以使用不同的插件实现,支持租户优先级排序和过滤。
61 | - **热插拔机制**:支持插件的动态加载与卸载,提升系统的扩展性和灵活性。
62 | - **类加载隔离**:插件与主应用之间的类加载实现隔离,确保独立性与安全性。
63 |
64 | ## 许可证
65 |
66 | [Apache License, Version 2.0](LICENSE)
67 |
68 | ## 联系方式
69 |
70 | 如有任何问题或需要支持,请联系社区团队:mqttsnet@163.com。
71 |
72 | ## 源码
73 |
74 | 此项目的源码可在以下地址找到:[GitHub 仓库](https://github.com/mqttsnet/open-exp-plugin)
75 |
76 | ## 加入我们
77 |
78 | 欢迎加入 **MQTTSNET 社区**,与全球物联网开发者一起探索和推动物联网技术的发展。通过社区,您可以获取最新的技术资讯、丰富的开发资源,以及与其他开发者交流的机会。
79 |
80 | 访问 [ThingLinks 官网](https://www.mqttsnet.com) 了解更多信息,并加入我们的开发者社区!
81 |
--------------------------------------------------------------------------------
/docs/gb32960/GBT 32960.3-2016 电动汽车远程服务与管理系统技术规范 第3部分:通讯协议及数据格式.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/gb32960/GBT 32960.3-2016 电动汽车远程服务与管理系统技术规范 第3部分:通讯协议及数据格式.pdf
--------------------------------------------------------------------------------
/docs/images/img_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/img_1.png
--------------------------------------------------------------------------------
/docs/images/img_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/img_2.png
--------------------------------------------------------------------------------
/docs/images/img_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/img_3.png
--------------------------------------------------------------------------------
/docs/images/img_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/img_4.png
--------------------------------------------------------------------------------
/docs/images/img_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/img_5.png
--------------------------------------------------------------------------------
/docs/images/img_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/img_6.png
--------------------------------------------------------------------------------
/docs/images/img_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/img_7.png
--------------------------------------------------------------------------------
/docs/images/img_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/img_8.png
--------------------------------------------------------------------------------
/docs/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/docs/images/logo.png
--------------------------------------------------------------------------------
/example-extension-define/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 |
3 | ## 说明
4 | 此模块主要用于通用扩展接口的定义,为其他模块提供扩展点,以便在运行时动态加载和扩展功能。
5 |
6 | ## 功能
7 | 1. **定义扩展点**:提供一个接口,用于定义扩展点,其他模块可以通过这些扩展点进行扩展。
--------------------------------------------------------------------------------
/example-extension-define/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | tasks = executor.shutdownNow();
103 | log.info("终止{}中的{}个任务", poolName, tasks.size());
104 |
105 | try {
106 | if (!executor.awaitTermination(0, TimeUnit.SECONDS)) {
107 | log.warn("{}未能及时终止,可能存在资源泄漏", poolName);
108 | }
109 | } catch (InterruptedException e) {
110 | Thread.currentThread().interrupt();
111 | }
112 | }
113 | }
114 |
115 |
116 | /**
117 | * 发送 tcpserver 心跳
118 | */
119 | private void sendHeartbeat() {
120 | try {
121 | log.info("example-plugin-tcpserver Heartbeat sent successfully at at {} with IP: {}", LocalDateTime.now(), NetUtil.getLocalhostStr());
122 |
123 | // 发送心跳 到 插件服务器
124 | // sendMqttHeartbeat(Boot.mqttDeviceIdentification.getDefaultValue());
125 |
126 | } catch (Exception e) {
127 | log.error("Failed to send heartbeat at {}", LocalDateTime.now(), e);
128 | }
129 | }
130 |
131 | }
132 |
133 |
134 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/MyUserServicePluginImpl.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * @author cxs
9 | */
10 | @Slf4j
11 | @Component
12 | public class MyUserServicePluginImpl implements UserService {
13 |
14 | @Override
15 | public void createUserExt() {
16 | log.info("create user ext");
17 | }
18 |
19 | @Override
20 | public String getName() {
21 | return MyUserServicePluginImpl.class.getName();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/decoder/BaseProtocolDecoder.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.decoder;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.ChannelInboundHandlerAdapter;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | /**
8 | * -----------------------------------------------------------------------------
9 | * File Name: BaseProtocolDecoder
10 | * -----------------------------------------------------------------------------
11 | * Description: 基础协议解析处理器
12 | *
13 | * -----------------------------------------------------------------------------
14 | *
15 | * @author xiaonannet
16 | * @version 1.0
17 | * -----------------------------------------------------------------------------
18 | * Revision History:
19 | * Date Author Version Description
20 | * -------- -------- ------- --------------------
21 | * 2024/9/8 xiaonannet 1.0 Initial creation
22 | * -----------------------------------------------------------------------------
23 | * @email
24 | * @date 2024/9/8 18:49
25 | */
26 | @Slf4j
27 | public abstract class BaseProtocolDecoder extends ChannelInboundHandlerAdapter {
28 |
29 | /**
30 | * 判断消息是否可以被当前解码器解码
31 | *
32 | * @param msg 消息对象
33 | * @return true 表示可以解码,false 表示不支持
34 | */
35 | protected abstract boolean canDecode(Object msg);
36 |
37 | /**
38 | * 具体的解码逻辑
39 | *
40 | * @param msg 消息对象
41 | * @return 解码后的结果
42 | * @throws Exception 解析异常
43 | */
44 | protected abstract Object decode(Object msg) throws Exception;
45 |
46 |
47 | @Override
48 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
49 | if (canDecode(msg)) {
50 | Object decodedMessage = decode(msg);
51 | if (decodedMessage != null) {
52 | ctx.fireChannelRead(decodedMessage);
53 | } else {
54 | log.warn("Decoding failed, message is null.");
55 | }
56 | } else {
57 | log.warn("Cannot decode message, passing to the next handler.");
58 | ctx.fireChannelRead(msg); // 如果不能解码,则传递给下一个处理器
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/decoder/CustomProtocolDecoder.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.decoder;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.entity.custom.CustomMessage;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.handler.codec.ByteToMessageDecoder;
7 | import lombok.extern.slf4j.Slf4j;
8 |
9 | import java.nio.charset.StandardCharsets;
10 | import java.util.List;
11 |
12 | /**
13 | * -----------------------------------------------------------------------------
14 | * File Name: CustomProtocolDecoder
15 | * -----------------------------------------------------------------------------
16 | * Description:
17 | * 自定义协议解析器
18 | * -----------------------------------------------------------------------------
19 | *
20 | * @author xiaonannet
21 | * @version 1.0
22 | * -----------------------------------------------------------------------------
23 | * Revision History:
24 | * Date Author Version Description
25 | * -------- -------- ------- --------------------
26 | * 2024/9/8 xiaonannet 1.0 Initial creation
27 | * -----------------------------------------------------------------------------
28 | * @email
29 | * @date 2024/9/8 18:50
30 | */
31 | @Slf4j
32 | public class CustomProtocolDecoder extends ByteToMessageDecoder {
33 |
34 | private static final String DELIMITER = "\n"; // 使用换行符作为定界符
35 |
36 | @Override
37 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
38 | // 检查是否有数据可读取
39 | if (in.readableBytes() <= 0) {
40 | return;
41 | }
42 |
43 | // 将 ByteBuf 转换为字符串
44 | String decodedData = in.toString(StandardCharsets.UTF_8);
45 |
46 | // 根据定界符拆分消息
47 | String[] messages = decodedData.split(DELIMITER);
48 |
49 | for (String message : messages) {
50 | if (message.isEmpty()) {
51 | continue;
52 | }
53 |
54 | // 处理每个消息,这里可以根据实际协议进行解析
55 | log.info("Decoded custom protocol message: {}", message);
56 |
57 | // 将解析后的消息传递给下一个处理器
58 | out.add(parseCustomMessage(message));
59 | }
60 | }
61 |
62 | // 自定义消息的解析逻辑
63 | private CustomMessage parseCustomMessage(String message) {
64 | // 假设消息格式为: "messageType:payload"
65 | String[] parts = message.split(":");
66 |
67 | if (parts.length == 2) {
68 | String messageType = parts[0].trim();
69 | String payload = parts[1].trim();
70 |
71 | log.info("Parsed custom message - Type: {}, Payload: {}", messageType, payload);
72 |
73 | // 创建并返回 CustomMessage 对象
74 | return new CustomMessage(messageType, payload);
75 | } else {
76 | log.warn("Received invalid message format: {}", message);
77 | return null; // 如果消息格式不正确,返回null或者抛出异常
78 | }
79 | }
80 |
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/decoder/GB32960Decoder.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.decoder;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.GB32960MessageParser;
4 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao.GB32960MessageData;
5 | import io.netty.buffer.ByteBuf;
6 | import io.netty.buffer.ByteBufUtil;
7 | import io.netty.channel.ChannelHandlerContext;
8 | import io.netty.handler.codec.ByteToMessageDecoder;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | import java.nio.charset.StandardCharsets;
12 | import java.util.List;
13 |
14 | /**
15 | * -----------------------------------------------------------------------------
16 | * File Name: GB32960Decoder
17 | * -----------------------------------------------------------------------------
18 | * Description:
19 | *
20 | * 处理GB32960协议的粘包、拆包问题,并负责帧的完整性校验和具体解析业务逻辑。
21 | * -----------------------------------------------------------------------------
22 | *
23 | * @author xiaonannet
24 | * @version 1.0
25 | * -----------------------------------------------------------------------------
26 | * Revision History:
27 | * Date Author Version Description
28 | * -------- -------- ------- --------------------
29 | * 2024/9/9 xiaonannet 1.0 完整代码整合和优化
30 | * -----------------------------------------------------------------------------
31 | * @email
32 | * @date 2024/9/9 12:43
33 | */
34 | @Slf4j
35 | public class GB32960Decoder extends ByteToMessageDecoder {
36 |
37 | private static final int MIN_MESSAGE_LENGTH = 24; // 最小消息长度(包括固定头部)
38 |
39 | @Override
40 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
41 | log.info("GB32960Decoder Received bytes: {}", ByteBufUtil.hexDump(in).toUpperCase());
42 |
43 | // 如果可读字节数少于最小长度,返回并等待更多数据
44 | if (in.readableBytes() < MIN_MESSAGE_LENGTH) {
45 | return;
46 | }
47 |
48 | // 标记当前读指针,以便在数据不足时回退
49 | in.markReaderIndex();
50 |
51 | // 检查起始符号是否为 0x23 0x23(不移动读指针)
52 | if (!canDecodeGB32960(in)) {
53 | log.warn("Invalid start bytes, resetting reader index");
54 | in.resetReaderIndex(); // 恢复指针位置
55 | return;
56 | }
57 |
58 | // 保存当前完整的 ByteBuf 副本,以便稍后解析
59 | ByteBuf fullFrame = in.copy(); // 使用 copy() 创建完整的副本,包含全部未读字节
60 |
61 | // 读取命令标识 (1字节) 使用 getByte 方法而不改变读指针
62 | byte commandFlag = in.getByte(in.readerIndex() + 2);
63 | log.info("Command Flag: {}", commandFlag);
64 |
65 | // 读取应答标识 (1字节) 使用 getByte
66 | byte responseFlag = in.getByte(in.readerIndex() + 3);
67 | log.info("Response Flag: {}", responseFlag);
68 |
69 | // 读取唯一识别码 (17字节) 使用 getBytes
70 | byte[] vinBytes = new byte[17];
71 | in.getBytes(in.readerIndex() + 4, vinBytes);
72 | String uniqueIdentifier = new String(vinBytes, StandardCharsets.UTF_8);
73 | log.info("Unique Identifier (VIN): {}", uniqueIdentifier);
74 |
75 | // 读取加密方式 (1字节) 使用 getByte
76 | byte encryptionFlag = in.getByte(in.readerIndex() + 21);
77 | log.info("Encryption Flag: {}", encryptionFlag);
78 |
79 | // 读取数据单元长度字段(2字节) 使用 getUnsignedShort
80 | int dataCellLength = in.getUnsignedShort(in.readerIndex() + 22);
81 | log.info("Data Cell Length: {}", dataCellLength);
82 |
83 | // 计算消息的总长度 = 固定头部 + 数据单元长度 + 校验码(1字节)
84 | int totalMessageLength = MIN_MESSAGE_LENGTH + dataCellLength + 1;
85 |
86 | // 检查是否有足够的可读数据,如果没有则等待
87 | if (in.readableBytes() < totalMessageLength) {
88 | log.warn("Not enough data, waiting for more");
89 | in.resetReaderIndex(); // 恢复读指针,等待更多数据
90 | return;
91 | }
92 |
93 | // TODO BCC验证
94 |
95 | // 将完整的消息传递给解析器
96 | GB32960MessageData messageData = GB32960MessageParser.parse(fullFrame);
97 | // 将解析后的数据传递给下一个处理器
98 | out.add(messageData);
99 | in.clear();
100 | }
101 |
102 | /**
103 | * 计算校验码(BCC),根据 GB32960 协议从命令标识开始到数据单元的最后一个字节
104 | *
105 | * @param frame 完整的 ByteBuf 帧
106 | * @return 计算出的校验码
107 | */
108 | public static byte calculateCheckCode(ByteBuf frame) {
109 | byte checkCode = 0;
110 | for (int i = 0; i < frame.readableBytes(); i++) {
111 | checkCode ^= frame.getByte(i);
112 | }
113 | return checkCode;
114 | }
115 |
116 | /**
117 | * 判断消息是否为 GB32960 协议
118 | * 通过检查前两个字节是否为 0x23 0x23
119 | */
120 | private boolean canDecodeGB32960(ByteBuf buf) {
121 | if (buf.readableBytes() < 2) {
122 | return false;
123 | }
124 |
125 | byte firstByte = buf.getByte(buf.readerIndex());
126 | byte secondByte = buf.getByte(buf.readerIndex() + 1);
127 |
128 | return firstByte == 0x23 && secondByte == 0x23;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/dispatcher/MessageDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.dispatcher;
2 |
3 | import cn.hutool.core.util.StrUtil;
4 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao.GB32960MessageData;
5 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.service.DataParseService;
6 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.service.impl.DataParseServiceImpl;
7 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.utils.SpringUtils;
8 | import io.netty.channel.ChannelHandlerContext;
9 | import io.netty.channel.SimpleChannelInboundHandler;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.springframework.stereotype.Component;
12 |
13 | /**
14 | * -----------------------------------------------------------------------------
15 | * File Name: MessageDispatcher
16 | * -----------------------------------------------------------------------------
17 | * Description:
18 | * 消息分发器,根据消息类型将消息转发到相应的处理器。
19 | *
20 | * -----------------------------------------------------------------------------
21 | *
22 | * @author xiaonannet
23 | * @version 1.0
24 | * -----------------------------------------------------------------------------
25 | * Revision History:
26 | * Date Author Version Description
27 | * -------- -------- ------- --------------------
28 | * 2024/9/8 xiaonannet 1.0 Initial creation
29 | * -----------------------------------------------------------------------------
30 | * @email
31 | * @date 2024/9/8 18:54
32 | */
33 | @Slf4j
34 | @Component
35 | public class MessageDispatcher extends SimpleChannelInboundHandler {
36 |
37 | @Override
38 | protected void channelRead0(ChannelHandlerContext ctx, GB32960MessageData msg) throws Exception {
39 | DataParseService dataParseService = SpringUtils.getBean(DataParseServiceImpl.class);
40 | String msgCommand = msg.getMsgCommand();
41 | String response = "";
42 |
43 | // 根据消息命令分发到不同的处理方法
44 | switch (msgCommand) {
45 | case "01": // 处理车辆登录消息
46 | response = dataParseService.handleVehicleLogin(msg);
47 | break;
48 | case "02": // 处理实时信息上报
49 | response = dataParseService.handleRealtimeData(msg);
50 | break;
51 | case "03": // 处理补发信息上报
52 | response = dataParseService.handleSupplementaryData(msg);
53 | break;
54 | case "04": // 处理车辆登出
55 | response = dataParseService.handleVehicleLogout(msg);
56 | break;
57 | case "05": // 处理平台登录
58 | response = dataParseService.handlePlatformLogin(msg);
59 | break;
60 | case "06": // 处理平台登出
61 | response = dataParseService.handlePlatformLogout(msg);
62 | break;
63 | case "07": // 处理心跳
64 | response = dataParseService.handleHeartbeat(msg);
65 | break;
66 | case "08": // 同步校时
67 | response = dataParseService.handleTimeSynchronization(msg);
68 | break;
69 | default:
70 | log.warn("未处理的 GB32960 消息类型: {}", msgCommand);
71 | break;
72 | }
73 |
74 | // 如果有响应数据,返回给客户端
75 | if (StrUtil.isNotBlank(response)) {
76 | ctx.writeAndFlush(response);
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/encoder/EncoderHandler.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.encoder;
2 |
3 |
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.handler.codec.MessageToByteEncoder;
7 | import lombok.extern.slf4j.Slf4j;
8 |
9 | import java.nio.charset.StandardCharsets;
10 |
11 | /**
12 | * -----------------------------------------------------------------------------
13 | * File Name: EncoderHandler
14 | * -----------------------------------------------------------------------------
15 | * Description:
16 | * 编码器,将字符串消息编码为字节流。
17 | * -----------------------------------------------------------------------------
18 | *
19 | * @author mqttsnet
20 | * @version 1.0
21 | * -----------------------------------------------------------------------------
22 | * Revision History:
23 | * Date Author Version Description
24 | * -------- -------- ------- --------------------
25 | * 2024/9/8 mqttsnet 1.0 Initial creation
26 | * -----------------------------------------------------------------------------
27 | * @email
28 | * @date 2024/9/8 17:26
29 | */
30 | @Slf4j
31 | public class EncoderHandler extends MessageToByteEncoder {
32 |
33 | @Override
34 | protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
35 | // 将字符串编码为字节并写入缓冲区
36 | byte[] msgBytes = (msg + "\n").getBytes(StandardCharsets.UTF_8); // 加上定界符(换行符)
37 | out.writeBytes(msgBytes);
38 | log.info("Encoded message: {}", msg);
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/entity/custom/CustomMessage.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.entity.custom;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * -----------------------------------------------------------------------------
7 | * File Name: CustomMessage
8 | * -----------------------------------------------------------------------------
9 | * Description:
10 | *
11 | * -----------------------------------------------------------------------------
12 | *
13 | * @author xiaonannet
14 | * @version 1.0
15 | * -----------------------------------------------------------------------------
16 | * Revision History:
17 | * Date Author Version Description
18 | * -------- -------- ------- --------------------
19 | * 2024/9/8 xiaonannet 1.0 Initial creation
20 | * -----------------------------------------------------------------------------
21 | * @email
22 | * @date 2024/9/8 19:07
23 | */
24 |
25 | @Data
26 | public class CustomMessage {
27 | private String messageType; // 消息类型
28 | private String payload; // 消息内容
29 | private long timestamp; // 消息接收时间
30 |
31 | // 构造函数
32 | public CustomMessage(String messageType, String payload) {
33 | this.messageType = messageType;
34 | this.payload = payload;
35 | this.timestamp = System.currentTimeMillis(); // 获取消息的接收时间
36 | }
37 |
38 | // Getter 和 Setter 方法
39 | public String getMessageType() {
40 | return messageType;
41 | }
42 |
43 | public void setMessageType(String messageType) {
44 | this.messageType = messageType;
45 | }
46 |
47 | public String getPayload() {
48 | return payload;
49 | }
50 |
51 | public void setPayload(String payload) {
52 | this.payload = payload;
53 | }
54 |
55 | public long getTimestamp() {
56 | return timestamp;
57 | }
58 |
59 | public void setTimestamp(long timestamp) {
60 | this.timestamp = timestamp;
61 | }
62 |
63 | @Override
64 | public String toString() {
65 | return "CustomMessage{" +
66 | "messageType='" + messageType + '\'' +
67 | ", payload='" + payload + '\'' +
68 | ", timestamp=" + timestamp +
69 | '}';
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/GB32960MessageParser.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao.GB32960MessageData;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.ByteBufUtil;
6 | import lombok.extern.slf4j.Slf4j;
7 |
8 | import java.nio.charset.StandardCharsets;
9 |
10 | /**
11 | * -----------------------------------------------------------------------------
12 | * File Name: GB32960MessageParser
13 | * -----------------------------------------------------------------------------
14 | * Description:
15 | *
16 | * GB32960MessageParser 负责将 ByteBuf 中的 GB32960 消息解析为 GB32960MessageData 对象。
17 | * -----------------------------------------------------------------------------
18 | *
19 | * @author xiaonannet
20 | * @version 1.0
21 | * -----------------------------------------------------------------------------
22 | * Revision History:
23 | * Date Author Version Description
24 | * -------- -------- ------- --------------------
25 | * 2024/9/9 xiaonannet 1.0 Initial creation
26 | * -----------------------------------------------------------------------------
27 | * @email
28 | * @date 2024/9/9 14:38
29 | */
30 | @Slf4j
31 | public class GB32960MessageParser {
32 |
33 | /**
34 | * 解析 GB32960 消息
35 | *
36 | * @param frame 包含完整 GB32960 消息的 ByteBuf
37 | * @return 解析后的 GB32960MessageData 对象
38 | */
39 | public static GB32960MessageData parse(ByteBuf frame) {
40 | GB32960MessageData messageData = new GB32960MessageData();
41 |
42 | // 读取起始符
43 | messageData.setMsgHead(ByteBufUtil.hexDump(frame.readBytes(2)));
44 |
45 | // 读取命令标识
46 | messageData.setMsgCommand(ByteBufUtil.hexDump(frame.readBytes(1)));
47 |
48 | // 读取应答标识
49 | messageData.setMsgResponse(ByteBufUtil.hexDump(frame.readBytes(1)));
50 |
51 | // 读取唯一识别码
52 | byte[] vinBytes = new byte[17];
53 | frame.readBytes(vinBytes);
54 | messageData.setUniqueIdentifier(new String(vinBytes, StandardCharsets.UTF_8));
55 |
56 | // 读取加密方式
57 | messageData.setEncryption(ByteBufUtil.hexDump(frame.readBytes(1)));
58 |
59 | // 读取数据单元长度
60 | int dataCellLength = frame.readUnsignedShort();
61 | messageData.setDataCellLength(String.valueOf(dataCellLength));
62 |
63 | // 读取数据单元
64 | byte[] dataUnit = new byte[dataCellLength];
65 | frame.readBytes(dataUnit);
66 | messageData.setData(ByteBufUtil.hexDump(dataUnit));
67 |
68 | // 读取校验码
69 | messageData.setCheckCode(ByteBufUtil.hexDump(frame.readBytes(1)));
70 |
71 | return messageData;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960Alert.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960Alert {
17 |
18 | private boolean driveMotorControllerTemperatureAlert;
19 | private boolean highVoltageInterlockStatusAlert;
20 | private boolean driveMotorTemperatureAlert;
21 | private boolean energyStorageOvercharge;
22 | private boolean soclowAlert;
23 | private boolean socjumpAlert;
24 | private boolean temperatureDifferenceAlert;
25 | private boolean cellHighTemperatureAlert;
26 | private boolean energyStorageOverVoltageAlert;
27 | private boolean energyStorageUnderVoltageAlert;
28 | private boolean brakingSystemAlert;
29 | private boolean cellOverVoltageAlert;
30 | private boolean cellUnderVoltageAlert;
31 | private boolean insulationAlert;
32 | private boolean dctemperatureAlert;
33 | private boolean energyStorageSystemNotMatchAlert;
34 | private boolean cellConsistencyDifferenceAlert;
35 | private boolean dcstatusAlert;
36 | private boolean sochighAlert;
37 |
38 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960AlertStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * @Description:
9 | * @Author: mqttsnet
10 | * @E-mail: mqttsnet@163.com
11 | * @CreateDate: 2021/11/5$ 16:33$
12 | * @UpdateUser: mqttsnet
13 | * @UpdateDate: 2021/11/5$ 16:33$
14 | * @UpdateRemark: 修改内容
15 | * @Version: 1.0
16 | */
17 | @Data
18 | public class GB32960AlertStatus {
19 |
20 | /**
21 | * 最高报警等级,为当前发生的故障中的最高等级值,有效值范围:0~3
22 | */
23 | private String highestAlertLevel;
24 |
25 | /**
26 | * 通用告警标准,标准维持到报警条件解除
27 | */
28 | private String universalAlarmIdentification;
29 | /**
30 | * 可充电储能装置故障总数N1,N1个可充电储能装置故障,有效值范围:0~252,“0xFE”表示异常,“0xFF”表示无效
31 | */
32 | private int energyStorageAlertCount;
33 | /**
34 | * 可充电储能装置故障代码列表
35 | */
36 | private List energyStorageAlertList;
37 | /**
38 | * 驱动电机,故障总数N2,N2个驱动电机故障,有效值范围:0~252,“0xFE”表示异常,“0xFF”表示无效
39 | */
40 | private int driveMotorAlertCount;
41 | /**
42 | * 驱动电机故障代码列表
43 | */
44 | private List driveMotorAlertList;
45 | /**
46 | * 发动机故障总数N3,N3个驱动电机故障,有效值范围:0~252,“0xFE”表示异常,“0xFF”表示无效
47 | */
48 | private int engineAlertCount;
49 | /**
50 | * 发动机故障列表
51 | */
52 | private List engineAlertList;
53 | /**
54 | * 其他故障总数N4,N4个其他故障
55 | */
56 | private int otherAlertCount;
57 | /**
58 | * 其他故障代码列表
59 | */
60 | private List otherAlertList;
61 | /**
62 | * 通用报警标志
63 | */
64 | private GB32960Alert alert;
65 |
66 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960BaseDTO.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 | import lombok.Data;
3 |
4 | import java.util.Date;
5 |
6 | @Data
7 | public class GB32960BaseDTO {
8 |
9 | /**
10 | *车辆VIN
11 | */
12 | private String vin;
13 | /**
14 | * 电池包编码
15 | */
16 | private String batteryPackNumbers;
17 | /**
18 | * 整车数据
19 | */
20 | private GB32960VehicleStatus vehicleStatus;
21 | /**
22 | *驱动电机数据
23 | */
24 | private GB32960DriveMotorStatus driveMotorStatus;
25 | private String fuelCellStatus;
26 | private String engineStatus;
27 | /**
28 | * 车辆位置数据
29 | */
30 | private GB32960LocationStatus locationStatus;
31 | /**
32 | * 极值数据
33 | */
34 | private GB32960ExtremeStatus extremeStatus;
35 | /**
36 | *报警数据
37 | */
38 | private GB32960AlertStatus alertStatus;
39 | private GB32960EnergyStorageVoltageStatus energyStorageVoltageStatus;
40 | private GB32960EnergyStorageTemperatureStatus energyStorageTemperatureStatus;
41 | private GB32960CustomData customData;
42 | private Date acquisitionTime;
43 | private Long dataTime;
44 | private String command;
45 |
46 |
47 |
48 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960CustomData.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960CustomData {
17 |
18 | private double energyConsumption;
19 | private int handbrakeStatus;
20 | private int highVoltagePower;
21 |
22 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960DriveMotorStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 | import lombok.Data;
3 |
4 | import java.util.List;
5 |
6 | /**
7 | * @Description:
8 | * @Author: mqttsnet
9 | * @E-mail: mqttsnet@163.com
10 | * @CreateDate: 2021/11/5$ 16:33$
11 | * @UpdateUser: mqttsnet
12 | * @UpdateDate: 2021/11/5$ 16:33$
13 | * @UpdateRemark: 修改内容
14 | * @Version: 1.0
15 | */
16 | @Data
17 | public class GB32960DriveMotorStatus {
18 |
19 | private Integer driveMotorCount;
20 | private List driveMotors;
21 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960DriveMotors.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960DriveMotors {
17 |
18 | /**
19 | * 驱动电机序号
20 | */
21 | private Integer sn;
22 | /**
23 | * 驱动电机状态
24 | */
25 | private String driveMotorPower;
26 | /**
27 | * 驱动电机控制器温度
28 | */
29 | private Integer controllerTemperature;
30 | /**
31 | * 驱动电机转速
32 | */
33 | private Integer driveMotorSpeed;
34 | /**
35 | * 驱动电机转矩
36 | */
37 | private Double driveMotorTorque;
38 | /**
39 | * 驱动电机温度
40 | */
41 | private Integer driveMotorTemperature;
42 | /**
43 | * 电机控制器输入电压
44 | */
45 | private Double controllerInputVoltage;
46 | /**
47 | * 电机控制器直流母线电流
48 | */
49 | private Double dcBusCurrentOfController;
50 |
51 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960EnergyStorageTemperatureStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 | import lombok.Data;
3 |
4 | import java.util.List;
5 |
6 | /**
7 | * @author sunshihuan
8 | * 可充电储能装置温度数据
9 | */
10 | @Data
11 | public class GB32960EnergyStorageTemperatureStatus {
12 |
13 | private int subEnergyStorageSystemCount;
14 |
15 | private List energyStorageTemperatures;
16 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960EnergyStorageTemperatures.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 | import lombok.Data;
3 |
4 | import java.util.List;
5 |
6 |
7 | @Data
8 | public class GB32960EnergyStorageTemperatures {
9 |
10 | private Integer energyStorageSubSystemIndex1;
11 | private Integer probeCount;
12 | private List cellTemperatures;
13 |
14 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960EnergyStorageVoltageStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 | import lombok.Data;
3 |
4 | import java.util.List;
5 |
6 | /**
7 | * @Description: 可充电储能装置电压数据
8 | * @Author: mqttsnet
9 | * @E-mail: mqttsnet@163.com
10 | * @CreateDate: 2021/11/5$ 16:33$
11 | * @UpdateUser: mqttsnet
12 | * @UpdateDate: 2021/11/5$ 16:33$
13 | * @UpdateRemark: 修改内容
14 | * @Version: 1.0
15 | */
16 | @Data
17 | public class GB32960EnergyStorageVoltageStatus {
18 |
19 | private Integer subSystemOfEnergyStorageCount;
20 | private List energyStorageVoltages;
21 |
22 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960EnergyStorageVoltages.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 | import lombok.Data;
3 |
4 | import java.util.List;
5 |
6 | @Data
7 | public class GB32960EnergyStorageVoltages {
8 |
9 | private Integer energyStorageSubSystemIndex;
10 | private Double energyStorageVoltage;
11 | private Double energyStorageCurrent;
12 | private Integer cellCount;
13 | private Integer frameCellStartIndex;
14 | private Integer frameCellCount;
15 | private List cellVoltages;
16 |
17 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960ExtremeStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960ExtremeStatus {
17 |
18 | /**
19 | * 最高电压电池子系统号
20 | */
21 | private Integer subSystemIndexOfMaxVoltage;
22 | /**
23 | * 最高电压电池单体代号
24 | */
25 | private Integer cellIndexOfMaxVoltage;
26 | /**
27 | * 电池单体电压最高值
28 | */
29 | private Double cellMaxVoltage;
30 | /**
31 | * 最低电压电池子系统号
32 | */
33 | private Integer subSystemIndexOfMinVoltage;
34 | /**
35 | *最低电压电池单体代号
36 | */
37 | private Integer cellIndexOfMinVoltage;
38 | /**
39 | * 电池单体电压最低值
40 | */
41 | private Double cellMinVoltage;
42 | /**
43 | * 最高温度子系统号
44 | */
45 | private Integer subSystemIndexOfMaxTemperature;
46 | /**
47 | *最高温度探针序号
48 | */
49 | private Integer probeIndexOfMaxTemperature;
50 | /**
51 | * 最高温度值
52 | */
53 | private Integer maxTemperature;
54 | /**
55 | * 最低温度子系统号
56 | */
57 | private Integer subSystemIndexOfMinTemperature;
58 | /**
59 | * 最低温度探针序号
60 | */
61 | private Integer probeIndexOfMinTemperature;
62 | /**
63 | * 最低温度值
64 | */
65 | private Integer minTemperature;
66 |
67 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960LocateStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description: 定位状态
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960LocateStatus {
17 |
18 | private String validation;
19 | private String latitudeType;
20 | private String longitudeType;
21 |
22 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960LocationStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960LocationStatus {
17 |
18 | /**
19 | * 精度
20 | */
21 | private Double longitude;
22 | /**
23 | * 维度
24 | */
25 | private Double latitude;
26 | /**
27 | * 定位状态
28 | */
29 | private GB32960LocateStatus locateStatus;
30 |
31 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960MessageData.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description: GB32960报文体数据模型
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @Website: mqttsnet
10 | * @CreateDate: 2021/11/15$ 18:37$
11 | * @UpdateUser: mqttsnet
12 | * @UpdateDate: 2021/11/15$ 18:37$
13 | * @UpdateRemark: 修改内容
14 | * @Version: 1.0
15 | */
16 | @Data
17 | public class GB32960MessageData {
18 | /**
19 | * 起始符
20 | */
21 | private String msgHead;
22 |
23 |
24 | /**
25 | * 命令标识
26 | */
27 | private String msgCommand;
28 |
29 | /**
30 | * 应答标识
31 | */
32 | private String msgResponse;
33 |
34 | /**
35 | * 唯一识别码
36 | */
37 | private String uniqueIdentifier;
38 |
39 |
40 | /**
41 | * 加密方式
42 | */
43 | private String encryption;
44 |
45 |
46 | /**
47 | * 数据单元长度
48 | */
49 | private String dataCellLength;
50 |
51 | /**
52 | * 数据单元
53 | */
54 | private String data;
55 |
56 |
57 | /**
58 | * 校验码
59 | */
60 | private String checkCode;
61 | }
62 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960TransmissionStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960TransmissionStatus {
17 |
18 | /**
19 | * 档位
20 | */
21 | private Integer gear;
22 |
23 | private boolean hasDriverForce;
24 | private boolean hasBrakingForce;
25 |
26 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/entity/dao/GB32960VehicleStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960VehicleStatus {
17 |
18 | /**
19 | * 车辆状态
20 | */
21 | private String engineStatus;
22 | /**
23 | * 运行模式
24 | */
25 | private String runningModel;
26 | /**
27 | * 充电状态
28 | */
29 | private String chargingStatus;
30 | /**
31 | * 车速
32 | */
33 | private Double speed;
34 | /**
35 | * 累计里程
36 | */
37 | private Double mileage;
38 | /**
39 | * 总电压
40 | */
41 | private Double voltage;
42 | /**
43 | * 总电流
44 | */
45 | private Double current;
46 | /**
47 | * SOC
48 | */
49 | private Integer soc;
50 | /**
51 | * DC-DC状态
52 | */
53 | private String dcStatus;
54 | /**
55 | * 档位信息
56 | */
57 | private GB32960TransmissionStatus transmissionStatus;
58 | private Integer insulationResistance;
59 | private Integer accelerationPedalTravel;
60 | private Integer brakePedalState;
61 |
62 | }
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/handler/RealTimeDataHandler.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.handler;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.service.DataParseService;
4 | import io.netty.buffer.Unpooled;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.channel.ChannelInboundHandlerAdapter;
7 | import io.netty.util.CharsetUtil;
8 | import lombok.extern.slf4j.Slf4j;
9 |
10 | /**
11 | * -----------------------------------------------------------------------------
12 | * File Name: RealTimeDataHandler
13 | * -----------------------------------------------------------------------------
14 | * Description:
15 | *
16 | * 实时数据处理器
17 | * 用于处理 GB32960 协议的实时数据。
18 | *
19 | * -----------------------------------------------------------------------------
20 | *
21 | * @author xiaonannet
22 | * @version 1.0
23 | * -----------------------------------------------------------------------------
24 | * Revision History:
25 | * Date Author Version Description
26 | * -------- -------- ------- --------------------
27 | * 2024/9/8 xiaonannet 1.0 Initial creation
28 | * -----------------------------------------------------------------------------
29 | * @email
30 | * @date 2024/9/8 18:51
31 | */
32 | @Slf4j
33 | public class RealTimeDataHandler extends ChannelInboundHandlerAdapter {
34 |
35 | private final DataParseService parseService;
36 |
37 | public RealTimeDataHandler(DataParseService parseService) {
38 | this.parseService = parseService;
39 | }
40 |
41 | @Override
42 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
43 | if (msg instanceof String) {
44 | String msgStr = (String) msg;
45 | log.info("[GB32960] Received data: {}", msgStr);
46 | parseService.realTimeDataParseAndPushData(msgStr);
47 | // 响应客户端
48 | ctx.writeAndFlush(Unpooled.copiedBuffer("RealTimeDataProcessed", CharsetUtil.UTF_8));
49 | } else {
50 | log.warn("Unsupported message type: {}", msg.getClass().getSimpleName());
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/gb32960/service/DataParseService.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.service;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.gb32960.entity.dao.GB32960MessageData;
4 |
5 | /**
6 | * @Description: GB32960数据解析服务接口。
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @Website: https://www.mqttsnet.com
10 | * @CreateDate: 2024/09/10$ 18:30$
11 | * @UpdateUser: mqttsnet
12 | * @UpdateDate: 2024/09/10$ 18:30$
13 | * @UpdateRemark: 修改内容
14 | * @Version: 1.0
15 | */
16 |
17 | public interface DataParseService {
18 |
19 | /**
20 | * 实时数据解析并返回数据
21 | *
22 | * @param readDatas
23 | */
24 | String realTimeDataParseAndPushData(String readDatas) throws Exception;
25 |
26 | /**
27 | * 处理车辆登录消息
28 | *
29 | * @param msg GB32960MessageData 消息对象
30 | * @return 处理后的响应数据字符串
31 | */
32 | String handleVehicleLogin(GB32960MessageData msg);
33 |
34 | /**
35 | * 处理实时信息上报
36 | *
37 | * @param msg GB32960MessageData 消息对象
38 | * @return 处理后的响应数据字符串
39 | */
40 | String handleRealtimeData(GB32960MessageData msg);
41 |
42 | /**
43 | * 处理补发信息上报
44 | *
45 | * @param msg GB32960MessageData 消息对象
46 | * @return 处理后的响应数据字符串
47 | */
48 | String handleSupplementaryData(GB32960MessageData msg);
49 |
50 | /**
51 | * 处理车辆登出消息
52 | *
53 | * @param msg GB32960MessageData 消息对象
54 | * @return 处理后的响应数据字符串
55 | */
56 | String handleVehicleLogout(GB32960MessageData msg);
57 |
58 | /**
59 | * 处理平台登录消息
60 | *
61 | * @param msg GB32960MessageData 消息对象
62 | * @return 处理后的响应数据字符串
63 | */
64 | String handlePlatformLogin(GB32960MessageData msg);
65 |
66 | /**
67 | * 处理平台登出消息
68 | *
69 | * @param msg GB32960MessageData 消息对象
70 | * @return 处理后的响应数据字符串
71 | */
72 | String handlePlatformLogout(GB32960MessageData msg);
73 |
74 | /**
75 | * 处理心跳消息
76 | *
77 | * @param msg GB32960MessageData 消息对象
78 | * @return 处理后的响应数据字符串
79 | */
80 | String handleHeartbeat(GB32960MessageData msg);
81 |
82 | /**
83 | * 处理同步校时消息
84 | *
85 | * @param msg GB32960MessageData 消息对象
86 | * @return 处理后的响应数据字符串
87 | */
88 | String handleTimeSynchronization(GB32960MessageData msg);
89 | }
90 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/handler/ExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.handler;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.ChannelInboundHandlerAdapter;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | /**
8 | * -----------------------------------------------------------------------------
9 | * File Name: ExceptionHandler
10 | * -----------------------------------------------------------------------------
11 | * Description:
12 | *
13 | * -----------------------------------------------------------------------------
14 | *
15 | * @author xiaonannet
16 | * @version 1.0
17 | * -----------------------------------------------------------------------------
18 | * Revision History:
19 | * Date Author Version Description
20 | * -------- -------- ------- --------------------
21 | * 2024/9/8 xiaonannet 1.0 Initial creation
22 | * -----------------------------------------------------------------------------
23 | * @email
24 | * @date 2024/9/8 18:51
25 | */
26 | @Slf4j
27 | public class ExceptionHandler extends ChannelInboundHandlerAdapter {
28 |
29 | @Override
30 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
31 | // 记录详细的错误信息
32 | log.error("Exception caught in handler for channel: {}", ctx.channel().id(), cause);
33 |
34 | // 关闭连接
35 | ctx.close();
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/handler/MyServerHandler.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.handler;
2 |
3 | import io.netty.buffer.Unpooled;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.ChannelInboundHandlerAdapter;
6 | import io.netty.util.CharsetUtil;
7 | import lombok.extern.slf4j.Slf4j;
8 |
9 | /**
10 | * 自定义业务处理器,处理TCP连接的读写事件。
11 | *
12 | * @author mqttsnet
13 | */
14 | @Slf4j
15 | @Deprecated
16 | public class MyServerHandler extends ChannelInboundHandlerAdapter {
17 |
18 | /**
19 | * 当客户端连接到服务器时,打印连接信息。
20 | */
21 | @Override
22 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
23 | String clientAddress = ctx.channel().remoteAddress().toString();
24 | log.info("客户端连接建立,地址: {}", clientAddress);
25 | super.channelActive(ctx);
26 | }
27 |
28 | /**
29 | * 处理从客户端读取到的数据,并打印日志。
30 | *
31 | * @param ctx ChannelHandlerContext上下文
32 | * @param msg 读取到的消息
33 | */
34 | @Override
35 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
36 | String clientAddress = ctx.channel().remoteAddress().toString();
37 | log.info("服务器接收到来自客户端[{}]的消息: {}", clientAddress, msg.toString());
38 |
39 | // 发送消息回客户端
40 | String response = "服务器回复: Hello!";
41 | ctx.write(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
42 | log.info("发送回复给客户端[{}]: {}", clientAddress, response);
43 | }
44 |
45 | /**
46 | * 消息读取完成时,刷新消息到客户端并打印日志。
47 | *
48 | * @param ctx ChannelHandlerContext上下文
49 | */
50 | @Override
51 | public void channelReadComplete(ChannelHandlerContext ctx) {
52 | ctx.flush();
53 | String clientAddress = ctx.channel().remoteAddress().toString();
54 | log.info("消息已发送完毕,客户端[{}]", clientAddress);
55 | }
56 |
57 | /**
58 | * 当客户端断开连接时,打印断开信息。
59 | *
60 | * @param ctx ChannelHandlerContext上下文
61 | */
62 | @Override
63 | public void channelInactive(ChannelHandlerContext ctx) throws Exception {
64 | String clientAddress = ctx.channel().remoteAddress().toString();
65 | log.info("客户端连接断开,地址: {}", clientAddress);
66 | super.channelInactive(ctx);
67 | }
68 |
69 | /**
70 | * 捕获异常并关闭连接,同时打印异常日志。
71 | *
72 | * @param ctx ChannelHandlerContext上下文
73 | * @param cause 异常信息
74 | */
75 | @Override
76 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
77 | String clientAddress = ctx.channel().remoteAddress().toString();
78 | log.error("发生异常,关闭客户端连接[{}]", clientAddress, cause);
79 | ctx.close();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/initializer/MyServerInitializer.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.initializer;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.decoder.GB32960Decoder;
4 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.dispatcher.MessageDispatcher;
5 | import com.mqttsnet.thinglinks.open.exp.example.tcpserver.handler.ExceptionHandler;
6 | import io.netty.channel.ChannelInitializer;
7 | import io.netty.channel.ChannelPipeline;
8 | import io.netty.channel.socket.SocketChannel;
9 | import io.netty.handler.logging.LogLevel;
10 | import io.netty.handler.logging.LoggingHandler;
11 | import io.netty.handler.timeout.IdleStateHandler;
12 | import lombok.extern.slf4j.Slf4j;
13 | import org.springframework.stereotype.Component;
14 |
15 |
16 | /**
17 | * -----------------------------------------------------------------------------
18 | * File Name: MyServerInitializer
19 | * -----------------------------------------------------------------------------
20 | * Description:
21 | *
22 | * 初始化服务器通道,配置管道中各个处理器。
23 | * 采用定界符方案来解决 TCP 粘包和拆包问题。
24 | *
25 | * -----------------------------------------------------------------------------
26 | *
27 | * @author xiaonannet
28 | * @version 1.0
29 | * -----------------------------------------------------------------------------
30 | * Revision History:
31 | * Date Author Version Description
32 | * -------- -------- ------- --------------------
33 | * 2024/9/8 xiaonannet 1.0 Initial creation
34 | * -----------------------------------------------------------------------------
35 | * @email
36 | * @date 2024/9/8 18:48
37 | */
38 |
39 | @Slf4j
40 | @Component
41 | public class MyServerInitializer extends ChannelInitializer {
42 |
43 | @Override
44 | protected void initChannel(SocketChannel ch) throws Exception {
45 | ChannelPipeline pipeline = ch.pipeline();
46 |
47 | log.info("Initializing channel for client: {}", ch.remoteAddress());
48 |
49 | // 添加日志处理器,用于记录入站和出站事件
50 | pipeline.addLast(new LoggingHandler(LogLevel.INFO));
51 |
52 | // 添加空闲检测处理器,检测是否有读写超时
53 | pipeline.addLast(new IdleStateHandler(60, 0, 0));
54 |
55 | // 这里也可以使用 LengthFieldBasedFrameDecoder 来解决粘包和拆包问题(GB32960Decoder 中也处理了这里就可以不启用)
56 | /*pipeline.addLast(
57 | new LengthFieldBasedFrameDecoder(64 * 1024, 22, 2, 1, 2)
58 | );*/
59 |
60 |
61 | // 添加 GB32960 协议解码器,解决粘包和拆包问题
62 | pipeline.addLast(new GB32960Decoder());
63 |
64 | // 添加业务逻辑处理器,处理解码后的消息
65 | pipeline.addLast(new MessageDispatcher());
66 |
67 | // 添加异常处理器
68 | pipeline.addLast(new ExceptionHandler());
69 | }
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/utils/Sm4Utils.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.utils;
2 |
3 | import org.bouncycastle.crypto.engines.SM4Engine;
4 | import org.bouncycastle.crypto.modes.CBCBlockCipher;
5 | import org.bouncycastle.crypto.paddings.PKCS7Padding;
6 | import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
7 | import org.bouncycastle.crypto.params.KeyParameter;
8 | import org.bouncycastle.crypto.params.ParametersWithIV;
9 | import org.bouncycastle.util.encoders.Hex;
10 |
11 | import java.nio.charset.StandardCharsets;
12 |
13 | /**
14 | * @Description: 国密SM4分组密码算法工具类(对称加密)
15 | * @Author: mqttsnet
16 | * @CreateDate: 2021/8/25$ 15:27$
17 | * @UpdateUser: mqttsnet
18 | * @UpdateDate: 2021/8/25$ 15:27$
19 | * @UpdateRemark: 修改内容
20 | * @Version: 1.0
21 | */
22 | public class Sm4Utils {
23 | /**
24 | * 加密
25 | *
26 | * @param key 密钥
27 | * @param iv 初始向量
28 | * @param data 明文
29 | * @return 密文
30 | */
31 | public static String encrypt(String key, String iv, String data) {
32 | try {
33 | // 创建SM4引擎
34 | SM4Engine sm4Engine = new SM4Engine();
35 | // 创建CBC模式的加密器
36 | CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(sm4Engine);
37 | // 创建填充加密器
38 | PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbcBlockCipher, new PKCS7Padding());
39 | // 创建密钥参数
40 | KeyParameter keyParameter = new KeyParameter(Hex.decode(key));
41 | // 创建带IV的参数
42 | ParametersWithIV parametersWithIV = new ParametersWithIV(keyParameter, Hex.decode(iv));
43 | // 初始化加密器
44 | cipher.init(true, parametersWithIV);
45 |
46 | byte[] encryptedData = new byte[cipher.getOutputSize(data.getBytes(StandardCharsets.UTF_8).length)];
47 | int length = cipher.processBytes(data.getBytes(StandardCharsets.UTF_8), 0, data.getBytes(StandardCharsets.UTF_8).length, encryptedData, 0);
48 | cipher.doFinal(encryptedData, length);
49 |
50 | return Hex.toHexString(encryptedData);
51 | } catch (Exception e) {
52 | throw new RuntimeException("SM4加密失败", e);
53 | }
54 | }
55 |
56 | /**
57 | * 解密
58 | *
59 | * @param key 密钥
60 | * @param iv 初始向量
61 | * @param data 密文
62 | * @return 明文
63 | */
64 | public static String decrypt(String key, String iv, String data) {
65 | try {
66 | // 创建SM4引擎
67 | SM4Engine sm4Engine = new SM4Engine();
68 | // 创建CBC模式的解密器
69 | CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(sm4Engine);
70 | // 创建填充解密器
71 | PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbcBlockCipher, new PKCS7Padding());
72 | // 创建密钥参数
73 | KeyParameter keyParameter = new KeyParameter(Hex.decode(key));
74 | // 创建带IV的参数
75 | ParametersWithIV parametersWithIV = new ParametersWithIV(keyParameter, Hex.decode(iv));
76 | // 初始化解密器
77 | cipher.init(false, parametersWithIV);
78 |
79 | byte[] decryptedData = new byte[cipher.getOutputSize(Hex.decode(data).length)];
80 | int length = cipher.processBytes(Hex.decode(data), 0, Hex.decode(data).length, decryptedData, 0);
81 | int finalLength = cipher.doFinal(decryptedData, length);
82 |
83 | return new String(decryptedData, 0, length + finalLength, StandardCharsets.UTF_8);
84 | } catch (Exception e) {
85 | throw new RuntimeException("SM4解密失败", e);
86 | }
87 | }
88 |
89 | public static void main(String[] args) {
90 | String key = "0123456789abcdef0123456789abcdef";
91 | String iv = "0123456789abcdef0123456789abcdef";
92 | String data = "Hello, SM4!";
93 |
94 | String encryptedData = Sm4Utils.encrypt(key, iv, data);
95 | System.out.println("加密后的数据: " + encryptedData);
96 |
97 | String decryptedData = Sm4Utils.decrypt(key, iv, encryptedData);
98 | System.out.println("解密后的数据: " + decryptedData);
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/utils/SpringUtils.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.utils;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.context.ApplicationContext;
5 | import org.springframework.stereotype.Component;
6 | import org.springframework.util.Assert;
7 |
8 | import java.util.Map;
9 |
10 | /**
11 | * Spring工具类
12 | *
13 | * @author mqttsnet
14 | * @date 2017-12-25 16:27
15 | */
16 | @Component
17 | public final class SpringUtils {
18 | private static ApplicationContext applicationContext;
19 | private static ApplicationContext parentApplicationContext;
20 |
21 | private SpringUtils() {
22 | }
23 |
24 | /**
25 | * 单例Holder模式: 优点:将懒加载和线程安全完美结合的一种方式(无锁)。(推荐)
26 | *
27 | * @return 实实例
28 | */
29 | public static SpringUtils getInstance() {
30 | return SpringUtilsHolder.INSTANCE;
31 | }
32 |
33 | public static ApplicationContext getApplicationContext() {
34 | return applicationContext;
35 | }
36 |
37 | @Autowired
38 | public void setApplicationContext(ApplicationContext ctx) {
39 | Assert.notNull(ctx, "SpringUtil injection ApplicationContext is null");
40 | applicationContext = ctx;
41 | parentApplicationContext = ctx.getParent();
42 | }
43 |
44 | public static Object getBean(String name) {
45 | Assert.hasText(name, "SpringUtil name is null or empty");
46 | try {
47 | return applicationContext.getBean(name);
48 | } catch (Exception e) {
49 | return parentApplicationContext.getBean(name);
50 | }
51 | }
52 |
53 | public static T getBean(String name, Class type) {
54 | Assert.hasText(name, "SpringUtil name is null or empty");
55 | Assert.notNull(type, "SpringUtil type is null");
56 | try {
57 | return applicationContext.getBean(name, type);
58 | } catch (Exception e) {
59 | return parentApplicationContext.getBean(name, type);
60 | }
61 | }
62 |
63 | public static T getBean(Class type) {
64 | Assert.notNull(type, "SpringUtil type is null");
65 | try {
66 | return applicationContext.getBean(type);
67 | } catch (Exception e) {
68 | return parentApplicationContext.getBean(type);
69 | }
70 | }
71 |
72 | public static Map getBeansOfType(Class type) {
73 | Assert.notNull(type, "SpringUtil type is null");
74 | try {
75 | return applicationContext.getBeansOfType(type);
76 | } catch (Exception e) {
77 | return parentApplicationContext.getBeansOfType(type);
78 | }
79 | }
80 |
81 | public static ApplicationContext publishEvent(Object event) {
82 | applicationContext.publishEvent(event);
83 | return applicationContext;
84 | }
85 |
86 |
87 | /**
88 | *
89 | * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
90 | * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
91 | */
92 | private static class SpringUtilsHolder {
93 | /**
94 | * 静态初始化器,由JVM来保证线程安全
95 | */
96 | private static final SpringUtils INSTANCE = new SpringUtils();
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/utils/SubStringUtil.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.utils;
2 |
3 | /**
4 | * @Description: 字符串工具类-智能截取
5 | * @Author: mqttsnet
6 | * @E-mail: mqttsnet@163.com
7 | * @CreateDate: 2021/11/15$ 19:03$
8 | * @UpdateUser: mqttsnet
9 | * @UpdateDate: 2021/11/15$ 19:03$
10 | * @UpdateRemark: 修改内容
11 | * @Version: 1.0
12 | */
13 | public class SubStringUtil {
14 | /**
15 | * 从头开始截取
16 | *
17 | * @param str 字符串
18 | * @param end 结束位置
19 | * @return
20 | */
21 | public static String subStrStart(String str, int end){
22 | return subStr(str, 0, end);
23 | }
24 |
25 | /**
26 | * 从尾开始截取
27 | *
28 | * @param str 字符串
29 | * @param start 开始位置
30 | * @return
31 | */
32 | public static String subStrEnd(String str, int start){
33 | return subStr(str, str.length()-start, str.length());
34 | }
35 |
36 | /**
37 | * 截取字符串 (支持正向、反向截取)
38 | *
39 | * @param str 待截取的字符串
40 | * @param length 长度 ,>=0时,从头开始向后截取length长度的字符串;<0时,从尾开始向前截取length长度的字符串
41 | * @return 返回截取的字符串
42 | * @throws RuntimeException
43 | */
44 | public static String subStr(String str, int length) throws RuntimeException{
45 | if(str==null){
46 | throw new NullPointerException("字符串为null");
47 | }
48 | int len = str.length();
49 | if(len=0){
53 | return subStr(str, 0,length);
54 | }else{
55 | return subStr(str, len-Math.abs(length), len);
56 | }
57 | }
58 |
59 |
60 | /**
61 | * 截取字符串 (支持正向、反向选择)
62 | *
63 | * @param str 待截取的字符串
64 | * @param start 起始索引 ,>=0时,从start开始截取;<0时,从length-|start|开始截取
65 | * @param end 结束索引 ,>=0时,从end结束截取;<0时,从length-|end|结束截取
66 | * @return 返回截取的字符串
67 | * @throws RuntimeException
68 | */
69 | public static String subStr(String str, int start, int end) throws RuntimeException{
70 | if(str==null){
71 | throw new NullPointerException("");
72 | }
73 | int len = str.length();
74 | int s = 0;//记录起始索引
75 | int e = 0;//记录结尾索引
76 | if(len =0
83 | s = start;
84 | }
85 | if(len =0
92 | e = end;
93 | }
94 | if(e0){
118 | sb.delete(sb.length()-splitStr.length(), sb.length());
119 | }
120 | return sb.toString();
121 | }
122 | return null;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/utils/bytes/ByteCastUtil.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver.utils.bytes;
2 |
3 | import io.netty.buffer.ByteBuf;
4 |
5 | /**
6 | * @author mqttsnet
7 | * @description:
8 | * @date 2021/11/30 11:15
9 | */
10 | public class ByteCastUtil {
11 | //byte[]——>hexString
12 | public static String byteHexString(byte[] b) {
13 | String a = "";
14 | for (int i = 0; i < b.length; i++) {
15 | String hex = Integer.toHexString(b[i] & 0xFF);
16 | if (hex.length() == 1) {
17 | hex = '0' + hex;
18 | }
19 | a = a + hex;
20 | }
21 | return a;
22 | }
23 |
24 | //byte——>hexString
25 | public static String byte2HexString(byte b) {
26 | String hex = Integer.toHexString(b & 0xFF);
27 | if (hex.length() == 1) {
28 | hex = '0' + hex;
29 | }
30 | return hex;
31 | }
32 |
33 | //ByteBuf——>String
34 | public static String byteBuf2String(ByteBuf buf) {
35 | String str;
36 | if (buf.hasArray()) { // 处理堆缓冲区
37 | str = new String(buf.array(), buf.arrayOffset() + buf.readerIndex(), buf.readableBytes());
38 | } else { // 处理直接缓冲区以及复合缓冲区
39 | byte[] bytes = new byte[buf.readableBytes()];
40 | buf.getBytes(buf.readerIndex(), bytes);
41 | str = new String(bytes, 0, buf.readableBytes());
42 | }
43 | return str;
44 | }
45 |
46 |
47 | //byte[]——>String
48 | public static String bytesToHexString(byte[] src) {
49 | StringBuilder stringBuilder = new StringBuilder("");
50 | if (src == null || src.length <= 0) {
51 | return null;
52 | }
53 | for (int i = 0; i < src.length; i++) {
54 | int v = src[i] & 0xFF;
55 | String hv = Integer.toHexString(v);
56 | if (hv.length() < 2) {
57 | stringBuilder.append(0);
58 | }
59 | stringBuilder.append(hv);
60 | }
61 | return stringBuilder.toString();
62 | }
63 |
64 | //HexString——>byte[]
65 | public static byte[] hexStringToBytes(String hexString) {
66 | if (hexString == null || hexString.equals("")) {
67 | return null;
68 | }
69 | hexString = hexString.toUpperCase();
70 | int length = hexString.length() / 2;
71 | char[] hexChars = hexString.toCharArray();
72 | byte[] d = new byte[length];
73 | for (int i = 0; i < length; i++) {
74 | int pos = i * 2;
75 | d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
76 | }
77 | return d;
78 | }
79 |
80 | /**
81 | * 字符串转换为Ascii
82 | *
83 | * @param hexStr
84 | * @return
85 | */
86 | public static String hexToAscii(String hexStr) {
87 | StringBuilder output = new StringBuilder("");
88 | for (int i = 0; i < hexStr.length(); i += 2) {
89 | String str = hexStr.substring(i, i + 2);
90 | output.append((char) Integer.parseInt(str, 16));
91 | }
92 | return output.toString();
93 | }
94 |
95 | /**
96 | * Ascii转换为字符串
97 | *
98 | * @param asciiValue
99 | * @return
100 | */
101 |
102 | public static String asciiToString(String asciiValue) {
103 | StringBuffer sbu = new StringBuffer();
104 |
105 | String[] chars = asciiValue.split(",");
106 |
107 | for (int i = 0; i < chars.length; i++) {
108 | sbu.append((char) Integer.parseInt(chars[i]));
109 |
110 | }
111 |
112 | return sbu.toString();
113 |
114 | }
115 |
116 | private static byte charToByte(char c) {
117 | return (byte) "0123456789ABCDEF".indexOf(c);
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/resources/extension.properties:
--------------------------------------------------------------------------------
1 | com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService=com.mqttsnet.thinglinks.open.exp.example.tcpserver.MyUserServicePluginImpl
--------------------------------------------------------------------------------
/example-plugin-tcpserver/src/main/resources/pluginMeta.properties:
--------------------------------------------------------------------------------
1 | # plugin boot class
2 | plugin.boot.class=com.mqttsnet.thinglinks.open.exp.example.tcpserver.Boot
3 | # plugin code Cannot be null
4 | plugin.code=example.plugin.tcpserver
5 | # description
6 | plugin.desc=this a plugin a v1 demo
7 | # version
8 | plugin.version=1.0.0
9 | # extension
10 | plugin.ext=null
11 | # Classloading mode(parent-first || self-first), default: parent-first
12 | #plugin.classLoader.mode=self-first
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 |
3 | ## 说明
4 | 插件作为一个软网关,将TCP协议的数据转换为MQTT协议的数据,然后通过MQTT协议进行发布。
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/Boot.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.client.ConfigSupport;
4 | import com.mqttsnet.thinglinks.open.exp.plugin.depend.AbstractBoot;
5 |
6 | /**
7 | * 插件核心配置文件
8 | * 配置项初始化规范:
9 | * 1. 所有静态配置字段必须通过静态初始化块显式定义初始化顺序
10 | * 2. 禁止在字段声明时直接进行多级嵌套初始化(如 new A(new B()))
11 | * 3. 跨字段依赖必须满足:
12 | * - 被依赖字段在前序初始化块完成赋值
13 | * - 取值时必须通过已赋值的变量引用
14 | *
15 | * @author mqttsnet
16 | */
17 | public class Boot extends AbstractBoot {
18 |
19 | /**
20 | * 租户ID
21 | */
22 | public static ConfigSupport tenantId = new ConfigSupport("plugin.tenantId", "1");
23 |
24 | /**
25 | * TCP Server 启动的端口号
26 | */
27 | public static ConfigSupport tcpPort = new ConfigSupport("tcp.port", "50110");
28 |
29 | /**
30 | * Netty Worker线程数
31 | */
32 | public static ConfigSupport workerThreads = new ConfigSupport("netty.workerThreads", "4");
33 |
34 | /**
35 | * SO_BACKLOG: 全连接队列的最大长度
36 | */
37 | public static ConfigSupport soBacklog = new ConfigSupport("netty.soBacklog", "128");
38 |
39 | /**
40 | * TCP_NODELAY: 是否开启Nagle算法
41 | */
42 | public static ConfigSupport tcpNoDelay = new ConfigSupport("netty.tcpNoDelay", "true");
43 |
44 | /**
45 | * SO_KEEPALIVE: 是否开启TCP KeepAlive
46 | */
47 | public static ConfigSupport soKeepAlive = new ConfigSupport("netty.soKeepAlive", "true");
48 |
49 |
50 | // ------------------------------MQTT 相关配置-------------------------------------------------
51 |
52 | /**
53 | * mqtt broker Server Url
54 | * tcp://ip:port
55 | */
56 | public static ConfigSupport mqttBrokerServerUrl = new ConfigSupport("mqtt.broker.serverUrl", "tcp://broker.thinglinks.mqttsnet.com:11883");
57 |
58 | /**
59 | * 客户端ID
60 | * 软网关
61 | */
62 | public static ConfigSupport mqttClientClientId = new ConfigSupport("mqtt.client.clientId", "3653578716192768@483305815051076198");
63 |
64 |
65 | /**
66 | * 设备唯一标识
67 | * 软网关
68 | */
69 | public static ConfigSupport mqttClientDeviceIdentification = new ConfigSupport("mqtt.client.deviceIdentification", "3653578720387072");
70 |
71 | /**
72 | * 客户端用户名
73 | * 软网关
74 | */
75 | public static ConfigSupport mqttClientUsername = new ConfigSupport("mqtt.client.userName", "123456");
76 |
77 | /**
78 | * 客户端密码
79 | * 软网关
80 | */
81 | public static ConfigSupport mqttClientPassword = new ConfigSupport("mqtt.client.password", "123456");
82 |
83 | /**
84 | * 订阅的 topic
85 | * ThingLinks 命名下发Topic (云 ——》端)
86 | */
87 | public static ConfigSupport mqttClientCommandTopic = new ConfigSupport("mqtt.client.command.topic", "/v1/devices/3653578720387072/command");
88 |
89 | /**
90 | * 发布的 topic
91 | * ThingLinks 数据上报 Topic (云 ——》端)
92 | */
93 | public static ConfigSupport mqttClientDatasTopic = new ConfigSupport("mqtt.client.datas.topic", "/v1/devices/3653578720387072/datas");
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/MyUserServicePluginImpl.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * @author cxs
9 | */
10 | @Slf4j
11 | @Component
12 | public class MyUserServicePluginImpl implements UserService {
13 |
14 | @Override
15 | public void createUserExt() {
16 | log.info("create user ext");
17 | }
18 |
19 | @Override
20 | public String getName() {
21 | return MyUserServicePluginImpl.class.getName();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/custom/CustomMessage.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.custom;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * -----------------------------------------------------------------------------
7 | * File Name: CustomMessage
8 | * -----------------------------------------------------------------------------
9 | * Description:
10 | *
11 | * -----------------------------------------------------------------------------
12 | *
13 | * @author xiaonannet
14 | * @version 1.0
15 | * -----------------------------------------------------------------------------
16 | * Revision History:
17 | * Date Author Version Description
18 | * -------- -------- ------- --------------------
19 | * 2024/9/8 xiaonannet 1.0 Initial creation
20 | * -----------------------------------------------------------------------------
21 | * @email
22 | * @date 2024/9/8 19:07
23 | */
24 |
25 | @Data
26 | public class CustomMessage {
27 | private String messageType; // 消息类型
28 | private String payload; // 消息内容
29 | private long timestamp; // 消息接收时间
30 |
31 | // 构造函数
32 | public CustomMessage(String messageType, String payload) {
33 | this.messageType = messageType;
34 | this.payload = payload;
35 | this.timestamp = System.currentTimeMillis(); // 获取消息的接收时间
36 | }
37 |
38 | // Getter 和 Setter 方法
39 | public String getMessageType() {
40 | return messageType;
41 | }
42 |
43 | public void setMessageType(String messageType) {
44 | this.messageType = messageType;
45 | }
46 |
47 | public String getPayload() {
48 | return payload;
49 | }
50 |
51 | public void setPayload(String payload) {
52 | this.payload = payload;
53 | }
54 |
55 | public long getTimestamp() {
56 | return timestamp;
57 | }
58 |
59 | public void setTimestamp(long timestamp) {
60 | this.timestamp = timestamp;
61 | }
62 |
63 | @Override
64 | public String toString() {
65 | return "CustomMessage{" +
66 | "messageType='" + messageType + '\'' +
67 | ", payload='" + payload + '\'' +
68 | ", timestamp=" + timestamp +
69 | '}';
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/decoder/BaseProtocolDecoder.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.decoder;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.ChannelInboundHandlerAdapter;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | /**
8 | * -----------------------------------------------------------------------------
9 | * File Name: BaseProtocolDecoder
10 | * -----------------------------------------------------------------------------
11 | * Description: 基础协议解析处理器
12 | *
13 | * -----------------------------------------------------------------------------
14 | *
15 | * @author xiaonannet
16 | * @version 1.0
17 | * -----------------------------------------------------------------------------
18 | * Revision History:
19 | * Date Author Version Description
20 | * -------- -------- ------- --------------------
21 | * 2024/9/8 xiaonannet 1.0 Initial creation
22 | * -----------------------------------------------------------------------------
23 | * @email
24 | * @date 2024/9/8 18:49
25 | */
26 | @Slf4j
27 | public abstract class BaseProtocolDecoder extends ChannelInboundHandlerAdapter {
28 |
29 | /**
30 | * 判断消息是否可以被当前解码器解码
31 | *
32 | * @param msg 消息对象
33 | * @return true 表示可以解码,false 表示不支持
34 | */
35 | protected abstract boolean canDecode(Object msg);
36 |
37 | /**
38 | * 具体的解码逻辑
39 | *
40 | * @param msg 消息对象
41 | * @return 解码后的结果
42 | * @throws Exception 解析异常
43 | */
44 | protected abstract Object decode(Object msg) throws Exception;
45 |
46 |
47 | @Override
48 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
49 | if (canDecode(msg)) {
50 | Object decodedMessage = decode(msg);
51 | if (decodedMessage != null) {
52 | ctx.fireChannelRead(decodedMessage);
53 | } else {
54 | log.warn("Decoding failed, message is null.");
55 | }
56 | } else {
57 | log.warn("Cannot decode message, passing to the next handler.");
58 | ctx.fireChannelRead(msg); // 如果不能解码,则传递给下一个处理器
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/decoder/CustomProtocolDecoder.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.decoder;
2 |
3 | import java.nio.charset.StandardCharsets;
4 | import java.util.List;
5 |
6 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.custom.CustomMessage;
7 | import io.netty.buffer.ByteBuf;
8 | import io.netty.channel.ChannelHandlerContext;
9 | import io.netty.handler.codec.ByteToMessageDecoder;
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | /**
13 | * -----------------------------------------------------------------------------
14 | * File Name: CustomProtocolDecoder
15 | * -----------------------------------------------------------------------------
16 | * Description:
17 | * 自定义协议解析器
18 | * -----------------------------------------------------------------------------
19 | *
20 | * @author xiaonannet
21 | * @version 1.0
22 | * -----------------------------------------------------------------------------
23 | * Revision History:
24 | * Date Author Version Description
25 | * -------- -------- ------- --------------------
26 | * 2024/9/8 xiaonannet 1.0 Initial creation
27 | * -----------------------------------------------------------------------------
28 | * @email
29 | * @date 2024/9/8 18:50
30 | */
31 | @Slf4j
32 | public class CustomProtocolDecoder extends ByteToMessageDecoder {
33 |
34 | private static final String DELIMITER = "\n"; // 使用换行符作为定界符
35 |
36 | @Override
37 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
38 | // 检查是否有数据可读取
39 | if (in.readableBytes() <= 0) {
40 | return;
41 | }
42 |
43 | // 将 ByteBuf 转换为字符串
44 | String decodedData = in.toString(StandardCharsets.UTF_8);
45 |
46 | // 根据定界符拆分消息
47 | String[] messages = decodedData.split(DELIMITER);
48 |
49 | for (String message : messages) {
50 | if (message.isEmpty()) {
51 | continue;
52 | }
53 |
54 | // 处理每个消息,这里可以根据实际协议进行解析
55 | log.info("Decoded custom protocol message: {}", message);
56 |
57 | // 将解析后的消息传递给下一个处理器
58 | out.add(parseCustomMessage(message));
59 | }
60 | }
61 |
62 | // 自定义消息的解析逻辑
63 | private CustomMessage parseCustomMessage(String message) {
64 | // 假设消息格式为: "messageType:payload"
65 | String[] parts = message.split(":");
66 |
67 | if (parts.length == 2) {
68 | String messageType = parts[0].trim();
69 | String payload = parts[1].trim();
70 |
71 | log.info("Parsed custom message - Type: {}, Payload: {}", messageType, payload);
72 |
73 | // 创建并返回 CustomMessage 对象
74 | return new CustomMessage(messageType, payload);
75 | } else {
76 | log.warn("Received invalid message format: {}", message);
77 | return null; // 如果消息格式不正确,返回null或者抛出异常
78 | }
79 | }
80 |
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/decoder/GB32960Decoder.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.decoder;
2 |
3 | import java.nio.charset.StandardCharsets;
4 | import java.util.List;
5 |
6 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.GB32960MessageParser;
7 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao.GB32960MessageData;
8 | import io.netty.buffer.ByteBuf;
9 | import io.netty.buffer.ByteBufUtil;
10 | import io.netty.channel.ChannelHandlerContext;
11 | import io.netty.handler.codec.ByteToMessageDecoder;
12 | import lombok.extern.slf4j.Slf4j;
13 |
14 | /**
15 | * -----------------------------------------------------------------------------
16 | * File Name: GB32960Decoder
17 | * -----------------------------------------------------------------------------
18 | * Description:
19 | *
20 | * 处理GB32960协议的粘包、拆包问题,并负责帧的完整性校验和具体解析业务逻辑。
21 | * -----------------------------------------------------------------------------
22 | *
23 | * @author xiaonannet
24 | * @version 1.0
25 | * -----------------------------------------------------------------------------
26 | * Revision History:
27 | * Date Author Version Description
28 | * -------- -------- ------- --------------------
29 | * 2024/9/9 xiaonannet 1.0 完整代码整合和优化
30 | * -----------------------------------------------------------------------------
31 | * @email
32 | * @date 2024/9/9 12:43
33 | */
34 | @Slf4j
35 | public class GB32960Decoder extends ByteToMessageDecoder {
36 |
37 | private static final int MIN_MESSAGE_LENGTH = 24; // 最小消息长度(包括固定头部)
38 |
39 | @Override
40 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
41 | log.info("GB32960Decoder Received bytes: {}", ByteBufUtil.hexDump(in).toUpperCase());
42 |
43 | // 如果可读字节数少于最小长度,返回并等待更多数据
44 | if (in.readableBytes() < MIN_MESSAGE_LENGTH) {
45 | return;
46 | }
47 |
48 | // 标记当前读指针,以便在数据不足时回退
49 | in.markReaderIndex();
50 |
51 | // 检查起始符号是否为 0x23 0x23(不移动读指针)
52 | if (!canDecodeGB32960(in)) {
53 | log.warn("Invalid start bytes, resetting reader index");
54 | in.resetReaderIndex(); // 恢复指针位置
55 | return;
56 | }
57 |
58 | // 保存当前完整的 ByteBuf 副本,以便稍后解析
59 | ByteBuf fullFrame = in.copy(); // 使用 copy() 创建完整的副本,包含全部未读字节
60 |
61 | // 读取命令标识 (1字节) 使用 getByte 方法而不改变读指针
62 | byte commandFlag = in.getByte(in.readerIndex() + 2);
63 | log.info("Command Flag: {}", commandFlag);
64 |
65 | // 读取应答标识 (1字节) 使用 getByte
66 | byte responseFlag = in.getByte(in.readerIndex() + 3);
67 | log.info("Response Flag: {}", responseFlag);
68 |
69 | // 读取唯一识别码 (17字节) 使用 getBytes
70 | byte[] vinBytes = new byte[17];
71 | in.getBytes(in.readerIndex() + 4, vinBytes);
72 | String uniqueIdentifier = new String(vinBytes, StandardCharsets.UTF_8);
73 | log.info("Unique Identifier (VIN): {}", uniqueIdentifier);
74 |
75 | // 读取加密方式 (1字节) 使用 getByte
76 | byte encryptionFlag = in.getByte(in.readerIndex() + 21);
77 | log.info("Encryption Flag: {}", encryptionFlag);
78 |
79 | // 读取数据单元长度字段(2字节) 使用 getUnsignedShort
80 | int dataCellLength = in.getUnsignedShort(in.readerIndex() + 22);
81 | log.info("Data Cell Length: {}", dataCellLength);
82 |
83 | // 计算消息的总长度 = 固定头部 + 数据单元长度 + 校验码(1字节)
84 | int totalMessageLength = MIN_MESSAGE_LENGTH + dataCellLength + 1;
85 |
86 | // 检查是否有足够的可读数据,如果没有则等待
87 | if (in.readableBytes() < totalMessageLength) {
88 | log.warn("Not enough data, waiting for more");
89 | in.resetReaderIndex(); // 恢复读指针,等待更多数据
90 | return;
91 | }
92 |
93 | // TODO BCC验证
94 |
95 | // 将完整的消息传递给解析器
96 | GB32960MessageData messageData = GB32960MessageParser.parse(fullFrame);
97 | // 将解析后的数据传递给下一个处理器
98 | out.add(messageData);
99 | in.clear();
100 | }
101 |
102 | /**
103 | * 计算校验码(BCC),根据 GB32960 协议从命令标识开始到数据单元的最后一个字节
104 | *
105 | * @param frame 完整的 ByteBuf 帧
106 | * @return 计算出的校验码
107 | */
108 | public static byte calculateCheckCode(ByteBuf frame) {
109 | byte checkCode = 0;
110 | for (int i = 0; i < frame.readableBytes(); i++) {
111 | checkCode ^= frame.getByte(i);
112 | }
113 | return checkCode;
114 | }
115 |
116 | /**
117 | * 判断消息是否为 GB32960 协议
118 | * 通过检查前两个字节是否为 0x23 0x23
119 | */
120 | private boolean canDecodeGB32960(ByteBuf buf) {
121 | if (buf.readableBytes() < 2) {
122 | return false;
123 | }
124 |
125 | byte firstByte = buf.getByte(buf.readerIndex());
126 | byte secondByte = buf.getByte(buf.readerIndex() + 1);
127 |
128 | return firstByte == 0x23 && secondByte == 0x23;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/encoder/EncoderHandler.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.encoder;
2 |
3 |
4 | import java.nio.charset.StandardCharsets;
5 |
6 | import io.netty.buffer.ByteBuf;
7 | import io.netty.channel.ChannelHandlerContext;
8 | import io.netty.handler.codec.MessageToByteEncoder;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | /**
12 | * -----------------------------------------------------------------------------
13 | * File Name: EncoderHandler
14 | * -----------------------------------------------------------------------------
15 | * Description:
16 | * 编码器,将字符串消息编码为字节流。
17 | * -----------------------------------------------------------------------------
18 | *
19 | * @author mqttsnet
20 | * @version 1.0
21 | * -----------------------------------------------------------------------------
22 | * Revision History:
23 | * Date Author Version Description
24 | * -------- -------- ------- --------------------
25 | * 2024/9/8 mqttsnet 1.0 Initial creation
26 | * -----------------------------------------------------------------------------
27 | * @email
28 | * @date 2024/9/8 17:26
29 | */
30 | @Slf4j
31 | public class EncoderHandler extends MessageToByteEncoder {
32 |
33 | @Override
34 | protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
35 | // 将字符串编码为字节并写入缓冲区
36 | byte[] msgBytes = (msg + "\n").getBytes(StandardCharsets.UTF_8); // 加上定界符(换行符)
37 | out.writeBytes(msgBytes);
38 | log.info("Encoded message: {}", msg);
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/GB32960MessageParser.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao.GB32960MessageData;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.ByteBufUtil;
6 | import lombok.extern.slf4j.Slf4j;
7 |
8 | /**
9 | * -----------------------------------------------------------------------------
10 | * File Name: GB32960MessageParser
11 | * -----------------------------------------------------------------------------
12 | * Description:
13 | *
14 | * GB32960MessageParser 负责将 ByteBuf 中的 GB32960 消息解析为 GB32960MessageData 对象。
15 | * -----------------------------------------------------------------------------
16 | *
17 | * @author xiaonannet
18 | * @version 1.0
19 | * -----------------------------------------------------------------------------
20 | * Revision History:
21 | * Date Author Version Description
22 | * -------- -------- ------- --------------------
23 | * 2024/9/9 xiaonannet 1.0 Initial creation
24 | * -----------------------------------------------------------------------------
25 | * @email
26 | * @date 2024/9/9 14:38
27 | */
28 | @Slf4j
29 | public class GB32960MessageParser {
30 |
31 | /**
32 | * 解析 GB32960 消息
33 | *
34 | * @param frame 包含完整 GB32960 消息的 ByteBuf
35 | * @return 解析后的 GB32960MessageData 对象
36 | */
37 | public static GB32960MessageData parse(ByteBuf frame) {
38 | GB32960MessageData messageData = new GB32960MessageData();
39 |
40 | // 读取起始符
41 | messageData.setMsgHead(ByteBufUtil.hexDump(frame.readBytes(2)));
42 |
43 | // 读取命令标识
44 | messageData.setMsgCommand(ByteBufUtil.hexDump(frame.readBytes(1)));
45 |
46 | // 读取应答标识
47 | messageData.setMsgResponse(ByteBufUtil.hexDump(frame.readBytes(1)));
48 |
49 | // 读取唯一识别码
50 | messageData.setUniqueIdentifier(ByteBufUtil.hexDump(frame.readBytes(17)));
51 |
52 | // 读取加密方式
53 | messageData.setEncryption(ByteBufUtil.hexDump(frame.readBytes(1)));
54 |
55 | // 读取数据单元长度
56 | int dataCellLength = frame.readUnsignedShort();
57 | messageData.setDataCellLength(String.valueOf(dataCellLength));
58 |
59 | // 读取数据单元
60 | byte[] dataUnit = new byte[dataCellLength];
61 | frame.readBytes(dataUnit);
62 | messageData.setData(ByteBufUtil.hexDump(dataUnit));
63 |
64 | // 读取校验码
65 | messageData.setCheckCode(ByteBufUtil.hexDump(frame.readBytes(1)));
66 |
67 | return messageData;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960Alert.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960Alert {
17 |
18 | private boolean driveMotorControllerTemperatureAlert;
19 | private boolean highVoltageInterlockStatusAlert;
20 | private boolean driveMotorTemperatureAlert;
21 | private boolean energyStorageOvercharge;
22 | private boolean soclowAlert;
23 | private boolean socjumpAlert;
24 | private boolean temperatureDifferenceAlert;
25 | private boolean cellHighTemperatureAlert;
26 | private boolean energyStorageOverVoltageAlert;
27 | private boolean energyStorageUnderVoltageAlert;
28 | private boolean brakingSystemAlert;
29 | private boolean cellOverVoltageAlert;
30 | private boolean cellUnderVoltageAlert;
31 | private boolean insulationAlert;
32 | private boolean dctemperatureAlert;
33 | private boolean energyStorageSystemNotMatchAlert;
34 | private boolean cellConsistencyDifferenceAlert;
35 | private boolean dcstatusAlert;
36 | private boolean sochighAlert;
37 |
38 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960AlertStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import java.util.List;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @Description:
9 | * @Author: mqttsnet
10 | * @E-mail: mqttsnet@163.com
11 | * @CreateDate: 2021/11/5$ 16:33$
12 | * @UpdateUser: mqttsnet
13 | * @UpdateDate: 2021/11/5$ 16:33$
14 | * @UpdateRemark: 修改内容
15 | * @Version: 1.0
16 | */
17 | @Data
18 | public class GB32960AlertStatus {
19 |
20 | /**
21 | * 最高报警等级,为当前发生的故障中的最高等级值,有效值范围:0~3
22 | */
23 | private String highestAlertLevel;
24 |
25 | /**
26 | * 通用告警标准,标准维持到报警条件解除
27 | */
28 | private String universalAlarmIdentification;
29 | /**
30 | * 可充电储能装置故障总数N1,N1个可充电储能装置故障,有效值范围:0~252,“0xFE”表示异常,“0xFF”表示无效
31 | */
32 | private int energyStorageAlertCount;
33 | /**
34 | * 可充电储能装置故障代码列表
35 | */
36 | private List energyStorageAlertList;
37 | /**
38 | * 驱动电机,故障总数N2,N2个驱动电机故障,有效值范围:0~252,“0xFE”表示异常,“0xFF”表示无效
39 | */
40 | private int driveMotorAlertCount;
41 | /**
42 | * 驱动电机故障代码列表
43 | */
44 | private List driveMotorAlertList;
45 | /**
46 | * 发动机故障总数N3,N3个驱动电机故障,有效值范围:0~252,“0xFE”表示异常,“0xFF”表示无效
47 | */
48 | private int engineAlertCount;
49 | /**
50 | * 发动机故障列表
51 | */
52 | private List engineAlertList;
53 | /**
54 | * 其他故障总数N4,N4个其他故障
55 | */
56 | private int otherAlertCount;
57 | /**
58 | * 其他故障代码列表
59 | */
60 | private List otherAlertList;
61 | /**
62 | * 通用报警标志
63 | */
64 | private GB32960Alert alert;
65 |
66 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960BaseDTO.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import java.util.Date;
4 |
5 | import lombok.Data;
6 |
7 | @Data
8 | public class GB32960BaseDTO {
9 |
10 | /**
11 | * 车辆VIN
12 | */
13 | private String vin;
14 | /**
15 | * 电池包编码
16 | */
17 | private String batteryPackNumbers;
18 | /**
19 | * 整车数据
20 | */
21 | private GB32960VehicleStatus vehicleStatus;
22 | /**
23 | * 驱动电机数据
24 | */
25 | private GB32960DriveMotorStatus driveMotorStatus;
26 |
27 | private String fuelCellStatus;
28 |
29 | private String engineStatus;
30 | /**
31 | * 车辆位置数据
32 | */
33 | private GB32960LocationStatus locationStatus;
34 | /**
35 | * 极值数据
36 | */
37 | private GB32960ExtremeStatus extremeStatus;
38 | /**
39 | * 报警数据
40 | */
41 | private GB32960AlertStatus alertStatus;
42 |
43 | /**
44 | * 可充电储能装置电压数据
45 | */
46 | private GB32960EnergyStorageVoltageStatus energyStorageVoltageStatus;
47 |
48 | /**
49 | * 可充电储能装置温度数据
50 | */
51 | private GB32960EnergyStorageTemperatureStatus energyStorageTemperatureStatus;
52 |
53 |
54 | private GB32960CustomData customData;
55 |
56 | private Date acquisitionTime;
57 |
58 | private Long dataTime;
59 |
60 | private String command;
61 |
62 |
63 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960CustomData.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960CustomData {
17 |
18 | private double energyConsumption;
19 | private int handbrakeStatus;
20 | private int highVoltagePower;
21 |
22 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960DriveMotorStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import java.util.List;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @Description:
9 | * @Author: mqttsnet
10 | * @E-mail: mqttsnet@163.com
11 | * @CreateDate: 2021/11/5$ 16:33$
12 | * @UpdateUser: mqttsnet
13 | * @UpdateDate: 2021/11/5$ 16:33$
14 | * @UpdateRemark: 修改内容
15 | * @Version: 1.0
16 | */
17 | @Data
18 | public class GB32960DriveMotorStatus {
19 |
20 | /**
21 | * 驱动电机个数
22 | */
23 | private Integer driveMotorCount;
24 |
25 | /**
26 | * 驱动电机总 成信息列表
27 | */
28 | private List driveMotors;
29 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960DriveMotors.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960DriveMotors {
17 |
18 | /**
19 | * 驱动电机序号
20 | */
21 | private Integer sn;
22 | /**
23 | * 驱动电机状态
24 | */
25 | private String driveMotorPower;
26 | /**
27 | * 驱动电机控制器温度
28 | */
29 | private Integer controllerTemperature;
30 | /**
31 | * 驱动电机转速
32 | */
33 | private Integer driveMotorSpeed;
34 | /**
35 | * 驱动电机转矩
36 | */
37 | private Double driveMotorTorque;
38 | /**
39 | * 驱动电机温度
40 | */
41 | private Integer driveMotorTemperature;
42 | /**
43 | * 电机控制器输入电压
44 | */
45 | private Double controllerInputVoltage;
46 | /**
47 | * 电机控制器直流母线电流
48 | */
49 | private Double dcBusCurrentOfController;
50 |
51 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960EnergyStorageTemperatureStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import java.util.List;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @author sunshihuan
9 | * 可充电储能装置温度数据
10 | */
11 | @Data
12 | public class GB32960EnergyStorageTemperatureStatus {
13 |
14 | private int subEnergyStorageSystemCount;
15 |
16 | private List energyStorageTemperatures;
17 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960EnergyStorageTemperatures.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import java.util.List;
4 |
5 | import lombok.Data;
6 |
7 |
8 | @Data
9 | public class GB32960EnergyStorageTemperatures {
10 |
11 | private Integer energyStorageSubSystemIndex1;
12 | private Integer probeCount;
13 | private List cellTemperatures;
14 |
15 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960EnergyStorageVoltageStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import java.util.List;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @Description: 可充电储能装置电压数据
9 | * @Author: mqttsnet
10 | * @E-mail: mqttsnet@163.com
11 | * @CreateDate: 2021/11/5$ 16:33$
12 | * @UpdateUser: mqttsnet
13 | * @UpdateDate: 2021/11/5$ 16:33$
14 | * @UpdateRemark: 修改内容
15 | * @Version: 1.0
16 | */
17 | @Data
18 | public class GB32960EnergyStorageVoltageStatus {
19 |
20 | private Integer subSystemOfEnergyStorageCount;
21 | private List energyStorageVoltages;
22 |
23 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960EnergyStorageVoltages.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import java.util.List;
4 |
5 | import lombok.Data;
6 |
7 | @Data
8 | public class GB32960EnergyStorageVoltages {
9 |
10 | private Integer energyStorageSubSystemIndex;
11 | private Double energyStorageVoltage;
12 | private Double energyStorageCurrent;
13 | private Integer cellCount;
14 | private Integer frameCellStartIndex;
15 | private Integer frameCellCount;
16 | private List cellVoltages;
17 |
18 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960ExtremeStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960ExtremeStatus {
17 |
18 | /**
19 | * 最高电压电池子系统号
20 | */
21 | private Integer subSystemIndexOfMaxVoltage;
22 | /**
23 | * 最高电压电池单体代号
24 | */
25 | private Integer cellIndexOfMaxVoltage;
26 | /**
27 | * 电池单体电压最高值
28 | */
29 | private Double cellMaxVoltage;
30 | /**
31 | * 最低电压电池子系统号
32 | */
33 | private Integer subSystemIndexOfMinVoltage;
34 | /**
35 | *最低电压电池单体代号
36 | */
37 | private Integer cellIndexOfMinVoltage;
38 | /**
39 | * 电池单体电压最低值
40 | */
41 | private Double cellMinVoltage;
42 | /**
43 | * 最高温度子系统号
44 | */
45 | private Integer subSystemIndexOfMaxTemperature;
46 | /**
47 | *最高温度探针序号
48 | */
49 | private Integer probeIndexOfMaxTemperature;
50 | /**
51 | * 最高温度值
52 | */
53 | private Integer maxTemperature;
54 | /**
55 | * 最低温度子系统号
56 | */
57 | private Integer subSystemIndexOfMinTemperature;
58 | /**
59 | * 最低温度探针序号
60 | */
61 | private Integer probeIndexOfMinTemperature;
62 | /**
63 | * 最低温度值
64 | */
65 | private Integer minTemperature;
66 |
67 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960LocateStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description: 定位状态
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960LocateStatus {
17 |
18 | private String validation;
19 | private String latitudeType;
20 | private String longitudeType;
21 |
22 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960LocationStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960LocationStatus {
17 |
18 | /**
19 | * 精度
20 | */
21 | private Double longitude;
22 | /**
23 | * 维度
24 | */
25 | private Double latitude;
26 | /**
27 | * 定位状态
28 | */
29 | private GB32960LocateStatus locateStatus;
30 |
31 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960MessageData.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description: GB32960报文体数据模型
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @Website: mqttsnet
10 | * @CreateDate: 2021/11/15$ 18:37$
11 | * @UpdateUser: mqttsnet
12 | * @UpdateDate: 2021/11/15$ 18:37$
13 | * @UpdateRemark: 修改内容
14 | * @Version: 1.0
15 | */
16 | @Data
17 | public class GB32960MessageData implements Cloneable {
18 | /**
19 | * 起始符
20 | */
21 | private String msgHead;
22 |
23 |
24 | /**
25 | * 命令标识
26 | */
27 | private String msgCommand;
28 |
29 | /**
30 | * 应答标识
31 | */
32 | private String msgResponse;
33 |
34 | /**
35 | * 唯一识别码
36 | */
37 | private String uniqueIdentifier;
38 |
39 |
40 | /**
41 | * 加密方式
42 | */
43 | private String encryption;
44 |
45 |
46 | /**
47 | * 数据单元长度
48 | */
49 | private String dataCellLength;
50 |
51 | /**
52 | * 数据单元
53 | */
54 | private String data;
55 |
56 |
57 | /**
58 | * 校验码
59 | */
60 | private String checkCode;
61 |
62 |
63 | @Override
64 | public GB32960MessageData clone() {
65 | try {
66 | // 调用 Object 类的 clone 方法进行浅拷贝
67 | return (GB32960MessageData) super.clone();
68 | } catch (CloneNotSupportedException e) {
69 | throw new RuntimeException("Clone not supported", e);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960TransmissionStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960TransmissionStatus {
17 |
18 | /**
19 | * 档位
20 | */
21 | private String gear;
22 |
23 | /**
24 | * 有驱动力
25 | */
26 | private boolean hasDriverForce;
27 |
28 | /**
29 | * 有制动力
30 | */
31 | private boolean hasBrakingForce;
32 |
33 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/entity/dao/GB32960VehicleStatus.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Description:
7 | * @Author: mqttsnet
8 | * @E-mail: mqttsnet@163.com
9 | * @CreateDate: 2021/11/5$ 16:33$
10 | * @UpdateUser: mqttsnet
11 | * @UpdateDate: 2021/11/5$ 16:33$
12 | * @UpdateRemark: 修改内容
13 | * @Version: 1.0
14 | */
15 | @Data
16 | public class GB32960VehicleStatus {
17 |
18 | /**
19 | * 车辆状态
20 | */
21 | private String engineStatus;
22 | /**
23 | * 运行模式
24 | */
25 | private String runningModel;
26 | /**
27 | * 充电状态
28 | */
29 | private String chargingStatus;
30 | /**
31 | * 车速
32 | */
33 | private Double speed;
34 | /**
35 | * 累计里程
36 | */
37 | private Double mileage;
38 | /**
39 | * 总电压
40 | */
41 | private Double voltage;
42 | /**
43 | * 总电流
44 | */
45 | private Double current;
46 | /**
47 | * SOC
48 | */
49 | private Integer soc;
50 | /**
51 | * DC-DC状态
52 | */
53 | private String dcStatus;
54 | /**
55 | * 档位信息
56 | */
57 | private GB32960TransmissionStatus transmissionStatus;
58 |
59 | /**
60 | * 绝缘电阻
61 | */
62 | private Integer insulationResistance;
63 |
64 | /**
65 | * 加速踏板行程
66 | */
67 | private Integer accelerationPedalTravel;
68 |
69 | /**
70 | * 制动踏板状态
71 | */
72 | private Integer brakePedalState;
73 |
74 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/gb32960/service/DataParseService.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.service;
2 |
3 |
4 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.gb32960.entity.dao.GB32960MessageData;
5 |
6 | /**
7 | * @Description: GB32960数据解析服务接口。
8 | * @Author: mqttsnet
9 | * @E-mail: mqttsnet@163.com
10 | * @Website: https://www.mqttsnet.com
11 | * @CreateDate: 2024/09/10$ 18:30$
12 | * @UpdateUser: mqttsnet
13 | * @UpdateDate: 2024/09/10$ 18:30$
14 | * @UpdateRemark: 修改内容
15 | * @Version: 1.0
16 | */
17 |
18 | public interface DataParseService {
19 | /**
20 | * 处理车辆登录消息
21 | *
22 | * @param msg GB32960MessageData 消息对象
23 | * @return 处理后的响应数据字符串
24 | */
25 | String handleVehicleLogin(GB32960MessageData msg);
26 |
27 | /**
28 | * 处理实时信息上报
29 | *
30 | * @param msg GB32960MessageData 消息对象
31 | * @return 处理后的响应数据字符串
32 | */
33 | String handleRealtimeData(GB32960MessageData msg);
34 |
35 | /**
36 | * 处理补发信息上报
37 | *
38 | * @param msg GB32960MessageData 消息对象
39 | * @return 处理后的响应数据字符串
40 | */
41 | String handleSupplementaryData(GB32960MessageData msg);
42 |
43 | /**
44 | * 处理车辆登出消息
45 | *
46 | * @param msg GB32960MessageData 消息对象
47 | * @return 处理后的响应数据字符串
48 | */
49 | String handleVehicleLogout(GB32960MessageData msg);
50 |
51 | /**
52 | * 处理平台登录消息
53 | *
54 | * @param msg GB32960MessageData 消息对象
55 | * @return 处理后的响应数据字符串
56 | */
57 | String handlePlatformLogin(GB32960MessageData msg);
58 |
59 | /**
60 | * 处理平台登出消息
61 | *
62 | * @param msg GB32960MessageData 消息对象
63 | * @return 处理后的响应数据字符串
64 | */
65 | String handlePlatformLogout(GB32960MessageData msg);
66 |
67 | /**
68 | * 处理心跳消息
69 | *
70 | * @param msg GB32960MessageData 消息对象
71 | * @return 处理后的响应数据字符串
72 | */
73 | String handleHeartbeat(GB32960MessageData msg);
74 |
75 | /**
76 | * 处理同步校时消息
77 | *
78 | * @param msg GB32960MessageData 消息对象
79 | * @return 处理后的响应数据字符串
80 | */
81 | String handleTimeSynchronization(GB32960MessageData msg);
82 | }
83 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/handler/ExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.handler;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.ChannelInboundHandlerAdapter;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | /**
8 | * -----------------------------------------------------------------------------
9 | * File Name: ExceptionHandler
10 | * -----------------------------------------------------------------------------
11 | * Description:
12 | *
13 | * -----------------------------------------------------------------------------
14 | *
15 | * @author xiaonannet
16 | * @version 1.0
17 | * -----------------------------------------------------------------------------
18 | * Revision History:
19 | * Date Author Version Description
20 | * -------- -------- ------- --------------------
21 | * 2024/9/8 xiaonannet 1.0 Initial creation
22 | * -----------------------------------------------------------------------------
23 | * @email
24 | * @date 2024/9/8 18:51
25 | */
26 | @Slf4j
27 | public class ExceptionHandler extends ChannelInboundHandlerAdapter {
28 |
29 | @Override
30 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
31 | // 记录详细的错误信息
32 | log.error("Exception caught in handler for channel: {}", ctx.channel().id(), cause);
33 |
34 | // 关闭连接
35 | ctx.close();
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/initializer/MyServerInitializer.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.initializer;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.decoder.GB32960Decoder;
4 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.dispatcher.MessageDispatcher;
5 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.handler.ExceptionHandler;
6 | import io.netty.channel.ChannelInitializer;
7 | import io.netty.channel.ChannelPipeline;
8 | import io.netty.channel.socket.SocketChannel;
9 | import io.netty.handler.logging.LogLevel;
10 | import io.netty.handler.logging.LoggingHandler;
11 | import io.netty.handler.timeout.IdleStateHandler;
12 | import lombok.extern.slf4j.Slf4j;
13 | import org.springframework.stereotype.Component;
14 |
15 |
16 | /**
17 | * -----------------------------------------------------------------------------
18 | * File Name: MyServerInitializer
19 | * -----------------------------------------------------------------------------
20 | * Description:
21 | *
22 | * 初始化服务器通道,配置管道中各个处理器。
23 | * 采用定界符方案来解决 TCP 粘包和拆包问题。
24 | *
25 | * -----------------------------------------------------------------------------
26 | *
27 | * @author xiaonannet
28 | * @version 1.0
29 | * -----------------------------------------------------------------------------
30 | * Revision History:
31 | * Date Author Version Description
32 | * -------- -------- ------- --------------------
33 | * 2024/9/8 xiaonannet 1.0 Initial creation
34 | * -----------------------------------------------------------------------------
35 | * @email
36 | * @date 2024/9/8 18:48
37 | */
38 |
39 | @Slf4j
40 | @Component
41 | public class MyServerInitializer extends ChannelInitializer {
42 |
43 | @Override
44 | protected void initChannel(SocketChannel ch) throws Exception {
45 |
46 | ChannelPipeline pipeline = ch.pipeline();
47 |
48 | log.info("Initializing channel for client: {}", ch.remoteAddress());
49 |
50 | // 添加日志处理器,用于记录入站和出站事件
51 | pipeline.addLast(new LoggingHandler(LogLevel.INFO));
52 |
53 | // 添加空闲检测处理器,检测是否有读写超时
54 | pipeline.addLast(new IdleStateHandler(60, 0, 0));
55 |
56 | // 这里也可以使用 LengthFieldBasedFrameDecoder 来解决粘包和拆包问题(GB32960Decoder 中也处理了这里就可以不启用)
57 | /*pipeline.addLast(
58 | new LengthFieldBasedFrameDecoder(64 * 1024, 22, 2, 1, 2)
59 | );*/
60 |
61 |
62 | // 添加 GB32960 协议解码器,解决粘包和拆包问题
63 | pipeline.addLast(new GB32960Decoder());
64 |
65 | // 添加业务逻辑处理器,处理解码后的消息
66 | pipeline.addLast(new MessageDispatcher());
67 |
68 | // 添加异常处理器
69 | pipeline.addLast(new ExceptionHandler());
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/MqttConnectionStatusEvent.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event;
2 |
3 | import java.time.LocalDateTime;
4 |
5 | import lombok.Getter;
6 | import org.springframework.context.ApplicationEvent;
7 |
8 | /**
9 | * ============================================================================
10 | * Description:
11 | * MQTT连接状态变更事件
12 | * ============================================================================
13 | *
14 | * @author Sun Shihuan
15 | * @version 1.0.0
16 | * -----------------------------------------------------------------------------
17 | * Revision History:
18 | * Date Author Version Description
19 | * -------- -------- ------- --------------------
20 | * 2025/3/28 Sun Shihuan 1.0 Initial creation
21 | * -----------------------------------------------------------------------------
22 | * @email
23 | * @date 2025/3/28 18:45
24 | */
25 | @Getter
26 | public class MqttConnectionStatusEvent extends ApplicationEvent {
27 | private final boolean connected;
28 | private final String serverURI;
29 | private final LocalDateTime eventTime;
30 | private final Throwable cause;
31 |
32 | /**
33 | * 构造连接成功事件
34 | *
35 | * @param source 事件源
36 | * @param connected 连接状态
37 | * @param serverURI 服务器地址
38 | */
39 | public MqttConnectionStatusEvent(Object source, boolean connected, String serverURI) {
40 | this(source, connected, serverURI, null);
41 | }
42 |
43 | /**
44 | * 构造连接异常事件
45 | *
46 | * @param source 事件源
47 | * @param connected 连接状态
48 | * @param serverURI 服务器地址
49 | * @param cause 异常原因
50 | */
51 | public MqttConnectionStatusEvent(Object source, boolean connected, String serverURI, Throwable cause) {
52 | super(source);
53 | this.connected = connected;
54 | this.serverURI = serverURI;
55 | this.eventTime = LocalDateTime.now();
56 | this.cause = cause;
57 | }
58 |
59 | /**
60 | * 获取状态描述
61 | */
62 | public String getStatus() {
63 | return connected ? "CONNECTED" : "DISCONNECTED";
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/MqttMessageReceiveEvent.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event;
2 |
3 | import lombok.Getter;
4 | import org.springframework.context.ApplicationEvent;
5 |
6 | /**
7 | * -----------------------------------------------------------------------------
8 | * File Name: MqttMessageReceiveEvent
9 | * -----------------------------------------------------------------------------
10 | * Description:
11 | *
12 | * 消息接收事件
13 | * -----------------------------------------------------------------------------
14 | *
15 | * @author xiaonannet
16 | * @version 1.0
17 | * -----------------------------------------------------------------------------
18 | * Revision History:
19 | * Date Author Version Description
20 | * -------- -------- ------- --------------------
21 | * 2025/1/22 xiaonannet 1.0 Initial creation
22 | * -----------------------------------------------------------------------------
23 | * @email
24 | * @date 2025/1/22 18:15
25 | */
26 | @Getter
27 | public class MqttMessageReceiveEvent extends ApplicationEvent {
28 | private final String topic;
29 | private final byte[] payload;
30 | private final int qos;
31 |
32 | /**
33 | * 构造消息接收事件
34 | *
35 | * @param source 事件源(通常为发布者实例)
36 | * @param topic 消息主题
37 | * @param payload 消息内容(字节数组)
38 | * @param qos 服务质量等级(0-2)
39 | */
40 | public MqttMessageReceiveEvent(Object source, String topic, byte[] payload, int qos) {
41 | super(source);
42 | this.topic = topic;
43 | this.payload = payload;
44 | this.qos = qos;
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/MqttMessageSendEvent.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event;
2 |
3 | import lombok.Getter;
4 | import org.springframework.context.ApplicationEvent;
5 |
6 | /**
7 | * -----------------------------------------------------------------------------
8 | * File Name: MqttMessageSendEvent
9 | * -----------------------------------------------------------------------------
10 | * Description:
11 | * 消息发送事件
12 | * -----------------------------------------------------------------------------
13 | *
14 | * @author xiaonannet
15 | * @version 1.0
16 | * -----------------------------------------------------------------------------
17 | * Revision History:
18 | * Date Author Version Description
19 | * -------- -------- ------- --------------------
20 | * 2025/1/22 xiaonannet 1.0 Initial creation
21 | * -----------------------------------------------------------------------------
22 | * @email
23 | * @date 2025/1/22 18:14
24 | */
25 | @Getter
26 | public class MqttMessageSendEvent extends ApplicationEvent {
27 | private final int messageId;
28 | private final String[] topics;
29 |
30 | /**
31 | * 构造消息发送事件
32 | *
33 | * @param source 事件源
34 | * @param messageId 消息ID
35 | * @param topics 成功投递的主题数组
36 | */
37 | public MqttMessageSendEvent(Object source, int messageId, String[] topics) {
38 | super(source);
39 | this.messageId = messageId;
40 | this.topics = topics;
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/MqttPublishMessageEvent.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event;
2 |
3 | import lombok.Getter;
4 | import org.springframework.context.ApplicationEvent;
5 |
6 | /**
7 | * -----------------------------------------------------------------------------
8 | * File Name: MqttMessageEvent
9 | * -----------------------------------------------------------------------------
10 | * Description:
11 | *
12 | * -----------------------------------------------------------------------------
13 | *
14 | * @author xiaonannet
15 | * @version 1.0
16 | * -----------------------------------------------------------------------------
17 | * Revision History:
18 | * Date Author Version Description
19 | * -------- -------- ------- --------------------
20 | * 2025/1/22 xiaonannet 1.0 Initial creation
21 | * -----------------------------------------------------------------------------
22 | * @email
23 | * @date 2025/1/22 16:12
24 | */
25 |
26 | @Getter
27 | public class MqttPublishMessageEvent extends ApplicationEvent {
28 | private final String topic;
29 |
30 | private final byte[] payload;
31 |
32 | private final int qos;
33 |
34 | private final boolean retain;
35 |
36 | /**
37 | * 构造发布请求事件
38 | *
39 | * @param source 事件源
40 | * @param topic 目标主题
41 | * @param payload 消息内容
42 | * @param qos 服务质量
43 | * @param retain 保留标志
44 | */
45 | public MqttPublishMessageEvent(Object source, String topic, byte[] payload, int qos, boolean retain) {
46 | super(source);
47 | this.topic = topic;
48 | this.payload = payload;
49 | this.qos = qos;
50 | this.retain = retain;
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/listener/MqttConnectionStatusListener.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.listener;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.MqttConnectionStatusEvent;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.context.ApplicationListener;
6 | import org.springframework.core.annotation.Order;
7 | import org.springframework.stereotype.Component;
8 |
9 | /**
10 | * ============================================================================
11 | * Description:
12 | * 连接状态监听器
13 | * ============================================================================
14 | *
15 | * @author Sun Shihuan
16 | * @version 1.0.0
17 | * -----------------------------------------------------------------------------
18 | * Revision History:
19 | * Date Author Version Description
20 | * -------- -------- ------- --------------------
21 | * 2025/3/28 Sun Shihuan 1.0 Initial creation
22 | * -----------------------------------------------------------------------------
23 | * @email
24 | * @date 2025/3/28 18:46
25 | */
26 | @Slf4j
27 | @Component
28 | @Order(0) // 最高优先级
29 | public class MqttConnectionStatusListener implements ApplicationListener {
30 |
31 | @Override
32 | public void onApplicationEvent(MqttConnectionStatusEvent event) {
33 | if (event.isConnected()) {
34 | log.info("成功连接MQTT服务器 [URI:{}]", event.getServerURI());
35 | // 可在此触发重订阅等操作
36 | } else {
37 | log.error("连接异常断开 [URI:{}] 原因:{}",
38 | event.getServerURI(),
39 | event.getCause() != null ? event.getCause().getMessage() : "未知"
40 | );
41 | // 可在此触发重连策略
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/listener/MqttMessageReceiveEventListener.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.listener;
2 |
3 | import java.nio.charset.StandardCharsets;
4 |
5 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.MqttMessageReceiveEvent;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.springframework.context.ApplicationListener;
8 | import org.springframework.core.annotation.Order;
9 | import org.springframework.scheduling.annotation.Async;
10 | import org.springframework.stereotype.Component;
11 |
12 | /**
13 | * -----------------------------------------------------------------------------
14 | * File Name: MqttMessageEventListener
15 | * -----------------------------------------------------------------------------
16 | * Description:
17 | *
18 | * MQTT Publish 消息事件监听器
19 | * -----------------------------------------------------------------------------
20 | *
21 | * @author xiaonannet
22 | * @version 1.0
23 | * -----------------------------------------------------------------------------
24 | * Revision History:
25 | * Date Author Version Description
26 | * -------- -------- ------- --------------------
27 | * 2025/1/22 xiaonannet 1.0 Initial creation
28 | * -----------------------------------------------------------------------------
29 | * @email
30 | * @date 2025/1/22 16:16
31 | */
32 | @Slf4j
33 | @Component
34 | @Order(1)
35 | public class MqttMessageReceiveEventListener implements ApplicationListener {
36 | @Async
37 | @Override
38 | public void onApplicationEvent(MqttMessageReceiveEvent event) {
39 | log.info("MqttMessageReceiveEventListener 接收到消息:Topic = {}, QoS = {}, Payload = {}", event.getTopic(), event.getQos(), new String(event.getPayload(), StandardCharsets.UTF_8));
40 |
41 |
42 | }
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/listener/MqttMessageSendEventListener.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.listener;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.MqttMessageSendEvent;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.context.ApplicationListener;
6 | import org.springframework.core.annotation.Order;
7 | import org.springframework.scheduling.annotation.Async;
8 | import org.springframework.stereotype.Component;
9 |
10 | /**
11 | * -----------------------------------------------------------------------------
12 | * File Name: MqttMessageEventListener
13 | * -----------------------------------------------------------------------------
14 | * Description:
15 | *
16 | * MQTT Publish 消息事件监听器
17 | * -----------------------------------------------------------------------------
18 | *
19 | * @author xiaonannet
20 | * @version 1.0
21 | * -----------------------------------------------------------------------------
22 | * Revision History:
23 | * Date Author Version Description
24 | * -------- -------- ------- --------------------
25 | * 2025/1/22 xiaonannet 1.0 Initial creation
26 | * -----------------------------------------------------------------------------
27 | * @email
28 | * @date 2025/1/22 16:16
29 | */
30 | @Slf4j
31 | @Component
32 | @Order(1)
33 | public class MqttMessageSendEventListener implements ApplicationListener {
34 | @Async
35 | @Override
36 | public void onApplicationEvent(MqttMessageSendEvent event) {
37 | log.info("MqttMessageSendEventListener 消息发送成功:messageId = {}, topics = {}", event.getMessageId(), event.getTopics());
38 |
39 | }
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/listener/MqttPublishMessageEventListener.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.listener;
2 |
3 | import java.nio.charset.StandardCharsets;
4 |
5 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.MyComponent;
6 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.MqttPublishMessageEvent;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.eclipse.paho.client.mqttv3.MqttException;
9 | import org.eclipse.paho.client.mqttv3.MqttMessage;
10 | import org.springframework.context.ApplicationListener;
11 | import org.springframework.core.annotation.Order;
12 | import org.springframework.scheduling.annotation.Async;
13 | import org.springframework.stereotype.Component;
14 |
15 | /**
16 | * -----------------------------------------------------------------------------
17 | * File Name: MqttMessageEventListener
18 | * -----------------------------------------------------------------------------
19 | * Description:
20 | *
21 | * MQTT Publish 消息事件监听器
22 | * -----------------------------------------------------------------------------
23 | *
24 | * @author xiaonannet
25 | * @version 1.0
26 | * -----------------------------------------------------------------------------
27 | * Revision History:
28 | * Date Author Version Description
29 | * -------- -------- ------- --------------------
30 | * 2025/1/22 xiaonannet 1.0 Initial creation
31 | * -----------------------------------------------------------------------------
32 | * @email
33 | * @date 2025/1/22 16:16
34 | */
35 | @Slf4j
36 | @Component
37 | @Order(1)
38 | public class MqttPublishMessageEventListener implements ApplicationListener {
39 |
40 | private final MyComponent myComponent;
41 |
42 | public MqttPublishMessageEventListener(MyComponent myComponent) {
43 | this.myComponent = myComponent;
44 | }
45 |
46 | @Async
47 | @Override
48 | public void onApplicationEvent(MqttPublishMessageEvent event) {
49 | log.info("Handling event: topic={}, payload={}, qos={}, retain={}", event.getTopic(), new String(event.getPayload(), StandardCharsets.UTF_8), event.getQos(), event.isRetain());
50 | if (null == myComponent.getMqttClient()) {
51 | log.warn("MqttClient is not initialized, cannot publish message.");
52 | return;
53 | }
54 | MqttMessage mqttMessage = new MqttMessage(event.getPayload());
55 | mqttMessage.setQos(event.getQos());
56 | mqttMessage.setRetained(event.isRetain());
57 | try {
58 | myComponent.getMqttClient().publish(event.getTopic(), mqttMessage);
59 | } catch (MqttException e) {
60 | log.error("Failed to publish message to topic: {}", event.getTopic(), e);
61 | throw new RuntimeException(e);
62 | }
63 | }
64 |
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/mqtt/event/publisher/MqttEventPublisher.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.publisher;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.MqttConnectionStatusEvent;
4 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.MqttMessageReceiveEvent;
5 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.MqttMessageSendEvent;
6 | import com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.mqtt.event.MqttPublishMessageEvent;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.springframework.context.ApplicationEventPublisher;
9 | import org.springframework.lang.Nullable;
10 | import org.springframework.stereotype.Component;
11 |
12 | /**
13 | * -----------------------------------------------------------------------------
14 | * File Name: MqttEventPublisher
15 | * -----------------------------------------------------------------------------
16 | * Description:
17 | *
18 | * Mqtt 事件发布器
19 | * -----------------------------------------------------------------------------
20 | *
21 | * @author xiaonannet
22 | * @version 1.0
23 | * -----------------------------------------------------------------------------
24 | * Revision History:
25 | * Date Author Version Description
26 | * -------- -------- ------- --------------------
27 | * 2025/1/22 xiaonannet 1.0 Initial creation
28 | * -----------------------------------------------------------------------------
29 | * @email
30 | * @date 2025/1/22 16:18
31 | */
32 | @Component
33 | @Slf4j
34 | public class MqttEventPublisher {
35 |
36 | private final ApplicationEventPublisher eventPublisher;
37 |
38 | public MqttEventPublisher(ApplicationEventPublisher eventPublisher) {
39 | this.eventPublisher = eventPublisher;
40 | }
41 |
42 |
43 | /**
44 | * 发布消息接收事件
45 | *
46 | * @param topic 消息主题
47 | * @param payload 消息内容
48 | * @param qos 服务质量
49 | */
50 | public void publishMqttMessageReceiveEvent(String topic, byte[] payload, int qos) {
51 | eventPublisher.publishEvent(new MqttMessageReceiveEvent(this, topic, payload, qos));
52 | log.info("已发布消息接收事件 [Topic:{}]", topic);
53 | }
54 |
55 | /**
56 | * 发布消息发送成功事件
57 | *
58 | * @param messageId 消息ID
59 | * @param topics 成功投递的主题数组
60 | */
61 | public void publishMqttMessageSendEvent(int messageId, String[] topics) {
62 | eventPublisher.publishEvent(new MqttMessageSendEvent(this, messageId, topics));
63 | log.info("已发布消息发送事件 [MsgID:{}]", messageId);
64 | }
65 |
66 | /**
67 | * 发布消息发布请求事件
68 | *
69 | * @param topic 目标主题
70 | * @param payload 消息内容
71 | * @param qos 服务质量
72 | * @param retain 保留标志
73 | */
74 | public void publishMqttPublishMessageEvent(String topic, byte[] payload, int qos, boolean retain) {
75 | eventPublisher.publishEvent(new MqttPublishMessageEvent(this, topic, payload, qos, retain));
76 | log.info("已发布消息请求事件 [Topic:{}]", topic);
77 | }
78 |
79 | /**
80 | * 发布连接状态事件
81 | *
82 | * @param connected 是否已连接
83 | * @param serverUrl 服务器地址
84 | */
85 | public void publishConnectionStatusEvent(boolean connected, String serverUrl) {
86 | eventPublisher.publishEvent(new MqttConnectionStatusEvent(this, connected, serverUrl));
87 | log.info("已发布连接状态事件 [状态:{}]", connected ? "已连接" : "已断开");
88 | }
89 |
90 | /**
91 | * 发布连接状态事件(增强版)
92 | *
93 | * @param connected 连接状态
94 | * @param serverUrl 服务器地址
95 | * @param cause 连接异常原因(断开时必传)
96 | */
97 | public void publishConnectionStatusEvent(boolean connected, String serverUrl, @Nullable Throwable cause) {
98 | MqttConnectionStatusEvent event = new MqttConnectionStatusEvent(this, connected, serverUrl, cause);
99 | eventPublisher.publishEvent(event);
100 | if (connected) {
101 | log.info("连接状态变更 [成功] => {}", serverUrl);
102 | } else {
103 | String reason = cause != null ? cause.getMessage() : "主动断开";
104 | log.warn("连接状态变更 [断开] => {} 原因: {}", serverUrl, reason);
105 | }
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/utils/Sm4Utils.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.utils;
2 |
3 | import java.nio.charset.StandardCharsets;
4 |
5 | import org.bouncycastle.crypto.engines.SM4Engine;
6 | import org.bouncycastle.crypto.modes.CBCBlockCipher;
7 | import org.bouncycastle.crypto.paddings.PKCS7Padding;
8 | import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
9 | import org.bouncycastle.crypto.params.KeyParameter;
10 | import org.bouncycastle.crypto.params.ParametersWithIV;
11 | import org.bouncycastle.util.encoders.Hex;
12 |
13 | /**
14 | * @Description: 国密SM4分组密码算法工具类(对称加密)
15 | * @Author: mqttsnet
16 | * @CreateDate: 2021/8/25$ 15:27$
17 | * @UpdateUser: mqttsnet
18 | * @UpdateDate: 2021/8/25$ 15:27$
19 | * @UpdateRemark: 修改内容
20 | * @Version: 1.0
21 | */
22 | public class Sm4Utils {
23 | /**
24 | * 加密
25 | *
26 | * @param key 密钥
27 | * @param iv 初始向量
28 | * @param data 明文
29 | * @return 密文
30 | */
31 | public static String encrypt(String key, String iv, String data) {
32 | try {
33 | // 创建SM4引擎
34 | SM4Engine sm4Engine = new SM4Engine();
35 | // 创建CBC模式的加密器
36 | CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(sm4Engine);
37 | // 创建填充加密器
38 | PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbcBlockCipher, new PKCS7Padding());
39 | // 创建密钥参数
40 | KeyParameter keyParameter = new KeyParameter(Hex.decode(key));
41 | // 创建带IV的参数
42 | ParametersWithIV parametersWithIV = new ParametersWithIV(keyParameter, Hex.decode(iv));
43 | // 初始化加密器
44 | cipher.init(true, parametersWithIV);
45 |
46 | byte[] encryptedData = new byte[cipher.getOutputSize(data.getBytes(StandardCharsets.UTF_8).length)];
47 | int length = cipher.processBytes(data.getBytes(StandardCharsets.UTF_8), 0, data.getBytes(StandardCharsets.UTF_8).length, encryptedData, 0);
48 | cipher.doFinal(encryptedData, length);
49 |
50 | return Hex.toHexString(encryptedData);
51 | } catch (Exception e) {
52 | throw new RuntimeException("SM4加密失败", e);
53 | }
54 | }
55 |
56 | /**
57 | * 解密
58 | *
59 | * @param key 密钥
60 | * @param iv 初始向量
61 | * @param data 密文
62 | * @return 明文
63 | */
64 | public static String decrypt(String key, String iv, String data) {
65 | try {
66 | // 创建SM4引擎
67 | SM4Engine sm4Engine = new SM4Engine();
68 | // 创建CBC模式的解密器
69 | CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(sm4Engine);
70 | // 创建填充解密器
71 | PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbcBlockCipher, new PKCS7Padding());
72 | // 创建密钥参数
73 | KeyParameter keyParameter = new KeyParameter(Hex.decode(key));
74 | // 创建带IV的参数
75 | ParametersWithIV parametersWithIV = new ParametersWithIV(keyParameter, Hex.decode(iv));
76 | // 初始化解密器
77 | cipher.init(false, parametersWithIV);
78 |
79 | byte[] decryptedData = new byte[cipher.getOutputSize(Hex.decode(data).length)];
80 | int length = cipher.processBytes(Hex.decode(data), 0, Hex.decode(data).length, decryptedData, 0);
81 | int finalLength = cipher.doFinal(decryptedData, length);
82 |
83 | return new String(decryptedData, 0, length + finalLength, StandardCharsets.UTF_8);
84 | } catch (Exception e) {
85 | throw new RuntimeException("SM4解密失败", e);
86 | }
87 | }
88 |
89 | public static void main(String[] args) {
90 | String key = "0123456789abcdef0123456789abcdef";
91 | String iv = "0123456789abcdef0123456789abcdef";
92 | String data = "Hello, SM4!";
93 |
94 | String encryptedData = Sm4Utils.encrypt(key, iv, data);
95 | System.out.println("加密后的数据: " + encryptedData);
96 |
97 | String decryptedData = Sm4Utils.decrypt(key, iv, encryptedData);
98 | System.out.println("解密后的数据: " + decryptedData);
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/utils/SpringUtils.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.utils;
2 |
3 | import java.util.Map;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.context.ApplicationContext;
7 | import org.springframework.stereotype.Component;
8 | import org.springframework.util.Assert;
9 |
10 | /**
11 | * Spring工具类
12 | *
13 | * @author mqttsnet
14 | * @date 2017-12-25 16:27
15 | */
16 | @Component
17 | public final class SpringUtils {
18 | private static ApplicationContext applicationContext;
19 | private static ApplicationContext parentApplicationContext;
20 |
21 | private SpringUtils() {
22 | }
23 |
24 | /**
25 | * 单例Holder模式: 优点:将懒加载和线程安全完美结合的一种方式(无锁)。(推荐)
26 | *
27 | * @return 实实例
28 | */
29 | public static SpringUtils getInstance() {
30 | return SpringUtilsHolder.INSTANCE;
31 | }
32 |
33 | public static ApplicationContext getApplicationContext() {
34 | return applicationContext;
35 | }
36 |
37 | @Autowired
38 | public void setApplicationContext(ApplicationContext ctx) {
39 | Assert.notNull(ctx, "SpringUtil injection ApplicationContext is null");
40 | applicationContext = ctx;
41 | parentApplicationContext = ctx.getParent();
42 | }
43 |
44 | public static Object getBean(String name) {
45 | Assert.hasText(name, "SpringUtil name is null or empty");
46 | try {
47 | return applicationContext.getBean(name);
48 | } catch (Exception e) {
49 | return parentApplicationContext.getBean(name);
50 | }
51 | }
52 |
53 | public static T getBean(String name, Class type) {
54 | Assert.hasText(name, "SpringUtil name is null or empty");
55 | Assert.notNull(type, "SpringUtil type is null");
56 | try {
57 | return applicationContext.getBean(name, type);
58 | } catch (Exception e) {
59 | return parentApplicationContext.getBean(name, type);
60 | }
61 | }
62 |
63 | public static T getBean(Class type) {
64 | Assert.notNull(type, "SpringUtil type is null");
65 | try {
66 | return applicationContext.getBean(type);
67 | } catch (Exception e) {
68 | return parentApplicationContext.getBean(type);
69 | }
70 | }
71 |
72 | public static Map getBeansOfType(Class type) {
73 | Assert.notNull(type, "SpringUtil type is null");
74 | try {
75 | return applicationContext.getBeansOfType(type);
76 | } catch (Exception e) {
77 | return parentApplicationContext.getBeansOfType(type);
78 | }
79 | }
80 |
81 | public static ApplicationContext publishEvent(Object event) {
82 | applicationContext.publishEvent(event);
83 | return applicationContext;
84 | }
85 |
86 |
87 | /**
88 | *
89 | * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
90 | * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
91 | */
92 | private static class SpringUtilsHolder {
93 | /**
94 | * 静态初始化器,由JVM来保证线程安全
95 | */
96 | private static final SpringUtils INSTANCE = new SpringUtils();
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/utils/SubStringUtil.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.utils;
2 |
3 | /**
4 | * @Description: 字符串工具类-智能截取
5 | * @Author: mqttsnet
6 | * @E-mail: mqttsnet@163.com
7 | * @CreateDate: 2021/11/15$ 19:03$
8 | * @UpdateUser: mqttsnet
9 | * @UpdateDate: 2021/11/15$ 19:03$
10 | * @UpdateRemark: 修改内容
11 | * @Version: 1.0
12 | */
13 | public class SubStringUtil {
14 | /**
15 | * 从头开始截取
16 | *
17 | * @param str 字符串
18 | * @param end 结束位置
19 | * @return
20 | */
21 | public static String subStrStart(String str, int end){
22 | return subStr(str, 0, end);
23 | }
24 |
25 | /**
26 | * 从尾开始截取
27 | *
28 | * @param str 字符串
29 | * @param start 开始位置
30 | * @return
31 | */
32 | public static String subStrEnd(String str, int start){
33 | return subStr(str, str.length()-start, str.length());
34 | }
35 |
36 | /**
37 | * 截取字符串 (支持正向、反向截取)
38 | *
39 | * @param str 待截取的字符串
40 | * @param length 长度 ,>=0时,从头开始向后截取length长度的字符串;<0时,从尾开始向前截取length长度的字符串
41 | * @return 返回截取的字符串
42 | * @throws RuntimeException
43 | */
44 | public static String subStr(String str, int length) throws RuntimeException{
45 | if(str==null){
46 | throw new NullPointerException("字符串为null");
47 | }
48 | int len = str.length();
49 | if(len=0){
53 | return subStr(str, 0,length);
54 | }else{
55 | return subStr(str, len-Math.abs(length), len);
56 | }
57 | }
58 |
59 |
60 | /**
61 | * 截取字符串 (支持正向、反向选择)
62 | *
63 | * @param str 待截取的字符串
64 | * @param start 起始索引 ,>=0时,从start开始截取;<0时,从length-|start|开始截取
65 | * @param end 结束索引 ,>=0时,从end结束截取;<0时,从length-|end|结束截取
66 | * @return 返回截取的字符串
67 | * @throws RuntimeException
68 | */
69 | public static String subStr(String str, int start, int end) throws RuntimeException{
70 | if(str==null){
71 | throw new NullPointerException("");
72 | }
73 | int len = str.length();
74 | int s = 0;//记录起始索引
75 | int e = 0;//记录结尾索引
76 | if(len =0
83 | s = start;
84 | }
85 | if(len =0
92 | e = end;
93 | }
94 | if(e0){
118 | sb.delete(sb.length()-splitStr.length(), sb.length());
119 | }
120 | return sb.toString();
121 | }
122 | return null;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcptomqtt/utils/bytes/ByteCastUtil.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.utils.bytes;
2 |
3 | import io.netty.buffer.ByteBuf;
4 |
5 | /**
6 | * @author mqttsnet
7 | * @description:
8 | * @date 2021/11/30 11:15
9 | */
10 | public class ByteCastUtil {
11 | //byte[]——>hexString
12 | public static String byteHexString(byte[] b) {
13 | String a = "";
14 | for (int i = 0; i < b.length; i++) {
15 | String hex = Integer.toHexString(b[i] & 0xFF);
16 | if (hex.length() == 1) {
17 | hex = '0' + hex;
18 | }
19 | a = a + hex;
20 | }
21 | return a;
22 | }
23 |
24 | //byte——>hexString
25 | public static String byte2HexString(byte b) {
26 | String hex = Integer.toHexString(b & 0xFF);
27 | if (hex.length() == 1) {
28 | hex = '0' + hex;
29 | }
30 | return hex;
31 | }
32 |
33 | //ByteBuf——>String
34 | public static String byteBuf2String(ByteBuf buf) {
35 | String str;
36 | if (buf.hasArray()) { // 处理堆缓冲区
37 | str = new String(buf.array(), buf.arrayOffset() + buf.readerIndex(), buf.readableBytes());
38 | } else { // 处理直接缓冲区以及复合缓冲区
39 | byte[] bytes = new byte[buf.readableBytes()];
40 | buf.getBytes(buf.readerIndex(), bytes);
41 | str = new String(bytes, 0, buf.readableBytes());
42 | }
43 | return str;
44 | }
45 |
46 |
47 | //byte[]——>String
48 | public static String bytesToHexString(byte[] src) {
49 | StringBuilder stringBuilder = new StringBuilder("");
50 | if (src == null || src.length <= 0) {
51 | return null;
52 | }
53 | for (int i = 0; i < src.length; i++) {
54 | int v = src[i] & 0xFF;
55 | String hv = Integer.toHexString(v);
56 | if (hv.length() < 2) {
57 | stringBuilder.append(0);
58 | }
59 | stringBuilder.append(hv);
60 | }
61 | return stringBuilder.toString();
62 | }
63 |
64 | //HexString——>byte[]
65 | public static byte[] hexStringToBytes(String hexString) {
66 | if (hexString == null || hexString.equals("")) {
67 | return null;
68 | }
69 | hexString = hexString.toUpperCase();
70 | int length = hexString.length() / 2;
71 | char[] hexChars = hexString.toCharArray();
72 | byte[] d = new byte[length];
73 | for (int i = 0; i < length; i++) {
74 | int pos = i * 2;
75 | d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
76 | }
77 | return d;
78 | }
79 |
80 | /**
81 | * 字符串转换为Ascii
82 | *
83 | * @param hexStr
84 | * @return
85 | */
86 | public static String hexToAscii(String hexStr) {
87 | StringBuilder output = new StringBuilder("");
88 | for (int i = 0; i < hexStr.length(); i += 2) {
89 | String str = hexStr.substring(i, i + 2);
90 | output.append((char) Integer.parseInt(str, 16));
91 | }
92 | return output.toString();
93 | }
94 |
95 | /**
96 | * Ascii转换为字符串
97 | *
98 | * @param asciiValue
99 | * @return
100 | */
101 |
102 | public static String asciiToString(String asciiValue) {
103 | StringBuffer sbu = new StringBuffer();
104 |
105 | String[] chars = asciiValue.split(",");
106 |
107 | for (int i = 0; i < chars.length; i++) {
108 | sbu.append((char) Integer.parseInt(chars[i]));
109 |
110 | }
111 |
112 | return sbu.toString();
113 |
114 | }
115 |
116 | private static byte charToByte(char c) {
117 | return (byte) "0123456789ABCDEF".indexOf(c);
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/resources/extension.properties:
--------------------------------------------------------------------------------
1 | com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService=com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.MyUserServicePluginImpl
--------------------------------------------------------------------------------
/example-plugin-tcptomqtt/src/main/resources/pluginMeta.properties:
--------------------------------------------------------------------------------
1 | # plugin boot class
2 | plugin.boot.class=com.mqttsnet.thinglinks.open.exp.example.tcptomqtt.Boot
3 | # plugin code Cannot be null
4 | plugin.code=example.plugin.tcptomqtt
5 | # description
6 | plugin.desc=this a plugin a v1 demo
7 | # version
8 | plugin.version=1.0.0
9 | # extension
10 | plugin.ext=null
11 | # Classloading mode(parent-first || self-first), default: parent-first
12 | #plugin.classLoader.mode=self-first
--------------------------------------------------------------------------------
/example-plugin-udpserver/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/example-plugin-udpserver/README.md
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/Boot.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.client.ConfigSupport;
4 | import com.mqttsnet.thinglinks.open.exp.plugin.depend.AbstractBoot;
5 |
6 | public class Boot extends AbstractBoot {
7 | /**
8 | * udpServer启动的端口号
9 | */
10 | public static ConfigSupport udpPort = new ConfigSupport("udp.port", "9999");
11 | }
12 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/BootNettyUdpAbstractBootstrapServer.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import io.netty.channel.ChannelPipeline;
4 |
5 | /**
6 | * @author lin
7 | * @date 2024年08月30日 16:59
8 | */
9 | public abstract class BootNettyUdpAbstractBootstrapServer implements BootNettyUdpBootstrap {
10 | void initChannelHandler(ChannelPipeline channelPipeline) {
11 | channelPipeline.addLast(new BootNettyUdpSimpleChannelInboundHandler());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/BootNettyUdpBootstrap.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | /**
4 | * @author lin
5 | * @date 2024年08月30日 17:00
6 | */
7 | public interface BootNettyUdpBootstrap {
8 | void startup(int port);
9 |
10 | void shutdown();
11 | }
12 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/BootNettyUdpBootstrapServer.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.ChannelInitializer;
6 | import io.netty.channel.ChannelOption;
7 | import io.netty.channel.EventLoopGroup;
8 | import io.netty.channel.nio.NioEventLoopGroup;
9 | import io.netty.channel.socket.nio.NioDatagramChannel;
10 |
11 | /**
12 | * @author lin
13 | * @date 2024年08月30日 16:40
14 | */
15 | public class BootNettyUdpBootstrapServer extends BootNettyUdpAbstractBootstrapServer{
16 | private EventLoopGroup eventLoopGroup;
17 |
18 | /**
19 | * 启动服务
20 | */
21 | public void startup(int port) {
22 |
23 | eventLoopGroup = new NioEventLoopGroup(20);
24 | try {
25 | Bootstrap serverBootstrap = new Bootstrap();
26 | serverBootstrap = serverBootstrap.group(eventLoopGroup);
27 | serverBootstrap = serverBootstrap.channel(NioDatagramChannel.class);
28 | serverBootstrap = serverBootstrap.option(ChannelOption.SO_BROADCAST, true);
29 | serverBootstrap = serverBootstrap.handler(new ChannelInitializer(){
30 | @Override
31 | protected void initChannel(NioDatagramChannel ch) throws Exception {
32 | initChannelHandler(ch.pipeline());
33 | }
34 | });
35 | ChannelFuture f = serverBootstrap.bind(port).sync();
36 | if(f.isSuccess()) {
37 | System.out.println("netty udp start "+port);
38 | f.channel().closeFuture().sync();
39 | }
40 | } catch (Exception e) {
41 | // TODO: handle exception
42 | e.printStackTrace();
43 | } finally {
44 | System.out.println("netty udp close!");
45 | eventLoopGroup.shutdownGracefully();
46 | }
47 | }
48 |
49 | /**
50 | * 关闭服务
51 | */
52 | public void shutdown() {
53 | eventLoopGroup.shutdownGracefully();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/BootNettyUdpBootstrapThread.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | /**
4 | * @author lin
5 | * @date 2024年08月30日 17:01
6 | */
7 | public class BootNettyUdpBootstrapThread extends Thread{
8 | private final int port;
9 |
10 | public BootNettyUdpBootstrapThread(int port){
11 | this.port = port;
12 | }
13 |
14 | private BootNettyUdpBootstrap iotUdpBootstrap;
15 | public void run() {
16 | if (iotUdpBootstrap == null){
17 | iotUdpBootstrap = new BootNettyUdpBootstrapServer();
18 | }
19 | iotUdpBootstrap.startup(this.port);
20 | }
21 |
22 | public void shutdown(){
23 | if (iotUdpBootstrap != null){
24 | iotUdpBootstrap.shutdown();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/BootNettyUdpData.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author lin
7 | * @date 2024年08月30日 17:02
8 | */
9 | @Data
10 | public class BootNettyUdpData {
11 | private String address;
12 |
13 | private String content;
14 |
15 | private long time_stamp;
16 | }
17 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/BootNettyUdpDataCache.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * @author lin
8 | * @date 2024年08月30日 17:02
9 | */
10 | public class BootNettyUdpDataCache {
11 | public static List bootNettyUdpDataList = new ArrayList<>();
12 | }
13 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/BootNettyUdpSimpleChannelInboundHandler.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import io.netty.buffer.Unpooled;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.SimpleChannelInboundHandler;
6 | import io.netty.channel.socket.DatagramPacket;
7 | import io.netty.util.CharsetUtil;
8 |
9 | /**
10 | * @author lin
11 | * @date 2024年08月30日 17:03
12 | */
13 | public class BootNettyUdpSimpleChannelInboundHandler extends SimpleChannelInboundHandler {
14 | @Override
15 | protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
16 | long time_stamp = System.currentTimeMillis()/1000;
17 | System.out.println("received--data:"+packet.content().toString(CharsetUtil.UTF_8)+"--ip:"+packet.sender().getAddress()+"--port:"+packet.sender().getPort());
18 | try {
19 | BootNettyUdpData bootNettyUdpData = new BootNettyUdpData();
20 | bootNettyUdpData.setAddress(packet.sender().getAddress().toString());
21 | bootNettyUdpData.setContent(packet.content().toString(CharsetUtil.UTF_8));
22 | bootNettyUdpData.setTime_stamp(time_stamp);
23 | BootNettyUdpDataCache.bootNettyUdpDataList.add(bootNettyUdpData);
24 | ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(time_stamp+"", CharsetUtil.UTF_8), packet.sender()));
25 | } catch (Exception e) {
26 | ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("exception", CharsetUtil.UTF_8), packet.sender()));
27 | System.out.println("received exception--data:"+packet.content().toString(CharsetUtil.UTF_8)+"--"+e.toString());
28 | }
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/MyComponent.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import jakarta.annotation.PostConstruct;
4 | import jakarta.annotation.PreDestroy;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.stereotype.Component;
7 |
8 | import java.util.concurrent.ScheduledExecutorService;
9 | import java.util.concurrent.ScheduledThreadPoolExecutor;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | @Slf4j
13 | @Component
14 | public class MyComponent {
15 | ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
16 |
17 | BootNettyUdpBootstrapThread bootNettyUdpBootstrapThread = null;
18 | @PostConstruct
19 | public void init() {
20 | String udpPort = Boot.udpPort.getDefaultValue();
21 | log.info("start UdpServer plugin...");
22 | if (bootNettyUdpBootstrapThread == null){
23 | bootNettyUdpBootstrapThread = new BootNettyUdpBootstrapThread(Integer.valueOf(udpPort));
24 | }
25 | bootNettyUdpBootstrapThread.start();
26 | scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
27 | @Override
28 | public void run() {
29 | System.out.println("hello");
30 | }
31 | }, 1, 1, TimeUnit.SECONDS);
32 | }
33 |
34 | @PreDestroy
35 | public void aVoid() {
36 | log.info("插件关闭了");
37 | if (bootNettyUdpBootstrapThread != null){
38 | bootNettyUdpBootstrapThread.shutdown();
39 | }
40 | scheduledExecutorService.shutdown();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/MyUserServicePluginImpl.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * @author cxs
9 | */
10 | @Slf4j
11 | @Component
12 | public class MyUserServicePluginImpl implements UserService {
13 |
14 | @Override
15 | public void createUserExt() {
16 | log.info("create user ext");
17 | }
18 |
19 | @Override
20 | public String getName() {
21 | return MyUserServicePluginImpl.class.getName();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/UdpClient.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import org.springframework.web.bind.annotation.PostMapping;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RequestParam;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | import java.io.IOException;
9 | import java.net.DatagramPacket;
10 | import java.net.DatagramSocket;
11 | import java.net.InetAddress;
12 | import java.net.SocketException;
13 |
14 | /**
15 | * @author lin
16 | * @date 2024年08月30日 17:23
17 | */
18 | @RestController
19 | @RequestMapping("/UdpClient")
20 | public class UdpClient {
21 | @PostMapping("/send")
22 | public void send(@RequestParam("message") String message) {
23 | try (DatagramSocket socket = new DatagramSocket()) {
24 | // 将要发送的消息转换为字节数组
25 | byte[] buf = message.getBytes();
26 | // 创建一个DatagramPacket对象,用于封装要发送的数据
27 | InetAddress address = InetAddress.getByName("localhost");
28 | String udpPort = Boot.udpPort.getDefaultValue();
29 | DatagramPacket packet = new DatagramPacket(buf, buf.length, address, Integer.valueOf(udpPort));
30 | // 通过DatagramSocket发送数据包
31 | socket.send(packet);
32 | System.out.println("Message sent to server: " + message);
33 | } catch (SocketException e) {
34 | e.printStackTrace();
35 | } catch (IOException e) {
36 | e.printStackTrace();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udpserver/UdpServer.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udpserver;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | import java.io.IOException;
6 | import java.net.DatagramPacket;
7 | import java.net.DatagramSocket;
8 | import java.net.SocketException;
9 |
10 | /**
11 | * @author lin
12 | * @date 2024年08月30日 15:30
13 | */
14 | @Component
15 | public class UdpServer{
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/resources/extension.properties:
--------------------------------------------------------------------------------
1 | com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService=com.mqttsnet.thinglinks.open.exp.example.udpserver.MyUserServicePluginImpl
--------------------------------------------------------------------------------
/example-plugin-udpserver/src/main/resources/pluginMeta.properties:
--------------------------------------------------------------------------------
1 | # plugin boot class
2 | plugin.boot.class=com.mqttsnet.thinglinks.open.exp.example.udpserver.Boot
3 | # plugin code Cannot be null
4 | plugin.code=example.plugin.udpserver
5 | # description
6 | plugin.desc=this a plugin a v1 demo
7 | # version
8 | plugin.version=1.0.0
9 | # extension
10 | plugin.ext=null
11 | # Classloading mode(parent-first || self-first), default: parent-first
12 | #plugin.classLoader.mode=self-first
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/example-plugin-udptomqtt/README.md
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/Boot.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 |
4 | import com.mqttsnet.thinglinks.open.exp.client.ConfigSupport;
5 | import com.mqttsnet.thinglinks.open.exp.plugin.depend.AbstractBoot;
6 |
7 | public class Boot extends AbstractBoot {
8 | /**
9 | * udpServer启动的端口号
10 | */
11 | public static ConfigSupport udpPort = new ConfigSupport("udp.port", "9998");
12 | /**
13 | * mqtt broker地址
14 | */
15 | public static ConfigSupport mqttBrokerUrl = new ConfigSupport("mqtt.brokerUrl", "tcp://127.0.0.1:1883");
16 | /**
17 | * 订阅的topic
18 | */
19 | public static ConfigSupport mqttTopic = new ConfigSupport("mqtt.topic", "test/zjl");
20 | /**
21 | * 客户端id
22 | */
23 | public static ConfigSupport mqttClientId = new ConfigSupport("mqtt.clientId", "mqttx_cf427182");
24 | }
25 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/BootNettyUdpAbstractBootstrapServer.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import io.netty.channel.ChannelPipeline;
4 |
5 | /**
6 | * @author lin
7 | * @date 2024年08月30日 16:59
8 | */
9 | public abstract class BootNettyUdpAbstractBootstrapServer implements BootNettyUdpBootstrap {
10 | void initChannelHandler(ChannelPipeline channelPipeline) {
11 | channelPipeline.addLast(new BootNettyUdpSimpleChannelInboundHandler());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/BootNettyUdpBootstrap.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | /**
4 | * @author lin
5 | * @date 2024年08月30日 17:00
6 | */
7 | public interface BootNettyUdpBootstrap {
8 | void startup(int port);
9 |
10 | void shutdown();
11 | }
12 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/BootNettyUdpBootstrapServer.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.ChannelInitializer;
6 | import io.netty.channel.ChannelOption;
7 | import io.netty.channel.EventLoopGroup;
8 | import io.netty.channel.nio.NioEventLoopGroup;
9 | import io.netty.channel.socket.nio.NioDatagramChannel;
10 |
11 | /**
12 | * @author lin
13 | * @date 2024年08月30日 16:40
14 | */
15 | public class BootNettyUdpBootstrapServer extends BootNettyUdpAbstractBootstrapServer{
16 | private EventLoopGroup eventLoopGroup;
17 |
18 | /**
19 | * 启动服务
20 | */
21 | public void startup(int port) {
22 |
23 | eventLoopGroup = new NioEventLoopGroup(20);
24 | try {
25 | Bootstrap serverBootstrap = new Bootstrap();
26 | serverBootstrap = serverBootstrap.group(eventLoopGroup);
27 | serverBootstrap = serverBootstrap.channel(NioDatagramChannel.class);
28 | serverBootstrap = serverBootstrap.option(ChannelOption.SO_BROADCAST, true);
29 | serverBootstrap = serverBootstrap.handler(new ChannelInitializer(){
30 | @Override
31 | protected void initChannel(NioDatagramChannel ch) throws Exception {
32 | initChannelHandler(ch.pipeline());
33 | }
34 | });
35 | ChannelFuture f = serverBootstrap.bind(port).sync();
36 | if(f.isSuccess()) {
37 | System.out.println("netty udp start "+port);
38 | f.channel().closeFuture().sync();
39 | }
40 | } catch (Exception e) {
41 | // TODO: handle exception
42 | e.printStackTrace();
43 | } finally {
44 | System.out.println("netty udp close!");
45 | eventLoopGroup.shutdownGracefully();
46 | }
47 | }
48 |
49 | /**
50 | * 关闭服务
51 | */
52 | public void shutdown() {
53 | eventLoopGroup.shutdownGracefully();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/BootNettyUdpBootstrapThread.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | /**
4 | * @author lin
5 | * @date 2024年08月30日 17:01
6 | */
7 | public class BootNettyUdpBootstrapThread extends Thread{
8 | private final int port;
9 |
10 | public BootNettyUdpBootstrapThread(int port){
11 | this.port = port;
12 | }
13 |
14 | private BootNettyUdpBootstrap iotUdpBootstrap;
15 | public void run() {
16 | if (iotUdpBootstrap == null){
17 | iotUdpBootstrap = new BootNettyUdpBootstrapServer();
18 | }
19 | iotUdpBootstrap.startup(this.port);
20 | }
21 |
22 | public void shutdown(){
23 | if (iotUdpBootstrap != null){
24 | iotUdpBootstrap.shutdown();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/BootNettyUdpData.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author lin
7 | * @date 2024年08月30日 17:02
8 | */
9 | @Data
10 | public class BootNettyUdpData {
11 | private String address;
12 |
13 | private String content;
14 |
15 | private long time_stamp;
16 | }
17 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/BootNettyUdpDataCache.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * @author lin
8 | * @date 2024年08月30日 17:02
9 | */
10 | public class BootNettyUdpDataCache {
11 | public static List bootNettyUdpDataList = new ArrayList<>();
12 | }
13 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/BootNettyUdpSimpleChannelInboundHandler.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.SimpleChannelInboundHandler;
5 | import io.netty.channel.socket.DatagramPacket;
6 | import io.netty.util.CharsetUtil;
7 | import org.eclipse.paho.client.mqttv3.*;
8 |
9 | /**
10 | * @author lin
11 | * @date 2024年08月30日 17:03
12 | */
13 | public class BootNettyUdpSimpleChannelInboundHandler extends SimpleChannelInboundHandler {
14 | @Override
15 | protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
16 | String message = packet.content().toString(CharsetUtil.UTF_8);
17 | publishToMqtt(message);
18 | }
19 |
20 | private MqttClient mqttClient;
21 |
22 | private void publishToMqtt(String message) {
23 | String brokerUrl = Boot.mqttBrokerUrl.getDefaultValue();
24 | String topic = Boot.mqttTopic.getDefaultValue();
25 | String clientId = Boot.mqttClientId.getDefaultValue();
26 | try {
27 | if (mqttClient == null || ! mqttClient.isConnected()) {
28 | mqttClient = new MqttClient(brokerUrl, clientId);
29 | mqttClient.connect();
30 | mqttClient.setCallback(new MqttCallback() {
31 | @Override
32 | public void connectionLost(Throwable cause) {
33 | System.out.println("Connection lost");
34 | }
35 |
36 | @Override
37 | public void messageArrived(String topic, MqttMessage message) throws Exception {
38 | System.out.println("Topic:" + topic);
39 | byte[] payload = message.getPayload();
40 | System.out.println("Message Arrived:" + new String(payload));
41 | }
42 |
43 | @Override
44 | public void deliveryComplete(IMqttDeliveryToken token) {
45 | System.out.println("Message delivered");
46 | }
47 | });
48 | }
49 | MqttMessage mqttMessage = new MqttMessage(message.getBytes());
50 | mqttClient.publish(topic, mqttMessage);
51 | } catch (Exception e) {
52 | e.printStackTrace();
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/MyComponent.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import jakarta.annotation.PostConstruct;
4 | import jakarta.annotation.PreDestroy;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.stereotype.Component;
7 |
8 | import java.util.concurrent.ScheduledExecutorService;
9 | import java.util.concurrent.ScheduledThreadPoolExecutor;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | @Slf4j
13 | @Component
14 | public class MyComponent {
15 | ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
16 |
17 | BootNettyUdpBootstrapThread bootNettyUdpBootstrapThread = null;
18 | @PostConstruct
19 | public void init() {
20 | String udpPort = Boot.udpPort.getDefaultValue();
21 | log.info("start、 plugin...");
22 | if (bootNettyUdpBootstrapThread == null){
23 | bootNettyUdpBootstrapThread = new BootNettyUdpBootstrapThread(Integer.valueOf(udpPort));
24 | }
25 | bootNettyUdpBootstrapThread.start();
26 | scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
27 | @Override
28 | public void run() {
29 | System.out.println("hello");
30 | }
31 | }, 1, 1, TimeUnit.SECONDS);
32 | }
33 |
34 | @PreDestroy
35 | public void aVoid() {
36 | log.info("插件关闭了");
37 | if (bootNettyUdpBootstrapThread != null){
38 | bootNettyUdpBootstrapThread.shutdown();
39 | }
40 | scheduledExecutorService.shutdown();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/MyUserServicePluginImpl.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * @author cxs
9 | */
10 | @Slf4j
11 | @Component
12 | public class MyUserServicePluginImpl implements UserService {
13 |
14 | @Override
15 | public void createUserExt() {
16 | log.info("create user ext");
17 | }
18 |
19 | @Override
20 | public String getName() {
21 | return MyUserServicePluginImpl.class.getName();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/UdpClient.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import org.springframework.web.bind.annotation.PostMapping;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RequestParam;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | import java.io.IOException;
9 | import java.net.DatagramPacket;
10 | import java.net.DatagramSocket;
11 | import java.net.InetAddress;
12 | import java.net.SocketException;
13 |
14 | /**
15 | * @author lin
16 | * @date 2024年08月30日 17:23
17 | */
18 | @RestController
19 | @RequestMapping("/UdpClient")
20 | public class UdpClient {
21 | @PostMapping("/sendMQTT")
22 | public void sendMQTT(@RequestParam("message") String message) {
23 | try (DatagramSocket socket = new DatagramSocket()) {
24 | // 将要发送的消息转换为字节数组
25 | byte[] buf = message.getBytes();
26 | // 创建一个DatagramPacket对象,用于封装要发送的数据
27 | InetAddress address = InetAddress.getByName("localhost");
28 | String udpPort = Boot.udpPort.getDefaultValue();
29 | DatagramPacket packet = new DatagramPacket(buf, buf.length, address, Integer.valueOf(udpPort));
30 | // 通过DatagramSocket发送数据包
31 | socket.send(packet);
32 | System.out.println("Message sent to server: " + message);
33 | } catch (SocketException e) {
34 | e.printStackTrace();
35 | } catch (IOException e) {
36 | e.printStackTrace();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/java/com/mqttsnet/thinglinks/open/exp/example/udptomqtt/UdpServer.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.example.udptomqtt;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | import java.io.IOException;
6 | import java.net.DatagramPacket;
7 | import java.net.DatagramSocket;
8 | import java.net.SocketException;
9 |
10 | /**
11 | * @author lin
12 | * @date 2024年08月30日 15:30
13 | */
14 | @Component
15 | public class UdpServer{
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/resources/extension.properties:
--------------------------------------------------------------------------------
1 | com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService=com.mqttsnet.thinglinks.open.exp.example.udptomqtt.MyUserServicePluginImpl
--------------------------------------------------------------------------------
/example-plugin-udptomqtt/src/main/resources/pluginMeta.properties:
--------------------------------------------------------------------------------
1 | # plugin boot class
2 | plugin.boot.class=com.mqttsnet.thinglinks.open.exp.example.udptomqtt.Boot
3 | # plugin code Cannot be null
4 | plugin.code=example.plugin.udptomqtt
5 | # description
6 | plugin.desc=this a plugin a v1 demo
7 | # version
8 | plugin.version=1.0.0
9 | # extension
10 | plugin.ext=null
11 | # Classloading mode(parent-first || self-first), default: parent-first
12 | #plugin.classLoader.mode=self-first
--------------------------------------------------------------------------------
/example-springboot3/README.md:
--------------------------------------------------------------------------------
1 |
2 | # **插件管理接口使用指南**
3 |
4 | ## **介绍**
5 | 本接口是为 **mqttsnet** 社区的插件开发人员提供的一组便捷接口,旨在帮助用户测试插件的预加载、安装、卸载等功能。开发者可以通过这些接口在测试环境中验证插件的功能及兼容性,并确保插件在不同租户和环境下的正常运行。
6 |
7 | ## **接口概览**
8 | - `/base/hello`:测试服务是否可用。
9 | - `/base/run`:执行租户相关的用户服务操作。
10 | - `/base/preload`:预加载插件,支持从远程URL加载。
11 | - `/base/install`:安装插件,支持从远程URL下载并安装。
12 | - `/base/unInstall`:卸载插件。
13 |
14 | ## **快速开始**
15 |
16 | ### **1. Hello 测试接口**
17 |
18 | **URL**: `/base/hello`
19 |
20 | **请求方式**: `GET`
21 |
22 | **描述**: 测试系统是否正常运行,返回基本的响应模型。
23 |
24 | **示例**:
25 | ```bash
26 | curl http://localhost:18888/base/hello
27 | ```
28 |
29 | **响应**:
30 | ```json
31 | {
32 | "message": "Hello from the system!"
33 | }
34 | ```
35 |
36 | ---
37 |
38 | ### **2. 运行租户操作 (`/run`)**
39 |
40 | **URL**: `/base/run`
41 |
42 | **请求方式**: `GET`
43 |
44 | **参数**:
45 | - `tenantId` (必填): 租户的唯一标识符,用于指定哪个租户执行相关操作。
46 |
47 | **描述**: 根据传入的租户ID,执行与该租户相关的操作,主要用于创建用户扩展服务。
48 |
49 | **示例**:
50 | ```bash
51 | curl "http://localhost:18888/base/run?tenantId=12345"
52 | ```
53 |
54 | **响应**:
55 | - 成功:`"success"`
56 | - 失败:`"not found"`
57 |
58 | ---
59 |
60 | ### **3. 预加载插件 (`/preload`)**
61 |
62 | **URL**: `/base/preload`
63 |
64 | **请求方式**: `GET`
65 |
66 | **参数**:
67 | - `path` (必填): 插件的本地路径或远程URL地址,支持本地文件和远程HTTP下载。
68 |
69 | **描述**: 预加载插件文件,确保在正式安装前完成所有必要的预处理。如果路径是URL,将自动下载插件并进行预加载。
70 |
71 | **示例**:
72 | ```bash
73 | curl "http://localhost:18888/base/preload?path=http://example.com/plugins/example-plugin.jar"
74 | ```
75 |
76 | **响应**:
77 | ```json
78 | {
79 | "pluginId": "example-plugin_1.0.0",
80 | "status": "Preloaded"
81 | }
82 | ```
83 |
84 | ---
85 |
86 | ### **4. 安装插件 (`/install`)**
87 |
88 | **URL**: `/base/install`
89 |
90 | **请求方式**: `GET`
91 |
92 | **参数**:
93 | - `path` (必填): 插件的本地路径或远程URL地址,支持本地文件和远程HTTP下载。
94 | - `tenantId` (必填): 租户的唯一标识符,表示该插件安装到哪个租户下。
95 |
96 | **描述**: 安装插件,并为指定租户关联该插件。支持从远程URL下载插件并完成安装。
97 |
98 | **示例**:
99 | ```bash
100 | curl "http://localhost:18888/base/install?path=http://example.com/plugins/example-plugin.jar&tenantId=12345"
101 | ```
102 |
103 | **响应**:
104 | ```json
105 | {
106 | "pluginId": "example-plugin_1.0.0",
107 | "status": "Installed"
108 | }
109 | ```
110 |
111 | ---
112 |
113 | ### **5. 卸载插件 (`/unInstall`)**
114 |
115 | **URL**: `/base/unInstall`
116 |
117 | **请求方式**: `GET`
118 |
119 | **参数**:
120 | - `pluginId` (必填): 插件的唯一标识符,表示需要卸载的插件。
121 |
122 | **描述**: 卸载指定的插件,并清理该插件与租户的关联。
123 |
124 | **示例**:
125 | ```bash
126 | curl "http://localhost:18888/base/unInstall?pluginId=example-plugin_1.0.0"
127 | ```
128 |
129 | **响应**:
130 | ```json
131 | {
132 | "status": "ok"
133 | }
134 | ```
135 |
136 | ---
137 |
138 | ## **使用场景**
139 |
140 | ### **插件开发测试**
141 |
142 | 插件开发者可以使用 `/preload` 和 `/install` 接口来测试插件的预加载和安装功能,确保插件在实际部署前能够正常工作。通过这些接口,开发者能够模拟从远程服务器下载插件并加载到系统中。
143 |
144 | ### **插件卸载**
145 |
146 | 开发者通过 `/unInstall` 接口可以快速卸载已经安装的插件,适合在开发和测试阶段频繁安装和卸载插件的场景。
147 |
148 | ### **租户操作**
149 |
150 | 通过 `/run` 接口,开发者可以模拟插件在不同租户下的操作,确保插件能够正确地处理租户信息,并执行相应的业务逻辑。
151 |
152 | ---
153 |
154 | ## **错误处理**
155 |
156 | 所有接口的错误信息将通过标准的 HTTP 状态码和 JSON 错误响应返回,示例:
157 | ```json
158 | {
159 | "error": "Plugin not found",
160 | "message": "The requested plugin was not found in the system."
161 | }
162 | ```
163 |
164 | ---
165 |
166 | ## **反馈与支持**
167 |
168 | 本项目由 **mqttsnet** 社区维护和支持。
169 | 如果在使用过程中遇到问题或需要帮助,请在 [mqttsnet GitHub 社区](https://github.com/mqttsnet) 提交问题或反馈。
170 | 社区开发者将尽快帮助您解决问题,并提供相关支持。
171 |
172 | ---
173 |
174 | ## **总结**
175 |
176 | 本插件管理接口为开发者提供了简单、方便的方式来测试插件的预加载、安装、卸载和运行逻辑。通过这些接口,开发者可以轻松验证插件在不同租户和环境下的正常运行,并确保插件的高效、稳定运行。
177 | 欢迎大家积极参与 **mqttsnet** 社区的讨论和开发,共同打造高质量的插件生态系统。
178 |
--------------------------------------------------------------------------------
/example-springboot3/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.mqttsnet.thinglinks
8 | open-exp-plugin
9 | 1.0.0
10 |
11 |
12 | example-springboot3
13 |
14 |
15 | 17
16 | 17
17 | UTF-8
18 | UTF-8
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-dependencies
27 | 3.4.4
28 | import
29 | pom
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-web
39 | 3.4.4
40 |
41 |
42 |
43 | com.mqttsnet.thinglinks
44 | exp-plugin-manager-springboot-starter
45 | 1.0.9
46 |
47 |
48 |
49 | com.mqttsnet.thinglinks
50 | example-extension-define
51 | 1.0.0
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/example-springboot3/src/main/java/com/mqttsnet/thinglinks/open/exp/adapter/springboot3/HttpFileDownloader.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.adapter.springboot3;
2 |
3 | import java.io.FileOutputStream;
4 | import java.io.InputStream;
5 | import java.net.HttpURLConnection;
6 | import java.net.URL;
7 | import java.net.URLDecoder;
8 |
9 | public class HttpFileDownloader {
10 |
11 | public static void download(String fileURL, String savePath) {
12 | if (!savePath.startsWith("/") && !fileURL.startsWith("http")) {
13 | throw new RuntimeException("path 错误" + savePath);
14 | }
15 |
16 | try {
17 | if (fileURL.contains("%")) {
18 | fileURL = URLDecoder.decode(fileURL, "UTF-8");
19 | }
20 | URL url = new URL(fileURL);
21 | HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
22 | int responseCode = httpConn.getResponseCode();
23 |
24 | if (responseCode == HttpURLConnection.HTTP_OK) {
25 | InputStream inputStream = httpConn.getInputStream();
26 | FileOutputStream outputStream = new FileOutputStream(savePath);
27 |
28 | int bytesRead;
29 | byte[] buffer = new byte[4096];
30 |
31 | while ((bytesRead = inputStream.read(buffer)) != -1) {
32 | outputStream.write(buffer, 0, bytesRead);
33 | }
34 |
35 | outputStream.close();
36 | inputStream.close();
37 | }
38 | httpConn.disconnect();
39 | } catch (Exception e) {
40 | throw new RuntimeException(e);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/example-springboot3/src/main/java/com/mqttsnet/thinglinks/open/exp/adapter/springboot3/Main.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.adapter.springboot3;
2 |
3 | import java.util.List;
4 | import java.util.Optional;
5 |
6 | import com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService;
7 | import com.mqttsnet.thinglinks.open.exp.client.ExpAppContext;
8 | import com.mqttsnet.thinglinks.open.exp.client.ExpAppContextSpiFactory;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.springframework.boot.SpringApplication;
11 | import org.springframework.boot.autoconfigure.SpringBootApplication;
12 | import org.springframework.context.annotation.ComponentScan;
13 | import org.springframework.scheduling.annotation.EnableAsync;
14 | import org.springframework.web.bind.annotation.RequestMapping;
15 | import org.springframework.web.bind.annotation.RestController;
16 |
17 | /**
18 | * spring boot 3 需要jdk的版本至少为17
19 | * VM --add-opens java.base/java.lang=ALL-UNNAMED
20 | **/
21 | @Slf4j
22 | @SpringBootApplication
23 | @ComponentScan(basePackages = {"com.mqttsnet.thinglinks"})
24 | @EnableAsync
25 | public class Main {
26 |
27 | public static void main(String[] args) {
28 | SpringApplication.run(Main.class, args);
29 | }
30 |
31 | @RestController
32 | @RequestMapping("/hello")
33 | class Cont {
34 |
35 | ExpAppContext expAppContext = ExpAppContextSpiFactory.getFirst();
36 |
37 | @RequestMapping("/hi")
38 | public User s() {
39 | List userServices = expAppContext.streamOne(UserService.class);
40 | Optional first = userServices.stream().findFirst();
41 | if (first.isPresent()) {
42 | first.get().createUserExt();
43 | } else {
44 | log.info("no user service found");
45 | }
46 | return new User();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/example-springboot3/src/main/java/com/mqttsnet/thinglinks/open/exp/adapter/springboot3/PluginConfigImpl.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.adapter.springboot3;
2 |
3 | import com.mqttsnet.thinglinks.open.exp.client.PluginConfig;
4 |
5 | /**
6 | * @version 1.0
7 | * @Author cxs
8 | * @Description
9 | * @date 2023/8/12
10 | **/
11 | public class PluginConfigImpl implements PluginConfig {
12 | @Override
13 | public String getProperty(String pluginId, String key, String defaultValue) {
14 | return pluginId + " --->>> " + key;
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/example-springboot3/src/main/java/com/mqttsnet/thinglinks/open/exp/adapter/springboot3/ResModel.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.adapter.springboot3;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @version 1.0
7 | * @Author cxs
8 | * @Description
9 | * @date 2023/8/14
10 | **/
11 | @Data
12 | public class ResModel {
13 |
14 | String name;
15 |
16 | int age;
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/example-springboot3/src/main/java/com/mqttsnet/thinglinks/open/exp/adapter/springboot3/User.java:
--------------------------------------------------------------------------------
1 | package com.mqttsnet.thinglinks.open.exp.adapter.springboot3;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @version 1.0
7 | * @Author cxs
8 | * @Description
9 | * @date 2023/8/25
10 | **/
11 | @Data
12 | public class User {
13 |
14 | String name;
15 | }
16 |
--------------------------------------------------------------------------------
/example-springboot3/src/main/resources/META-INF/services/com.mqttsnet.thinglinks.open.exp.client.PluginConfig:
--------------------------------------------------------------------------------
1 | com.mqttsnet.thinglinks.open.exp.adapter.springboot3.PluginConfigImpl
--------------------------------------------------------------------------------
/example-springboot3/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | # 是否自启动exp
2 | exp_enable: true
3 | # springboot 启动时, exp主动加载的插件目录
4 | plugins_path: exp-plugins
5 | # exp 的工作目录, 其会将代码解压达成这个目录里,子目录名为插件 id
6 | plugins_work_dir: exp-workdir
7 | # 是否自动删除已经存在的 plugin 目录
8 | plugins_auto_delete_enable: true
9 | # 插件是否可以覆盖主程序 url, 注意, 目前无法支持多租户级别的覆盖
10 | plugins_spring_url_replace_enable: true
11 | # 插件动态增加字段json
12 | exp_object_field_config_json: '[
13 | {
14 | "className": "com.mqttsnet.thinglinks.open.exp.adapter.springboot3.ResModel",
15 | "fieldModelsDesc": [{
16 | "fieldType": "java.lang.String",
17 | "fieldName": "email"
18 | }]
19 | }
20 | ]'
21 |
22 |
23 | server:
24 | port: 18888
25 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | com.mqttsnet.thinglinks
9 | open-exp-plugin
10 | 1.0.0
11 | pom
12 |
13 |
14 | open-exp-plugin
15 | http://www.mqttsnet.com
16 | A comprehensive collection of modular and hot-swappable plugin examples, designed to demonstrate the
17 | dynamic extension and customization capabilities of the ThingLinks platform using the Open EXP framework.
18 |
19 |
20 |
21 |
22 | example-extension-define
23 |
24 |
25 | example-plugin-demo
26 |
27 |
28 |
29 |
30 |
31 | example-plugin-tcptomqtt
32 |
33 |
34 |
35 |
36 | example-springboot3
37 |
38 |
39 |
40 | 17
41 | 17
42 | UTF-8
43 | UTF-8
44 |
45 |
46 |
47 |
48 | central
49 | https://repo.maven.apache.org/maven2
50 |
51 |
52 | aliyun
53 | aliyun
54 | https://maven.aliyun.com/repository/public
55 |
56 |
57 | spring-milestones
58 | Spring Milestones
59 | https://repo.spring.io/libs-milestone
60 |
61 |
62 | oss-public
63 | https://oss.sonatype.org/content/repositories/public
64 |
65 |
66 |
67 |
68 |
69 | aliyun-plugin
70 | https://maven.aliyun.com/repository/public
71 |
72 | false
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------