协议开发常见问题

如何在协议包中使用第三方的依赖或者sdk

  1. pom.xml中添加依赖
  2. 使用maven-shade-plugin将依赖的jar打包在一起
  1. <plugin>
  2. <groupId>org.apache.maven.plugins</groupId>
  3. <artifactId>maven-shade-plugin</artifactId>
  4. <version>3.1.1</version>
  5. <executions>
  6. <execution>
  7. <phase>package</phase>
  8. <goals>
  9. <goal>shade</goal>
  10. </goals>
  11. <configuration>
  12. <artifactSet>
  13. <includes>
  14. <!-- 添加需要打包在一起的第三方依赖信息 -->
  15. <!-- <include>com.domain:*</include> -->
  16. </includes>
  17. </artifactSet>
  18. </configuration>
  19. </execution>
  20. </executions>
  21. </plugin>

提示

请勿将jetlinks,spring等平台自身的依赖打包在一起.

更多maven-shade-plugin功能请查询相关文档常见问题 - 图1 (opens new window)

本地开发时如何调试协议包

  1. 将协议项目文件和jetlinks平台代码放在一起(不用添加依赖,只需要放到同一个项目目录即可)
  2. 添加为Maven项目,右键目录-Add as Maven Project,等待加载完成.
  3. 在响应代码打断点即可,和普通项目debug没有区别

debug

温馨提示

linuxmacOS下可以通过软连接的方式将文件链接到平台项目中,不用复制粘贴导致文件重复. 如: ln -s ~/custom-protocol dev/

协议包是响应式的,不会写怎么办

协议包编解码等操作是响应式的,如果实际编解码不存在异步操作(获取设备配置信息,下发回复给设备等)的话, 可以单独封装一个非响应式的方法来做解析,最终使用此方法来做解析,然后在编解码方法中调用即可.

  1. public Flux<DeviceMessage> decode(MessageDecodeContext context){
  2. MqttMessage message = (MqttMessage) ctx.getMessage();
  3. List<DeviceMessage> result = doDecode(message);
  4. return Flux.fromIterable(result);
  5. }
  6. private List<DeviceMessage> doDecode(MqttMessage message){
  7. //.... 解码为设备消息
  8. }

温馨提示

如果需要深入了解和开发协议以及JetLinks平台,建议学习一下Project Reactor常见问题 - 图3 (opens new window)框架.

如何在收到设备的指令后,给设备进行应答

在一些tcp或者udp的设备可能需要平台处理消息后进行应答.

  1. //解码,回复设备并返回一个消息
  2. public Mono<DeviceMessage> decode(MessageDecodeContext context){
  3. EncodedMessage message = context.getMessage();
  4. ByteBuf payload = message.payload();//上报的数据
  5. DeviceMessage message = doEncode(payload); //解码
  6. //强转为FromDeviceMessageContext
  7. FromDeviceMessageContext ctx = (FromDeviceMessageContext)context;
  8. EncodedMessage msg = createAckMessage(); //构造应答消息
  9. return ctx
  10. .getSession()
  11. .send(msg) //发送消息给设备
  12. .thenReturn(message);
  13. }

特别注意

DeviceSession.send是响应式操作,需要和整个响应式流组合在一起,否则可能无法发送到设备.

错误的例子:

  1. ctx.getSession().send(msg);
  2. return Flux.just(message);

正确的例子:

  1. ctx
  2. .getSession()
  3. .send(msg)
  4. .thenReturn(message);

如何在解码时获取设备信息

  1. context.getDevice(); 此方法可能返回null,比如在tcp首次发送报文时.
  1. public Mono<DeviceMessage> decode(MessageDecodeContext context) {
  2. FromDeviceMessageContext ctx = ((FromDeviceMessageContext) context);
  3. ByteBuf payload = message.payload();
  4. DeviceOperator device = context.getDevice();
  5. //device为null说明时首次连接,平台未识别到当前连接属于哪个设备
  6. //返回了DeviceMessage之后,将会与DeviceMessage对应的设备进行绑定,之后getDevice()则为此设备对应的信息.
  7. if(device==null){
  8. //处理认证等逻辑,具体根据实际协议来处理
  9. return handleFirstMessage(payload);
  10. } else {
  11. return handleMessage(payload);
  12. }
  13. }
  1. context.getDevice(deviceId);使用已知的设备ID来获取设备信息.
  1. public Mono<DeviceMessage> decode(MessageDecodeContext context) {
  2. FromDeviceMessageContext ctx = ((FromDeviceMessageContext) context);
  3. ByteBuf payload = message.payload();
  4. //从报文中获取
  5. String deviceId = getDeviceIdFromMessage(payload);
  6. return context
  7. .getDevice(deviceId)
  8. //获取设备的配置
  9. .flatMap(device-> device.getSelfConfig("des_key"))
  10. //设备或者设备配置des_key不存在时会走swichIfEmpty的逻辑
  11. .swichIfEmpty(Mono.defer(()->handleDeviceNotFound(ctx).then(Mono.empty())))
  12. .mapNotNull(key->{
  13. //解码
  14. return doDecodeMessage(key.asString(),payload)
  15. })
  16. }
  17. private Mono<Void> handleDeviceNotFound(FromDeviceMessageContext ctx){
  18. //构造设备不存在的消息
  19. EncodedMessage ack = ...;
  20. return ctx
  21. .getSession()
  22. .send(ack)
  23. .then();
  24. }
  25. private DeviceMessage doDecodeMessage(String key,ByteBuf payload){
  26. //解码并返回设备消息
  27. return ...;
  28. }
  29. private String getDeviceIdFromMessage(ByteBuf buf){
  30. //根据协议约定从报文中获取设备ID,比如设备sn等.
  31. return ...;
  32. }