跳到主要内容

Android RadioTransport 统一传输层

SDK Version: 1.0.0
Package: com.xtalk.mesh.sdk.transport

RadioTransport 让 Auth、Init 和业务层用同一套接口访问 BLE 或客户 UART。它不打开物理通道;构造前必须先完成 BLE 连接或打开 UART。

1. 模型与枚举

类型值/字段用途
RadioTransportModeBLE, UART当前物理通道
RadioSendResultSEND_OK, SEND_FAIL_NOT_CONNECTED, SEND_FAIL_INTERNALRF 发送受理结果
AtCommandErrorPolicyFAIL_FAST, COLLECT_UNTIL_COMPLETE遇到错误行立即失败或让自定义完成条件收集完整响应
RadioPacketSourceBLE_DIRECT, BLE_PASSTHROUGH_DI, UART_DI, UART_HEX入站包来源
RadioInboundPacketpayload, source, snr, rssi, receivedAtNanosRF 入站数据;payload 是业务字节副本
RadioCrcErrorEventsource, slot, snr, rssi, rawLine, receivedAtNanos可结构化的 CRC 错误;缺失元数据为 null
RadioPacketListeneronPacket(packet)普通或实时包回调
RadioCrcErrorListeneronCrcError(event)CRC 回调

监听器执行线程由具体 transport 决定,默认按高频 I/O 回调处理:不要阻塞,不要直接操作 View,把 UI 更新切到主线程。

2. RadioTransport 每个接口

共同前置条件:AT 和 RF 方法在后台线程调用;Auth/Init/自定义 AT 不可并发。timeoutMs 单位毫秒,超时不代表设备一定未执行命令,因此超时后先查询状态再重试。

接口参数/默认值返回、错误与配对操作
mode, label当前通道枚举与日志标签
isOpenOrConnected()物理链路是否打开;不代表 Auth/Init 完成
isReadyForSend()是否满足业务发送门槛
localDeviceLongId()已认证 DID;未知为 null
runAtCommand(command, timeoutMs = 3000, log = true, errorPolicy = FAIL_FAST, stopWhen = null)AT 文本、自定义完成条件返回响应行;未连接、发送失败、设备错误或超时可抛异常
runAtCommandDrainingResponseTail(...)stopWhen 必填匹配业务载荷后仍持有锁到终止 OK/ERROR 或原截止时间,防止迟到 OK 污染下一条命令;不支持的实现抛 UnsupportedOperationException
sendAtCommand(command)AT 文本等 SDK 发送结果,成功为 true;不返回响应行
sendAtCommandNoWait(command)AT 文本只报告是否受理;调用方不能据此判断设备执行成功
sendRfPayload(payload, timeoutMs = 3000)非空业务帧RadioSendResult;普通可靠业务首选
sendRfPayloadAndWaitSendFinish(payload, timeoutMs = 3000)非空业务帧等设备 SEND_FINISH;成功为 true
sendRfPayloadNoWait(payload)非空实时帧低延迟受理结果;无设备完成确认,适合实时语音,调用方负责节流
addPacketListener / removePacketListener同一个 listener 实例普通 RF 包生命周期配对
addRealtimePacketListener / removeRealtimePacketListener同一个 listener 实例实时高优先级包生命周期配对
addCrcErrorListener / removeCrcErrorListener同一个 listener 实例CRC 事件配对;具体 bridge 必须有 CRC 解析源
setExpectedRealtimeSourceShortId(shortId)null 清除UART/高级实现可用它过滤实时源;BLE 实现可能无操作
isRecentLocalRealtimeEcho(payload)完整帧是否是近期本机实时回声;不支持的实现返回 false
setFrequencyHz(freqHz)获准频率 Hz全通道设置成功为 true;失败后不要发送
setRateMode(rateMode)设备支持的模式成功为 true
queryRateMode(timeoutMs = 1500)超时当前模式;解析/响应失败为 null
queryWorkMode(timeoutMs = 1500)超时当前模式;解析/响应失败为 null
restoreTextPttDefaults(defaultRateMode, defaultAddtl = 1, defaultWorkMode = 21)三项默认参数全部恢复成功为 true;通话/文件传输结束后调用
val lines = withContext(Dispatchers.IO) {
radio.runAtCommand("AT+RATE?", timeoutMs = 1_500L)
}
val sent = withContext(Dispatchers.IO) {
radio.sendRfPayload(payload, timeoutMs = 3_000L)
}

3. BleRadioTransport

val radio: RadioTransport = BleRadioTransport(
eventBridge = customerEventBridge,
clearRealtimeNoWaitMark = { echoTracker.clear() },
markRealtimeNoWaitSend = { bytes -> echoTracker.mark(bytes) },
)
  • 前置:XTalkBleClient.ensureInitialized() 成功;发送前 BLE 已连接,Auth/Init 已完成。
  • eventBridge 把底层入站事件映射给 transport listeners;完整 BLE 示例见完整接入示例
  • 两个 realtime mark 回调用于客户的本地回声识别。不需要时可传空 lambda。
  • BleRadioTransport 不拥有 BLE 连接;销毁时仍由客户移除 listeners 并调用 XTalkBleClient.disconnect()

