# 自定义协议开发 ## 环境准备和开发工具 * JDK:1.8+ * MAVEN:3.1+ ::: warning 注意 maven不要使用全局仓库配置,可能导致依赖无法下载 ::: * 开发工具:idea ## 第一步 通过idea创建maven工程:demo-protocol ## 第二步 修改pom文件,添加依赖 ```xml // jetlinks 核心依赖 org.jetlinks jetlinks-core 1.0.2-BUILD-SNAPSHOT // jetlinks 协议解析接口包 org.jetlinks jetlinks-supports 1.0.2-BUILD-SNAPSHOT // lombok,需要idea安装lombok插件,否则去掉 org.projectlombok lombok 1.18.10 // vertx核心包,可以用来进行网络模拟测试 io.vertx vertx-core 3.8.3 test // 单元测试包 org.junit.jupiter junit-jupiter 5.5.2 test // logback日志 ch.qos.logback logback-classic 1.2.3 // netty组件 io.netty netty-bom ${netty.version} pom import ``` * 修改maven pom文件properties ~~~xml UTF-8 zh_CN 1.8 ${java.version} 4.1.45.Final ~~~ * 添加maven编译规则 ~~~xml org.apache.maven.plugins maven-compiler-plugin 3.1 ${project.build.jdk} ${project.build.jdk} ${project.build.sourceEncoding} ~~~ * 添加hsweb私服和阿里云仓库 ~~~xml hsweb-nexus Nexus Release Repository http://nexus.hsweb.me/content/groups/public/ true always aliyun-nexus aliyun http://maven.aliyun.com/nexus/content/groups/public/ ~~~ ## 第三步 协议开发 * 新建packag:org.jetlinks.demo.protocol * 创建协议编码解码类:DemoDeviceMessageCodec ```java import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import org.jetlinks.core.message.DeviceMessage; import org.jetlinks.core.message.DeviceOnlineMessage; import org.jetlinks.core.message.Message; import org.jetlinks.core.message.codec.*; import org.jetlinks.core.message.function.FunctionInvokeMessage; import org.jetlinks.core.message.function.FunctionInvokeMessageReply; import org.jetlinks.core.server.session.DeviceSession; import org.jetlinks.wt.protocol.message.NbIotMessage; import org.jetlinks.wt.protocol.message.data.enums.DataIdEnum; import org.jetlinks.wt.protocol.message.enums.ControlEnum; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @AllArgsConstructor @Slf4j public class DemoDeviceMessageCodec implements DeviceMessageCodec { // 传输协议定义 @Override public Transport getSupportTransport() { return DefaultTransport.TCP; } // 把tcp消息解码为平台消息,多用于设备上报消息到平台 @Override public Mono decode(MessageDecodeContext context) { return Mono.empty(); } // 把平台消息编码为协议传输消息,多用于平台命令下发到设备 @Override public Publisher encode(MessageEncodeContext context) { retrun Mono.empty(); } } ``` * 创建协议入口类: DemoProtocolSupportProvider ~~~java import org.jetlinks.core.ProtocolSupport; import org.jetlinks.core.Value; import org.jetlinks.core.defaults.CompositeProtocolSupport; import org.jetlinks.core.device.AuthenticationResponse; import org.jetlinks.core.device.DeviceRegistry; import org.jetlinks.core.device.MqttAuthenticationRequest; import org.jetlinks.core.message.codec.DefaultTransport; import org.jetlinks.core.metadata.DefaultConfigMetadata; import org.jetlinks.core.metadata.types.PasswordType; import org.jetlinks.core.metadata.types.StringType; import org.jetlinks.core.spi.ProtocolSupportProvider; import org.jetlinks.core.spi.ServiceContext; import org.jetlinks.demo.protocol.tcp.DemoTcpMessageCodec; import org.jetlinks.supports.official.JetLinksDeviceMetadataCodec; import reactor.core.publisher.Mono; public class DemoProtocolSupportProvider implements ProtocolSupportProvider { @Override public Mono create(ServiceContext context) { CompositeProtocolSupport support = new CompositeProtocolSupport(); // 协议ID support.setId("demo-v1"); // 协议名称 support.setName("演示协议v1"); // 协议说明 support.setDescription("演示协议"); // 物模型编解码,固定为JetLinksDeviceMetadataCodec support.setMetadataCodec(new JetLinksDeviceMetadataCodec()); //TCP消息编解码器 DemoDeviceMessageCodec codec = new DemoDeviceMessageCodec(); // 两个参数,协议支持和编解码类DemoDeviceMessageCodec中保持一致,第二个参数定义使用的编码解码类 support.addMessageCodecSupport(DefaultTransport.TCP, () -> Mono.just(codec)); return Mono.just(support); } } ~~~ ## 第四步 设备上报消息解码 本部分代码全部写在DemoDeviceMessageCodec.decode方法中 ~~~java @AllArgsConstructor @Slf4j public class DemoTcpMessageCodec implements DeviceMessageCodec { .... // 把tcp消息解码为平台消息,多用于设备上报消息到平台 @Override public Mono decode(MessageDecodeContext context) { return Mono.defer(() -> { // 消息上下文 FromDeviceMessageContext ctx = ((FromDeviceMessageContext) context); // 从上下文中获取消息字节数组 ByteBuf byteBuf = context.getMessage().getPayload(); byte[] payload = ByteBufUtil.getBytes(byteBuf, 0, byteBuf.readableBytes(), false); // 把字节流转换为字符串,根据不同设备不同协议进行解析, String text=new String(payload); ReportPropertyMessage message.setProperties(properties); = new ReportPropertyMessage(); // 设置消息ID为我们获得的消息内容 message.setDeviceId(text); // 以当前时间戳为消息时间 long time=LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli(); message.setTimestamp(time); // 构造上报属性 Map properties = new HashMap<>(); properties.put("text",text); // 设置上报属性 message.setProperties(properties); // 获取设备会话信息 DeviceSession session = ((FromDeviceMessageContext) context).getSession(); // 如果session中没有设备信息,则为设备首次上线 if (session.getOperator() == null) { DeviceOnlineMessage onlineMessage = new DeviceOnlineMessage(); onlineMessage.setDeviceId(text); onlineMessage.setTimestamp(System.currentTimeMillis()); // 返回到平台上线消息 return Flux.just(message,onlineMessage); } // 返回到平台属性上报消息 return Mono.just(message); }); } ..... } ~~~ ## 第五步 平台发送消息到设备(消息编码) 本部分代码全部写在DemoDeviceMessageCodec.encode方法中 ~~~java @AllArgsConstructor @Slf4j public class DemoDeviceMessageCodec implements DeviceMessageCodec { .......... // 把平台消息编码为协议传输消息,多用于平台命令下发到设备 @Override public Publisher encode(MessageEncodeContext context) { // 从平台消息上下文中获取消息内容 Message message = context.getMessage(); EncodedMessage encodedMessage=EncodedMessage.simple(Unpooled.wrappedBuffer(toBytes());) = null; // 根据消息类型的不同,构造不同的消息 if (message instanceof ReadPropertyMessage) { ReadPropertyMessage readPropertyMessage = (ReadPropertyMessage) message; // 获取需要传输的字节 byte[] bytes=readPropertyMessage.toString().getBytes(); // 构造为平台传输到设备的消息体 encodedMessage=EncodedMessage.simple(Unpooled.wrappedBuffer(bytes)); } ... retrun encodedMessage != null ? Mono.just(encodedMessage) : Mono.empty(); } } ~~~ ## 第六步 打成jar包上传到平台,并进行调试 以[TCP服务网关接入设备](../best-practices/tcp-connection.md#使用tcp工具接入)为例。 进行协议上传和 配置,并使用TCP工具进行调试。