refactor(serial): 将 SerialPortManager 合并到 ScaleManager
- 删除 SerialPortManager,串口生命周期(open/startReceive/release)内嵌到 ScaleManager - ScaleManager 改为 object 单例,去除 .instance 调用 - init() 简化为只接收 activateCode,去除 deviceId + activateMap 查表逻辑 - 新增 start(preHook) 方法合并 open + startReceive,支持链式调用到末尾 - BaseActivity.sendCmd 改为直接调用 ScaleManager.sendCmd Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,6 @@ import com.shuwei.intelligent.shelves.serial.ProtocolConstants
|
||||
import com.shuwei.intelligent.shelves.serial.ProtocolConstants.C_TEMP_CMD
|
||||
import com.shuwei.intelligent.shelves.serial.ProtocolConstants.START_TEMP_CTRL_CMD
|
||||
import com.shuwei.intelligent.shelves.serial.ScaleManager
|
||||
import com.shuwei.intelligent.shelves.serial.SerialPortManager
|
||||
import com.shuwei.intelligent.shelves.utils.GridLayoutTool
|
||||
import com.shuwei.intelligent.shelves.utils.IntervalExecutor
|
||||
import com.shuwei.intelligent.shelves.utils.ext.copyTextToClipboard
|
||||
@@ -77,33 +76,34 @@ class HomeActivity : BaseActivity() {
|
||||
|
||||
// 串口协议解析器,通过回调通知本 Activity
|
||||
private fun initScaleManager() {
|
||||
ScaleManager.instance
|
||||
.init(
|
||||
sendAction = { cmd ->
|
||||
// ScaleManager 的自动应答(心跳/对时等)直接写串口,不经过 BaseActivity.sendCmd
|
||||
lifecycleScope.launch { SerialPortManager.send(cmd) }
|
||||
},
|
||||
deviceId = App.deviceId ?: "",
|
||||
activateMap = ProtocolConstants.ACTIVE_MAP
|
||||
)
|
||||
.onHeartbeat { _, data ->
|
||||
data.weights.forEach { weight ->
|
||||
val shelfNo = weight.index
|
||||
if (shelfNo in 1..list.size) {
|
||||
list.firstOrNull { it.deviceNo == shelfNo }?.let { model ->
|
||||
model.weight = weight.grams.toDouble()
|
||||
val pos = list.indexOf(model)
|
||||
shelfAdapter.notifyItemChanged(pos)
|
||||
log("getWeightInfo: deviceNo=${model.deviceNo},realWeight=${weight.grams}")
|
||||
EventBus.getDefault().post(SendWeightEvent(model.deviceNo, weight.grams))
|
||||
lifecycleScope.launch {
|
||||
val ok = ScaleManager
|
||||
.init(activateCode = ProtocolConstants.ACTIVE_MAP[App.deviceId] ?: "")
|
||||
.onHeartbeat { _, data ->
|
||||
data.weights.forEach { weight ->
|
||||
val shelfNo = weight.index
|
||||
if (shelfNo in 1..list.size) {
|
||||
list.firstOrNull { it.deviceNo == shelfNo }?.let { model ->
|
||||
model.weight = weight.grams.toDouble()
|
||||
val pos = list.indexOf(model)
|
||||
shelfAdapter.notifyItemChanged(pos)
|
||||
log("getWeightInfo: deviceNo=${model.deviceNo},realWeight=${weight.grams}")
|
||||
EventBus.getDefault().post(SendWeightEvent(model.deviceNo, weight.grams))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onTempReport { _ ->
|
||||
updateLeftStatus(deviceName)
|
||||
}
|
||||
.onLog { message -> log(message) }
|
||||
.onTempReport { _ ->
|
||||
updateLeftStatus(deviceName)
|
||||
}
|
||||
.onLog { message -> log(message) }
|
||||
.start { data ->
|
||||
lastDataReceivedTime = System.currentTimeMillis()
|
||||
noDataWarningDialog?.takeIf { it.isShowing }?.dismiss()
|
||||
log("receiveSerialPortData: $data")
|
||||
}
|
||||
log("onCreate: openState=$ok")
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@@ -119,28 +119,9 @@ class HomeActivity : BaseActivity() {
|
||||
updateLeftStatus("")
|
||||
initRecyclerView()
|
||||
|
||||
// 初始化 ScaleManager(串口初始化前注册回调)
|
||||
// 初始化 ScaleManager:注册回调、打开串口、启动接收
|
||||
initScaleManager()
|
||||
|
||||
// 串口初始化,独立协程,不阻塞网络状态监听
|
||||
lifecycleScope.launch {
|
||||
val openState = SerialPortManager.open()
|
||||
log("onCreate: openState=$openState")
|
||||
if (openState) {
|
||||
SerialPortManager.startReceive { data ->
|
||||
// 切换到主线程协程处理串口数据
|
||||
lifecycleScope.launch {
|
||||
runCatching { receiveSerialPortData(data) }
|
||||
.onFailure {
|
||||
it.printStackTrace()
|
||||
log("串口数据处理异常: ${it.message}")
|
||||
toast("串口数据处理异常,请检查设备连接")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 网络状态监听,独立协程,与串口初始化并行执行
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
@@ -250,14 +231,6 @@ class HomeActivity : BaseActivity() {
|
||||
loadEmptyView()
|
||||
}
|
||||
|
||||
private fun receiveSerialPortData(srcData: String) {
|
||||
log("receiveSerialPortData: $srcData")
|
||||
// 重置无数据计时,并关闭已显示的超时提示弹窗
|
||||
lastDataReceivedTime = System.currentTimeMillis()
|
||||
noDataWarningDialog?.takeIf { it.isShowing }?.dismiss()
|
||||
ScaleManager.instance.onDataReceived(srcData)
|
||||
}
|
||||
|
||||
private var tipDialog: CommonDialog? = null
|
||||
|
||||
/** 最后一次成功收到串口数据的时间戳(毫秒) */
|
||||
@@ -309,8 +282,7 @@ class HomeActivity : BaseActivity() {
|
||||
noDataWarningDialog?.dismiss()
|
||||
noDataWarningDialog = null
|
||||
|
||||
runBlocking { SerialPortManager.close() }
|
||||
ScaleManager.instance.release()
|
||||
runBlocking { ScaleManager.release() }
|
||||
saveTaskJob?.cancel()
|
||||
overdueTaskJob?.cancel()
|
||||
EventBus.getDefault().unregister(this)
|
||||
@@ -333,10 +305,10 @@ class HomeActivity : BaseActivity() {
|
||||
lifecycleScope.launch {
|
||||
if (isNeedOpenLock()) {
|
||||
val openCmd = if (list[position].deviceNo in 1..5)
|
||||
ScaleManager.instance.buildOpenLock1Cmd()
|
||||
ScaleManager.buildOpenLock1Cmd()
|
||||
else
|
||||
ScaleManager.instance.buildOpenLock2Cmd()
|
||||
ScaleManager.instance.sendCmd(openCmd)
|
||||
ScaleManager.buildOpenLock2Cmd()
|
||||
ScaleManager.sendCmd(openCmd)
|
||||
}
|
||||
launch(Intent(this@HomeActivity, ShelfActivity::class.java).also {
|
||||
it.putExtra(ShelfActivity.SHELF_MODEL, list[position])
|
||||
|
||||
@@ -146,7 +146,7 @@ class ShelfActivity : BaseActivity() {
|
||||
//binding.tvFoodWeight.text = "0克"
|
||||
EventBus.getDefault().post(ClearShelfEvent(shelfModel!!.deviceNo, tempWeight))
|
||||
}
|
||||
sendCmd(ScaleManager.instance.buildGetStatusCmd())
|
||||
sendCmd(ScaleManager.buildGetStatusCmd())
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.getGoodsListUiState.collect { state ->
|
||||
@@ -359,7 +359,7 @@ class ShelfActivity : BaseActivity() {
|
||||
private fun clearZero() {
|
||||
lifecycleScope.launch {
|
||||
val shelfNo = shelfModel?.deviceNo ?: 0
|
||||
val zeroClearingCmd = ScaleManager.instance.buildScaleZeroCmd(shelfNo)
|
||||
val zeroClearingCmd = ScaleManager.buildScaleZeroCmd(shelfNo)
|
||||
log("编号${shelfNo}货架执行清零指令:${zeroClearingCmd}")
|
||||
sendCmd(zeroClearingCmd)
|
||||
// sendCmd(
|
||||
@@ -369,7 +369,7 @@ class ShelfActivity : BaseActivity() {
|
||||
realWeight = 0
|
||||
// binding.tvFoodWeight.text = "${realWeight}克"
|
||||
|
||||
sendCmd(ScaleManager.instance.buildGetStatusCmd())
|
||||
sendCmd(ScaleManager.buildGetStatusCmd())
|
||||
startTime = System.currentTimeMillis()
|
||||
Loading.show(this@ShelfActivity)
|
||||
window.decorView.postDelayed({ Loading.dismiss() }, 10000)
|
||||
|
||||
@@ -12,14 +12,13 @@ import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.shuwei.intelligent.shelves.activity.LogActivity
|
||||
import com.shuwei.intelligent.shelves.R
|
||||
import com.shuwei.intelligent.shelves.databinding.ActivityBaseBinding
|
||||
import com.shuwei.intelligent.shelves.databinding.PopupRightStatusMenuBinding
|
||||
import com.shuwei.intelligent.shelves.serial.ProtocolConstants.LIGHT_CLOSE
|
||||
import com.shuwei.intelligent.shelves.serial.ProtocolConstants.LIGHT_OPEN
|
||||
import com.shuwei.intelligent.shelves.serial.SerialPortManager
|
||||
import com.shuwei.intelligent.shelves.serial.ScaleManager
|
||||
import com.shuwei.intelligent.shelves.utils.FileLogger
|
||||
import com.shuwei.intelligent.shelves.utils.ext.clickWithDebounce
|
||||
import com.shuwei.intelligent.shelves.utils.ext.dp
|
||||
@@ -215,10 +214,8 @@ open class BaseActivity : AppCompatActivity() {
|
||||
|
||||
|
||||
fun sendCmd(cmd: String) {
|
||||
lifecycleScope.launch {
|
||||
val result = SerialPortManager.send(cmd)
|
||||
log("发送命令${cmd}结果:${result}")
|
||||
}
|
||||
ScaleManager.sendCmd(cmd)
|
||||
log("发送命令: $cmd")
|
||||
}
|
||||
|
||||
val fileLogger by lazy { FileLogger(this) }
|
||||
|
||||
@@ -12,59 +12,69 @@ import com.shuwei.intelligent.shelves.serial.ScaleProtocolConstants.CMD_TIME_SYN
|
||||
import com.shuwei.intelligent.shelves.serial.ScaleProtocolConstants.CMD_UNLOCK
|
||||
import com.shuwei.intelligent.shelves.serial.ScaleProtocolConstants.FOOTER_HEX
|
||||
import com.shuwei.intelligent.shelves.serial.ScaleProtocolConstants.HEADER_HEX
|
||||
import com.shuwei.intelligent.shelves.utils.hexToBytes
|
||||
import com.shuwei.intelligent.shelves.utils.toHexString2
|
||||
import io.github.jeadyx.jserialport.AndroidSerialPort
|
||||
import io.github.jeadyx.jserialport.SerialPort
|
||||
import io.github.jeadyx.jserialport.SerialPortFactory
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
/**
|
||||
* 称重控制板协议管理器(SDK 入口)
|
||||
*
|
||||
* 职责:
|
||||
* - 管理串口连接(open/startReceive/release)
|
||||
* - 接收串口原始分片数据,自动拼装帧并分发到对应回调
|
||||
* - 内置各命令的自动应答逻辑(心跳/对时/卡号等)
|
||||
* - 提供链式回调注册、常用指令构建、资源释放
|
||||
*
|
||||
* 设计原则:
|
||||
* - 不依赖具体串口实现,通过 init(sendAction) 注入发送函数
|
||||
* - 不依赖任何业务类,可直接复制到其他项目使用
|
||||
* - 非线程安全,建议在同一线程(如串口IO线程或主线程)中使用
|
||||
*
|
||||
* 典型用法:
|
||||
* ```kotlin
|
||||
* // 初始化(Activity/Application中)
|
||||
* ScaleManager.instance
|
||||
* .init { hexStr -> scope.launch { SerialPortManager.send(hexStr) } }
|
||||
* // 初始化并注册回调
|
||||
* ScaleManager
|
||||
* .init(deviceId = App.deviceId, activateMap = ACTIVE_MAP)
|
||||
* .onHeartbeat { frame, data ->
|
||||
* data.weights.forEach { Log.d("Scale", "秤${it.index}: ${it.grams}g") }
|
||||
* }
|
||||
* .onStatus { frame, data ->
|
||||
* Log.d("Scale", "锁1状态: ${data.lock1Lock}")
|
||||
* }
|
||||
* .onLog { msg -> Log.d("Scale", msg) }
|
||||
*
|
||||
* // 在串口数据回调中调用
|
||||
* ScaleManager.instance.onDataReceived(hexData)
|
||||
* // 打开串口并启动接收
|
||||
* lifecycleScope.launch {
|
||||
* if (ScaleManager.open()) {
|
||||
* ScaleManager.startReceive { data ->
|
||||
* // 主线程前置钩子,处理看门狗等副作用
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // 主动发送指令
|
||||
* ScaleManager.instance.sendCmd(ScaleManager.instance.buildGetStatusCmd())
|
||||
* ScaleManager.sendCmd(ScaleManager.buildGetStatusCmd())
|
||||
*
|
||||
* // 页面销毁时释放回调(不影响其他页面注册的回调,若需完全释放则调用 release())
|
||||
* // 页面销毁时释放
|
||||
* runBlocking { ScaleManager.release() }
|
||||
* ```
|
||||
*/
|
||||
class ScaleManager {
|
||||
object ScaleManager {
|
||||
|
||||
companion object {
|
||||
/** 默认单例,适用于单设备场景;多设备场景请自行实例化 */
|
||||
@JvmStatic
|
||||
val instance: ScaleManager by lazy { ScaleManager() }
|
||||
}
|
||||
/** 串口端口名,默认 /dev/ttyS1 */
|
||||
var portName: String = "/dev/ttyS1"
|
||||
/** 串口波特率,默认 115200 */
|
||||
var baudRate: Int = 115200
|
||||
|
||||
private val parser = ScaleFrameParser()
|
||||
private var sendAction: ((String) -> Unit)? = null
|
||||
private var serialPort: AndroidSerialPort? = null
|
||||
private var scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
/** 激活码映射表:key=deviceId,value=完整激活应答帧十六进制字符串 */
|
||||
private var activateMap: Map<String, String> = emptyMap()
|
||||
|
||||
/** 当前设备ID,用于激活码查表 */
|
||||
private var currentDeviceId: String = ""
|
||||
/** 当前设备激活码,非空时收到激活请求自动应答 */
|
||||
private var activateCode: String = ""
|
||||
|
||||
/** 心跳应答:心跳上报间隔(秒),默认5秒 */
|
||||
var heartbeatInterval: Int = 5
|
||||
@@ -91,25 +101,78 @@ class ScaleManager {
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 初始化,注入串口发送函数与激活码映射表
|
||||
* 初始化,设置设备激活码
|
||||
*
|
||||
* @param sendAction 发送十六进制字符串指令的函数,在该函数内完成实际串口写入
|
||||
* @param deviceId 当前设备ID,用于激活码查表
|
||||
* @param activateMap 激活码映射表:key=deviceId,value=完整激活应答帧十六进制字符串;
|
||||
* 传入后终端激活请求将由 SDK 内部自动应答,无需再注册 onActivate 回调
|
||||
* @param activateCode 当前设备的激活应答帧十六进制字符串,非空时收到激活请求自动应答
|
||||
* @return this,支持链式调用
|
||||
*/
|
||||
fun init(
|
||||
sendAction: (String) -> Unit,
|
||||
deviceId: String = "",
|
||||
activateMap: Map<String, String> = emptyMap()
|
||||
): ScaleManager {
|
||||
this.sendAction = sendAction
|
||||
this.currentDeviceId = deviceId
|
||||
this.activateMap = activateMap
|
||||
fun init(activateCode: String = ""): ScaleManager {
|
||||
this.activateCode = activateCode
|
||||
return this
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 串口生命周期
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 打开串口并启动接收,在回调注册完成后调用(链式末尾)
|
||||
*
|
||||
* @param preHook 每帧数据到达时的前置钩子(主线程),用于看门狗计时、UI副作用等
|
||||
* @return true=串口打开成功并已启动接收,false=串口打开失败
|
||||
*/
|
||||
suspend fun start(preHook: ((String) -> Unit)? = null): Boolean {
|
||||
if (!openPort()) return false
|
||||
startReceive(preHook)
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun openPort(): Boolean {
|
||||
if (!scope.isActive) {
|
||||
scope = CoroutineScope(Dispatchers.IO)
|
||||
}
|
||||
return try {
|
||||
serialPort = SerialPortFactory.create().apply {
|
||||
open(
|
||||
portName = portName,
|
||||
baudRate = baudRate,
|
||||
dataBits = 8,
|
||||
stopBits = 1,
|
||||
parity = SerialPort.PARITY_NONE
|
||||
)
|
||||
} as AndroidSerialPort?
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun startReceive(preHook: ((String) -> Unit)? = null) {
|
||||
scope.launch {
|
||||
while (isActive) {
|
||||
try {
|
||||
val flow = serialPort?.read()
|
||||
flow?.collect { buffer ->
|
||||
val data = buffer.toHexString2().uppercase(Locale.ROOT)
|
||||
withContext(Dispatchers.Main) {
|
||||
try {
|
||||
preHook?.invoke(data)
|
||||
onDataReceived(data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
log("串口数据处理异常: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
delay(50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 数据接收入口
|
||||
// ----------------------------------------------------------------
|
||||
@@ -243,11 +306,18 @@ class ScaleManager {
|
||||
* @param hexStr 完整帧十六进制字符串,含协议头尾
|
||||
*/
|
||||
fun sendCmd(hexStr: String) {
|
||||
val action = sendAction ?: run {
|
||||
log("警告:ScaleManager 尚未初始化,请先调用 init(sendAction)")
|
||||
if (!scope.isActive) {
|
||||
log("警告:ScaleManager 串口未打开或已释放")
|
||||
return
|
||||
}
|
||||
action(hexStr)
|
||||
scope.launch {
|
||||
try {
|
||||
serialPort?.write(hexStr.hexToBytes())
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
log("发送命令失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
@@ -352,7 +422,10 @@ class ScaleManager {
|
||||
* 释放所有资源:清空回调列表、发送函数及帧缓冲
|
||||
* 通常在 Activity/Fragment 销毁或不再使用时调用
|
||||
*/
|
||||
fun release() {
|
||||
suspend fun release() {
|
||||
scope.cancel()
|
||||
serialPort?.close()
|
||||
serialPort = null
|
||||
heartbeatListeners.clear()
|
||||
statusListeners.clear()
|
||||
lockListeners.clear()
|
||||
@@ -364,9 +437,7 @@ class ScaleManager {
|
||||
activateListeners.clear()
|
||||
rawListeners.clear()
|
||||
logListeners.clear()
|
||||
sendAction = null
|
||||
activateMap = emptyMap()
|
||||
currentDeviceId = ""
|
||||
activateCode = ""
|
||||
parser.reset()
|
||||
}
|
||||
|
||||
@@ -400,7 +471,7 @@ class ScaleManager {
|
||||
*/
|
||||
private fun handleActivate(frame: ScaleFrame, rawHex: String) {
|
||||
log("收到[终端激活]指令,终端ID:${frame.terminalId},固件版本:${frame.firmwareVersion},原始数据:$rawHex")
|
||||
val activeCmd = activateMap[currentDeviceId]
|
||||
val activeCmd = activateCode
|
||||
if (!activeCmd.isNullOrBlank()) {
|
||||
sendCmd(activeCmd)
|
||||
log("激活码自动应答:$activeCmd")
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.shuwei.intelligent.shelves.serial
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.shuwei.intelligent.shelves.utils.hexToBytes
|
||||
import com.shuwei.intelligent.shelves.utils.toHexString2
|
||||
import io.github.jeadyx.jserialport.AndroidSerialPort
|
||||
import io.github.jeadyx.jserialport.SerialPort
|
||||
import io.github.jeadyx.jserialport.SerialPortFactory
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import java.nio.charset.Charset
|
||||
import java.util.Locale
|
||||
|
||||
object SerialPortManager {
|
||||
|
||||
private const val PORT_NAME: String = "/dev/ttyS1"
|
||||
private const val BAUD_RATE: Int = 115200
|
||||
private var serialPort: AndroidSerialPort? = null
|
||||
|
||||
// private var serialPort: SerialPort? = null
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
// 打开串口
|
||||
suspend fun open(): Boolean {
|
||||
return try {
|
||||
serialPort = SerialPortFactory.create().apply {
|
||||
open(
|
||||
portName = PORT_NAME,
|
||||
baudRate = BAUD_RATE,
|
||||
dataBits = 8,
|
||||
stopBits = 1,
|
||||
parity = SerialPort.PARITY_NONE
|
||||
)
|
||||
} as AndroidSerialPort?
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 发送数据
|
||||
suspend fun send(data: String): Boolean {
|
||||
return try {
|
||||
serialPort?.write(data.hexToBytes())
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 启动接收协程
|
||||
fun startReceive(callback: (String) -> Unit) {
|
||||
scope.launch {
|
||||
while (isActive) {
|
||||
try {
|
||||
val flow = serialPort?.read()
|
||||
flow?.collect { buffer ->
|
||||
val data = buffer.toHexString2().uppercase(Locale.ROOT)
|
||||
callback(data)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
delay(50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭串口
|
||||
suspend fun close() {
|
||||
scope.cancel()
|
||||
serialPort?.close()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user