4. RadioTransportEventBridge

接口包含三组严格配对方法:普通包、实时包、CRC。实现必须保存 listener → 底层 callback 映射,移除时使用原 callback;不能每次重新创建 callback。普通 BLE 数据桥示例已放在完整接入示例

5. 客户 UART 适配器

如果客户没有交付的 UART transport,可按接口包装已有驱动。下面只展示契约骨架;响应分行、DI 解码、CRC、互斥锁和超时必须由实现补齐:

class CustomerUartRadioTransport(
private val uart: CustomerUartDriver,
) : RadioTransport {
override val mode = RadioTransportMode.UART
override val label = "customer-uart"

override fun isOpenOrConnected() = uart.isOpen
override fun isReadyForSend() = uart.isOpen && uart.did != null && uart.initialized
override fun localDeviceLongId() = uart.did

override fun runAtCommand(
command: String,
timeoutMs: Long,
log: Boolean,
errorPolicy: AtCommandErrorPolicy,
stopWhen: ((List<String>) -> Boolean)?,
): List<String> = uart.runExclusiveAt(command, timeoutMs, errorPolicy, stopWhen)

override fun sendAtCommand(command: String) = uart.sendAt(command, waitWrite = true)
override fun sendAtCommandNoWait(command: String) = uart.sendAt(command, waitWrite = false)
override fun sendRfPayload(payload: ByteArray, timeoutMs: Long) =
if (uart.sendRf(payload, timeoutMs)) RadioSendResult.SEND_OK
else RadioSendResult.SEND_FAIL_INTERNAL
override fun sendRfPayloadAndWaitSendFinish(payload: ByteArray, timeoutMs: Long) =
uart.sendRfAndAwaitFinish(payload, timeoutMs)
override fun sendRfPayloadNoWait(payload: ByteArray) = uart.sendRfNoWait(payload)

// add/remove listeners、频率/速率查询设置和默认恢复按同名接口委托给客户驱动。
// 不支持的能力必须返回 false/null 或明确抛 UnsupportedOperationException,不能伪造成功。
}

用于 Auth 的最小 UART 只需实现 DeviceAuthenticator.AtCommandTransport;用于 Init 的最小 UART 只需实现 DeviceInitializer.AtCommandTransport。只有业务层需要统一切换时才实现完整 RadioTransport

6. RadioTransportRouter

val router = RadioTransportRouter(bleTransport, uartTransport, RadioTransportMode.BLE)
router.addPacketListener(packetListener)
router.setMode(RadioTransportMode.UART)
val active = router.currentTransport()

setMode 会把 Router 已保存的三类 listener 从旧 transport 移到新 transport。切换前客户必须保证新物理通道已打开且完成对应 Auth/Init。销毁时仍需逐个 remove;Router 不关闭底层通道。

7. Advanced:入站解析工具

这些是低层 transport 作者使用的公开工具,普通 BLE 接入不需要直接调用:

API契约
AtLineBuffer.hasBufferedData()是否还有未终止的半行
AtLineBuffer.append(bytes, onLine)按 CR/LF 组装行;二参数 callback 同时给出组装毫秒数;线程安全但 callback 应快速返回
AtResponsePolicy.isOkLine/isErrorLine/isSendFinishLine识别兼容终止行
AtResponsePolicy.isResponseTailOkLine/isResponseTailErrorLine严格 tail-drain 终止判断
AtResponsePolicy.isFreqProbeAccepted/freqProbeStatus返回 NORMAL_FREQNEEDS_VERIFYnull
AtResponseTailDrainTracker.observe返回 CONTINUE/COMPLETE/ERRORpayloadMatched 一旦为真不会回退
BinaryEscape.escape/unescape对 CR、LF、转义字节做可逆封装;不修改输入数组
DiFrameParser.parseLine/parseBytes解析 +DI/AT+DI;失败为 nulllengthMismatch 必须作为丢包依据
DiFrameParser.parseDiMeta提取 DiMeta 的计数、长度、slot、SNR/RSSI、TXP;缺字段为 null
DiMeta.hasInvalidRemoteFeedback()远端反馈组合是否无效
CrcErrorParser.parseLine/parseBytes解析 +CRCERR,可传 AT decoder;失败为 null
AsciiPrefix.findDiPrefixIndex/findCrcErrPrefixIndex/findAsciiPrefixIndex在可能含前导噪声的字节中定位 ASCII 前缀

解析器只做边界解析,不负责线程、串口读取、重连或业务重试。

返回接入总览 · API 覆盖矩阵