实现 thingsboard 网关下设备 OTA 升级功能(十)

331 阅读2分钟

处理网关获取升级包的请求(四)

再坚持一会

前面写到,我们又要写一个方法sendOtaPackageToGateway(),不写代码一直是我们的人生信条,想要我们写代码,除非走头无路,别无他法,所以继续复制粘贴。

首先复制一份sendOtaPackage()方法,改名为sendOtaPackageToGateway,如下所示:

private void sendOtaPackage(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, OtaPackageType type) {
    log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
    ack(ctx, msgId);
    try {
        byte[] firmwareChunk = context.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk);
        deviceSessionCtx.getPayloadAdaptor()
                .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type)
                .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
    } catch (Exception e) {
        log.trace("[{}] Failed to send firmware response!", sessionId, e);
    }
}

由于deviceSessionCtx.getPayloadAdaptor().convertToPublish()方法内部,写的topic不是我们想要的,所以还是老办法,再新建一个方法,叫convertToGatewayPublish,为什么取这个名字呢?我怎么取的这么好呢?取得这么优雅,那当然还是抄的嘛,deviceSessionCtx.getPayloadAdaptor()接口如下:

public interface MqttTransportAdaptor {

    ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);

    PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;

    PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;

    GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException;

    ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg, String topicBase) throws AdaptorException;

    ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg, String topicBase) throws AdaptorException;

    ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;

    Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg, String topicBase) throws AdaptorException;

    Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, GetAttributeResponseMsg responseMsg) throws AdaptorException;

    Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, AttributeUpdateNotificationMsg notificationMsg, String topic) throws AdaptorException;

    Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;

    Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ToDeviceRpcRequestMsg rpcRequest, String topicBase) throws AdaptorException;

    Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException;

    Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse, String topicBase) throws AdaptorException;

    ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;

    Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;

    Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException;

    // 这个方法是我新增的,方法名上面有,算是重载
    default Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException {
        return null;
    }

    default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) {
        MqttFixedHeader mqttFixedHeader =
                new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
        MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
        ByteBuf payload = ALLOCATOR.buffer();
        payload.writeBytes(payloadInBytes);
        return new MqttPublishMessage(mqttFixedHeader, header, payload);
    }
}

新增convertToGatewayPublish()方法,前面加 defulat,给个默认实现,是因为擅自加个接口方法,影响比较大,很多实现类都要必须实现我这个方法,加个default,就没影响了,我只需要在我需要的那个实现类重写该方法。

我们在JsonMqttAdaptor类中实现新增的方法,还是老办法,先复制,再修改,我们复制的方法就是下面这个方法:

@Override
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) {
    return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
}

只需要将 DEVICE_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT 改为 GATEWAY_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT, GATEWAY_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT 最开始已经定义过,最终新增convertToGatewayPublish()方法实现为:

@Override
public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException {
    return Optional.of(createMqttPublishMsg(ctx, String.format(GATEWAY_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
}

最后将 deviceSessionCtx.getPayloadAdaptor().convertToPublish 换成 deviceSessionCtx.getPayloadAdaptor().convertToGatewayPublish,sendOtaPackageToGateway()方法就写好了,最终sendOtaPackageToGateway()代码如下:

private void sendOtaPackageToGateway(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, OtaPackageType type) {
    log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
    ack(ctx, msgId);
    try {
        byte[] firmwareChunk = context.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk);
        deviceSessionCtx.getPayloadAdaptor()
                .convertToGatewayPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type)
                .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
    } catch (Exception e) {
        log.trace("[{}] Failed to send firmware response!", sessionId, e);
    }
}