簡體   English   中英

Android BLE 使用 RxAndroidBle 以阻塞方式寫入大文件

[英]Android BLE Write large file using RxAndroidBle in blocking way

我有一個使用 RxAndroidBle 作為 BLE 解決方案的 Android 應用程序,它非常棒,可以節省大量的工作時間。

但最近我必須實施更新固件功能,但我遇到了困難。

自定義 BLE 設備之一必須在寫入之前等待通知 ByteAray。 每 16 個包(每個包 20 個字節),設備將向某些特征發送通知 ByteAray。

所以我想做的是等待通知,然后發送那些固件包。 而且我發現我必須添加一個 160 毫秒的計時器,這樣設備就不會被這些包(背壓?)壓倒。

仍然沒有運氣。 設備將 go 無響應然后斷開一定量的數據后斷開連接,如 256 字節 * 12(文件大小范圍為 256 字節 * 330 ~ 785)。

這是當前的實現:

 .flatMap { ifFrameCountAccepted ->
                if (ifFrameCountAccepted) {
                    Timber.d("Wait 2 seconds cleaning up flash")
                    Flowable
                        .timer(3000, TimeUnit.MILLISECONDS)
                        .flatMap { sendFramesFlowable(firmware, isPic) }
                } else {
                    Flowable.error(RuntimeException("MCU L2 frame count error."))
                }
            }
            .toObservable()
            .flatMap { isFirmwareTransmissionDone ->
                if (isFirmwareTransmissionDone) {
                    waitUntilL2McuUpgradeFinish(isPic)
                } else {
                    Observable.just(false)
                }
            }

    private fun sendFramesFlowable(
        firmware: FirmwareUpgradeData,
        isPic: Boolean
    ): Flowable<Boolean> {
        Timber.d("Firmware size: ${firmware.processed.size}")

        val frameInvalidPublishSubject = PublishSubject.create<Boolean>()
            val frameObservable = connection.toFlowable(BackpressureStrategy.BUFFER).flatMap { rxConnection ->
            rxConnection.setupNotification(
                RC_NOTIFICATION_CHARACTERISTIC,
                NotificationSetupMode.DEFAULT
            ).toFlowable(BackpressureStrategy.BUFFER)
                .concatMap { notification ->
                    notification.toFlowable(BackpressureStrategy.BUFFER)
                }
            }
                .filter { it.size == 20 }
                .filter { it.second.first().toUnsignedValue() == COMMAND_HEADER_L2_FRAME }
                .map { reply ->
                    Timber.d("[A2] decoded: ${reply.second.toHex()}")
                    val payload = reply.second.dataPayload(reply.first)
                    val isValid = upgradeDataTransmission.resolveFrameCommand(payload)
                    Timber.d("MCU L2 upgrade frame is accepted: $isValid")
                    unless(!isValid) {
                        frameInvalidPublishSubject.onNext(true)
                    }
                    isValid
                }
            .zipWith(Flowable.range(1, firmware.frameCount), BiFunction { _: Boolean, frameCount: Int ->
                frameCount
            })
            .doOnNext { frameCount ->
                val base = if (isPic) 45.minus(25) else 99.minus(45)
                val progress = base.div(firmware.frameCount.toFloat())
                    .times(frameCount).toInt()
                    .plus(if (isPic) 25 else 45)
                _upgradeProgress.postValue(Event.success(progress))
            }
            .flatMap { frameCount ->
                Timber.d("frame count now is:$frameCount")
                if (frameCount == firmware.frameCount) {
                    triggerL2Upgrade(firmware.crcCheck)
                } else {
                    Flowable.just(false)
                }
            }

        Flowable.fromIterable(firmware.processed.withIndex())
            .buffer(16)
            .takeUntil(frameInvalidPublishSubject.toFlowable(BackpressureStrategy.BUFFER))
            .concatMap { frame ->
                Flowable.timer(160, TimeUnit.MILLISECONDS).concatMap {
                    Flowable.fromIterable(frame)
                        .concatMap { perPackage ->
                            connection.toFlowable(BackpressureStrategy.BUFFER)
                                .concatMap {
                                    it.writeCharacteristic(RC_WRITE_CHARACTERISTIC, perPackage.value.toByteArray())
                                        .toFlowable()
                                }
                        }
                }
            }
            .forEachWhile {
                true
            }

        return frameObservable
    }

RxAndroidBle中有一個內置的幫助器來處理這種情況:
RxBleConnection.createLongWriteBuilder()

您可以閱讀它為您提供的確切可能性,但使用writeOperationAckStrategy()您可以推遲寫入下一個字節數組:

val notificationsEvery16thWrite: Observable<ByteArray> = (...)
rxBleConnection.createNewLongWriteBuilder()
    .setWriteOperationAckStrategy { writeAcks ->
        val emitEveryWriteAckApartEvery16th = writeAcks
            .scan(0 to null as Boolean?) { acc, boolean ->
                acc.first.plus(1) to boolean
            }
            .filter { it.first != 0 && it.first % 16 != 0 }
            .map { it.second!! }
        val emitEvery16thWriteAckAfterNotification = notificationsEvery16thWrite.zipWith(
            writeAcks.buffer(16), // buffer 16 writes ACKs
            BiFunction { _, writesCompleted -> writesCompleted.last() }) // use last when notification arrives
        Observable.merge(
            emitEveryWriteAckApartEvery16th,
            emitEvery16thWriteAckAfterNotification
        )
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM