├── .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 | [![MQTTSNET Logo](./docs/images/logo.png)](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 | ![img_1.png](docs/images/img_1.png) 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 | ![img_2.png](docs/images/img_2.png) 34 | 35 | 4. **After Packaging**: The plugin packages are by default generated in the `exp-plugins` directory. 36 | 37 | ![img_3.png](docs/images/img_3.png) 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 | ![img_4.png](docs/images/img_4.png) 47 | Configuration usage: 48 | ![img_5.png](docs/images/img_5.png) 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 | ![img_8.png](docs/images/img_8.png) 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 | ![img_7.png](docs/images/img_7.png) 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 | [![MQTTSNET Logo](./docs/images/logo.png)](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 | ![img_1.png](docs/images/img_1.png) 29 | 30 | 2. **刷新项目**:重新加载所有 Maven 项目,确保依赖正确加载。 31 | 32 | 3. **清理打包**:对 `all-package` 进行清理打包操作。 33 | 34 | ![img_2.png](docs/images/img_2.png) 35 | 36 | 4. **打包完成后**,插件包默认生成在 `exp-plugins` 目录下。 37 | 38 | ![img_3.png](docs/images/img_3.png) 39 | 40 | 5. **启动主应用**:运行 `example-springboot3` 模块中的 `Main` 类。 41 | 42 | 主应用会自动加载并安装打包好的插件( 即`exp-plugins` 目录下插件),如果需要重新安装及卸载插件,调用其API即可。 43 | 44 | ### 注意事项 45 | 46 | 1. **配置定义**:插件内的配置需要在 `Boot` 类中定义。 47 | ![img_4.png](docs/images/img_4.png) 48 | 配置的使用方式: 49 | ![img_5.png](docs/images/img_5.png) 50 | 51 | 2. **MQTT 配置**:在 `example-plugin-tcptomqtt` 和 `example-plugin-udptomqtt` 插件中,MQTT 服务端的配置需要根据实际环境进行调整。 52 | ![img_8.png](docs/images/img_8.png) 53 | 54 | 3. **注解导入**:确保插件启动入口的 `@PostConstruct` 和 `@PreDestroy` 注解导入的包是否正确。 55 | ![img_7.png](docs/images/img_7.png) 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 | 5 | 4.0.0 6 | 7 | com.mqttsnet.thinglinks 8 | open-exp-plugin 9 | 1.0.0 10 | 11 | 12 | example-extension-define 13 | 1.0.0 14 | 15 | ${project.artifactId} 16 | https://www.mqttsnet.com 17 | example-extension-define 扩展接口定义 18 | 19 | 20 | 17 21 | 17 22 | UTF-8 23 | UTF-8 24 | 25 | 26 | -------------------------------------------------------------------------------- /example-extension-define/src/main/java/com/mqttsnet/thinglinks/open/exp/adapter/springboot3/example/UserService.java: -------------------------------------------------------------------------------- 1 | package com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example; 2 | 3 | public interface UserService { 4 | 5 | 6 | void createUserExt(); 7 | 8 | String getName(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /example-plugin-demo/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/example-plugin-demo/README.md -------------------------------------------------------------------------------- /example-plugin-demo/src/main/assembly/zip.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | ${project.version} 9 | 10 | 11 | 12 | zip 13 | 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | target/classes/ 22 | / 23 | 24 | 25 | src/main/resources 26 | ./ 27 | 28 | *.properties 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /example-plugin-demo/src/main/java/com/mqttsnet/thinglinks/open/exp/example/a/Boot.java: -------------------------------------------------------------------------------- 1 | package com.mqttsnet.thinglinks.open.exp.example.a; 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 | public static ConfigSupport configSupport = new ConfigSupport("aaa", "111"); 9 | } 10 | -------------------------------------------------------------------------------- /example-plugin-demo/src/main/java/com/mqttsnet/thinglinks/open/exp/example/a/MyComponent.java: -------------------------------------------------------------------------------- 1 | package com.mqttsnet.thinglinks.open.exp.example.a; 2 | 3 | import cn.hutool.core.net.NetUtil; 4 | import jakarta.annotation.PostConstruct; 5 | import jakarta.annotation.PreDestroy; 6 | import jakarta.annotation.Resource; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.time.LocalDateTime; 11 | import java.util.concurrent.ScheduledExecutorService; 12 | import java.util.concurrent.ScheduledThreadPoolExecutor; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | @Slf4j 16 | @Component 17 | public class MyComponent { 18 | 19 | 20 | ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1); 21 | 22 | 23 | @Resource 24 | MyUserServicePluginImpl myUserServicePlugin; 25 | 26 | public MyComponent() { 27 | System.out.println("example-plugin-demo plugin is starting for MyComponent..."); 28 | } 29 | 30 | @PostConstruct 31 | public void init() { 32 | System.out.println("--->>>>"); 33 | log.info("start example-plugin-demo plugin..."); 34 | 35 | scheduledExecutorService.scheduleAtFixedRate(new Runnable() { 36 | @Override 37 | public void run() { 38 | log.info("example-plugin-demo Heartbeat sent successfully at at {} with IP: {}", LocalDateTime.now(), NetUtil.getLocalhostStr()); 39 | } 40 | }, 30, 30, TimeUnit.SECONDS); 41 | } 42 | 43 | @PreDestroy 44 | public void aVoid() { 45 | scheduledExecutorService.shutdown(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /example-plugin-demo/src/main/java/com/mqttsnet/thinglinks/open/exp/example/a/MyUserServicePluginImpl.java: -------------------------------------------------------------------------------- 1 | package com.mqttsnet.thinglinks.open.exp.example.a; 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 | /** 9 | * @author cxs 10 | */ 11 | @Slf4j 12 | @Component 13 | public class MyUserServicePluginImpl implements UserService { 14 | 15 | @jakarta.annotation.PostConstruct 16 | public void init() { 17 | System.out.println("example-plugin-demo plugin is starting for MyUserServicePluginImpl..."); 18 | } 19 | 20 | @Override 21 | public void createUserExt() { 22 | log.info("create user ext"); 23 | } 24 | 25 | @Override 26 | public String getName() { 27 | return MyUserServicePluginImpl.class.getName(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example-plugin-demo/src/main/java/com/mqttsnet/thinglinks/open/exp/example/a/TestController.java: -------------------------------------------------------------------------------- 1 | package com.mqttsnet.thinglinks.open.exp.example.a; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | /** 8 | * @author lin 9 | * @date 2024年08月29日 16:33 10 | */ 11 | @RestController 12 | @RequestMapping("/test") 13 | public class TestController { 14 | @GetMapping("/doTest") 15 | public String doTest(String param){ 16 | return "doTest2_" + param; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example-plugin-demo/src/main/resources/extension.properties: -------------------------------------------------------------------------------- 1 | com.mqttsnet.thinglinks.open.exp.adapter.springboot3.example.UserService=com.mqttsnet.thinglinks.open.exp.example.a.MyUserServicePluginImpl -------------------------------------------------------------------------------- /example-plugin-demo/src/main/resources/pluginMeta.properties: -------------------------------------------------------------------------------- 1 | # plugin boot class 2 | plugin.boot.class=com.mqttsnet.thinglinks.open.exp.example.a.Boot 3 | # plugin code Cannot be null 4 | plugin.code=example.plugin.a 5 | # description 6 | plugin.desc=this a plugin a v1 demo 7 | # version 8 | plugin.version=1.0.0 -------------------------------------------------------------------------------- /example-plugin-tcpserver/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mqttsnet/open-exp-plugin/f6c68e9cee76b9de6a5ba668fe96e43d9663995d/example-plugin-tcpserver/README.md -------------------------------------------------------------------------------- /example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/Boot.java: -------------------------------------------------------------------------------- 1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver; 2 | 3 | import com.mqttsnet.thinglinks.open.exp.client.ConfigSupport; 4 | import com.mqttsnet.thinglinks.open.exp.plugin.depend.AbstractBoot; 5 | 6 | /** 7 | * 启动类,暴露Netty服务器相关的关键配置参数。 8 | */ 9 | public class Boot extends AbstractBoot { 10 | 11 | /** 12 | * 租户ID 13 | */ 14 | public static ConfigSupport tenantId = new ConfigSupport("plugin.tenantId", "1"); 15 | 16 | /** 17 | * TCP Server 启动的端口号 18 | */ 19 | public static ConfigSupport tcpPort = new ConfigSupport("tcp.port", "50100"); 20 | 21 | /** 22 | * Netty Worker线程数 23 | */ 24 | public static ConfigSupport workerThreads = new ConfigSupport("netty.workerThreads", "4"); 25 | 26 | /** 27 | * SO_BACKLOG: 全连接队列的最大长度 28 | */ 29 | public static ConfigSupport soBacklog = new ConfigSupport("netty.soBacklog", "128"); 30 | 31 | /** 32 | * TCP_NODELAY: 是否开启Nagle算法 33 | */ 34 | public static ConfigSupport tcpNoDelay = new ConfigSupport("netty.tcpNoDelay", "true"); 35 | 36 | /** 37 | * SO_KEEPALIVE: 是否开启TCP KeepAlive 38 | */ 39 | public static ConfigSupport soKeepAlive = new ConfigSupport("netty.soKeepAlive", "true"); 40 | 41 | 42 | /** 43 | * 认证鉴权URL 44 | */ 45 | public static ConfigSupport authProviderConfigConnectionUrl = new ConfigSupport("thinglinks.authProviderConfig.authConnectionUrl", "http://127.0.0.1:18760/link/anyTenant/deviceOpen/clientConnectionAuthentication"); 46 | 47 | 48 | /** 49 | * 事件收集 kafka服务地址 50 | * 用于推送tcp事件到kafka 51 | */ 52 | public static ConfigSupport eventCollectorConfigKafkaBootstrapServer = new ConfigSupport("thinglinks.eventCollectorConfig.kafkaBootstrapServer", "124.223.113.139:9092"); 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /example-plugin-tcpserver/src/main/java/com/mqttsnet/thinglinks/open/exp/example/tcpserver/MyComponent.java: -------------------------------------------------------------------------------- 1 | package com.mqttsnet.thinglinks.open.exp.example.tcpserver; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.List; 5 | import java.util.concurrent.ScheduledExecutorService; 6 | import java.util.concurrent.ScheduledThreadPoolExecutor; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import cn.hutool.core.net.NetUtil; 10 | import jakarta.annotation.PostConstruct; 11 | import jakarta.annotation.PreDestroy; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 14 | import org.springframework.context.annotation.Scope; 15 | import org.springframework.stereotype.Component; 16 | 17 | /** 18 | * 组件管理总线 19 | * 20 | * @author mqttsnet 21 | */ 22 | @Slf4j 23 | @Component 24 | @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) 25 | public class MyComponent { 26 | 27 | // 使用守护线程池(防止JVM无法退出) 28 | private final ScheduledExecutorService tcpServerExecutor = 29 | new ScheduledThreadPoolExecutor(1, r -> { 30 | Thread t = new Thread(r, "TCP-Server-Thread"); 31 | t.setDaemon(true); 32 | return t; 33 | }); 34 | 35 | private final ScheduledExecutorService scheduledTaskExecutor = 36 | new ScheduledThreadPoolExecutor(1, r -> { 37 | Thread t = new Thread(r, "Heartbeat-Thread"); 38 | t.setDaemon(true); 39 | return t; 40 | }); 41 | 42 | private static boolean initialized = false; 43 | private final TcpServer tcpServer; 44 | 45 | public MyComponent(TcpServer tcpServer) { 46 | this.tcpServer = tcpServer; 47 | } 48 | 49 | 50 | /** 51 | * 初始化组件并启动TcpServer。 52 | */ 53 | @PostConstruct 54 | public void init() { 55 | log.info("初始化 tcpserver 插件..."); 56 | if (initialized) { 57 | log.warn("重复初始化 tcpserver 插件已被阻止"); 58 | return; 59 | } 60 | initialized = true; 61 | 62 | // 启动TCP服务(带异常重试机制) 63 | tcpServerExecutor.submit(() -> { 64 | try { 65 | tcpServer.start(); 66 | } catch (Exception e) { 67 | log.error("TCP服务启动失败,触发紧急关闭", e); 68 | shutdown(); // 启动失败时立即清理 69 | } 70 | }); 71 | 72 | // 定时心跳任务(30秒间隔) 73 | scheduledTaskExecutor.scheduleAtFixedRate( 74 | this::sendHeartbeat, 30, 30, TimeUnit.SECONDS 75 | ); 76 | } 77 | 78 | /** 79 | * 在应用关闭时优雅地关闭TcpServer和定时任务。 80 | */ 81 | @PreDestroy 82 | public void shutdown() { 83 | log.info("开始关闭 tcpserver 插件..."); 84 | 85 | // 阶段1:停止所有定时任务 86 | shutdownExecutor(scheduledTaskExecutor, "tcpserver 插件心跳线程池"); 87 | 88 | // 阶段2:关闭TCP服务 89 | tcpServer.shutdown(); 90 | 91 | // 阶段3:关闭TCP服务线程池(所有资源已释放) 92 | shutdownExecutor(tcpServerExecutor, "TCP服务线程池"); 93 | 94 | log.info("所有资源已释放完毕"); 95 | } 96 | 97 | /** 98 | * 安全关闭线程池 99 | */ 100 | private void shutdownExecutor(ScheduledExecutorService executor, String poolName) { 101 | if (executor != null && !executor.isShutdown()) { 102 | List 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 | --------------------------------------------------------------------------------