diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/activity/HomeActivity.kt b/app/src/main/java/com/shuwei/intelligent/shelves/activity/HomeActivity.kt index 6975b5b..a03be83 100644 --- a/app/src/main/java/com/shuwei/intelligent/shelves/activity/HomeActivity.kt +++ b/app/src/main/java/com/shuwei/intelligent/shelves/activity/HomeActivity.kt @@ -3,11 +3,11 @@ package com.shuwei.intelligent.shelves.activity import android.annotation.SuppressLint import android.content.Intent import android.graphics.Color -import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Log +import android.util.SparseIntArray import androidx.activity.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -26,7 +26,6 @@ import com.shuwei.intelligent.shelves.model.ShelfBody import com.shuwei.intelligent.shelves.model.ShelfModel import com.shuwei.intelligent.shelves.model.ShelfResult import com.shuwei.intelligent.shelves.model.WeightEvent -import com.shuwei.intelligent.shelves.net.HomeViewModel import com.shuwei.intelligent.shelves.net.Loading import com.shuwei.intelligent.shelves.net.NetViewModel import com.shuwei.intelligent.shelves.net.RespData @@ -43,7 +42,6 @@ import com.shuwei.intelligent.shelves.utils.ext.toast import com.shuwei.intelligent.shelves.utils.ext.visible import com.shuwei.intelligent.shelves.utils.hexToBinary import kotlinx.coroutines.Job -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -99,6 +97,16 @@ class HomeActivity : BaseActivity() { const val TEMPERATURE_CTRL = "${HEADER}0F01040000${FOOTER}" + val list: MutableList = mutableListOf() + val weightArray = SparseIntArray() + var shelfIndex = 0 + + // public fun getRealWeight():Double = weightArray[shelfIndex]?:0.0 + // 温度 + var showTemperatureC = "0" + + // 湿度 + var showHumidity = "0" } //401-false,1楼餐厅-true @@ -107,7 +115,6 @@ class HomeActivity : BaseActivity() { private lateinit var binding: ActivityHomeBinding private val viewModel: NetViewModel by viewModels() - private val homeViewModel: HomeViewModel by viewModels() @SuppressLint("NotifyDataSetChanged", "HardwareIds") override fun onCreate(savedInstanceState: Bundle?) { @@ -121,34 +128,23 @@ class HomeActivity : BaseActivity() { updateLeftStatus("") initRecyclerView() - - // 串口初始化:独立协程,仅执行一次 lifecycleScope.launch { val openState = SerialPortManager.open() log("onCreate: openState=$openState") if (openState) { - // 串口回调在 IO 线程,通过 Channel 无锁传递数据,不再嵌套 launch SerialPortManager.startReceive { data -> - serialDataChannel.trySend(data) + // 更新UI显示接收数据 + lifecycleScope.launch { + try { + receiveSerialPortData(data) + } catch (e: Exception) { + e.printStackTrace() + } + } } //SerialPortManager.send(DEVICE_INFO_CMD) } - } - - // 串口数据消费:单一协程串行处理,serialReader 访问不存在并发竞争 - lifecycleScope.launch { - for (data in serialDataChannel) { - try { - receiveSerialPortData(data) - } catch (e: Exception) { - e.printStackTrace() - } - } - } - - // ViewModel 状态观察:STARTED 状态下收集,onStop 时自动取消,onStart 时恢复 - lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { + repeatOnLifecycle(Lifecycle.State.CREATED) { launch { viewModel.getAccessTokenUiState.collect { state -> when (state) { @@ -206,16 +202,16 @@ class HomeActivity : BaseActivity() { } private fun loadTestData() { - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 1, goodsName = "鸡蛋", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 6, goodsName = "", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 2, goodsName = "", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 7, goodsName = "生姜", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 3, goodsName = "黄瓜", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 8, goodsName = "", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 4, goodsName = "梨", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 9, goodsName = "梨", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 5, goodsName = "", deviceId = App.deviceId)) - homeViewModel.shelfItems.add(ShelfModel(deviceNo = 10, goodsName = "", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 1, goodsName = "鸡蛋", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 6, goodsName = "", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 2, goodsName = "", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 7, goodsName = "生姜", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 3, goodsName = "黄瓜", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 8, goodsName = "", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 4, goodsName = "梨", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 9, goodsName = "梨", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 5, goodsName = "", deviceId = App.deviceId)) + list.add(ShelfModel(deviceNo = 10, goodsName = "", deviceId = App.deviceId)) shelfAdapter.notifyDataSetChanged() } @@ -255,12 +251,12 @@ class HomeActivity : BaseActivity() { // toast("数据错误") // return // } - homeViewModel.shelfItems.clear() + list.clear() val subList01 = tempList.subList(0, tempList.size / 2) val subList02 = tempList.subList(tempList.size / 2, tempList.size) repeat(tempList.size / 2) { index -> - homeViewModel.shelfItems.add(subList01[index].also { it.deviceId = App.deviceId }) - homeViewModel.shelfItems.add(subList02[index].also { it.deviceId = App.deviceId }) + list.add(subList01[index].also { it.deviceId = App.deviceId }) + list.add(subList02[index].also { it.deviceId = App.deviceId }) } shelfAdapter.notifyDataSetChanged() } @@ -281,18 +277,7 @@ class HomeActivity : BaseActivity() { */ private var terminalVersion: String = "" - /** - * 串口数据接收通道,无界缓冲。 - * IO 线程(串口回调)通过 trySend 写入,单一消费协程串行读取, - * 确保 serialReader 始终在同一协程中访问,规避并发竞争。 - */ - private val serialDataChannel = Channel(Channel.UNLIMITED) - - /** - * 串口数据拼包缓冲器。 - * 仅由 serialDataChannel 的单一消费协程访问,线程安全。 - */ - private val serialReader = StringBuilder() + private val serialReader by lazy { StringBuilder() } private fun receiveSerialPortData(srcData: String) { log("receiveSerialPortData: $srcData") @@ -429,7 +414,7 @@ class HomeActivity : BaseActivity() { //D6DADEDB1001015D020000000027001E00020000001E001C00B40000000000010000000100040000001900440003000F00000000000FFFE6000200010002FFD80044000300010005001E0001FFD80056001400280000000000000000008300090000003C00D2000000000000007F003700000000A100000031323136334238423134303233303456322E313100FFFF runOnUiThread { if (tipDialog == null) { - tipDialog = CommonDialog(this).apply { + CommonDialog(this).apply { dialogTitle = "温馨提示" // dialogContent = logMsg confirmBlock = { @@ -528,21 +513,21 @@ class HomeActivity : BaseActivity() { var realWeight = 0 var firstByteBinary = hexToBinary(weightHex.substring(0, 2)) if (firstByteBinary.startsWith("0")) { - //正数:按十六进制解析,失败时兜底为 0 - realWeight = weightHex.toIntOrNull(16) ?: 0 + //正数 + realWeight = weightHex.toInt() } else { - //负数:清除符号位后取绝对值,再取反 + //负数 firstByteBinary = "0${firstByteBinary.substring(1)}" val firstByteHex = binaryToHex(firstByteBinary) weightHex = firstByteHex + weightHex.substring(2) - realWeight = -1 * (weightHex.toIntOrNull(16) ?: 0) + realWeight = -1 * weightHex.toInt() } - homeViewModel.putWeight(index, realWeight) + weightArray.put(index, realWeight) - if (index in 1..10 && homeViewModel.shelfItems.size == 10) { - homeViewModel.shelfItems.firstOrNull { it.deviceNo == index }?.let { + if (index in 1..10 && list.size == 10) { + list.firstOrNull { it.deviceNo == index }?.let { it.weight = realWeight.toDouble() - val pos = homeViewModel.shelfItems.indexOf(it) + val pos = list.indexOf(it) shelfAdapter.notifyItemChanged(pos) log("getWeightInfo: deviceNo=${it.deviceNo},realWeight=${realWeight}") EventBus.getDefault().post(SendWeightEvent(it.deviceNo, realWeight)) @@ -623,44 +608,35 @@ class HomeActivity : BaseActivity() { // TaskManager.cancelTask() saveTaskJob?.cancel() overdueTaskJob?.cancel() - // 关闭 Channel,消费协程的 for 循环将随之结束 - serialDataChannel.close() - // 销毁前关闭 Dialog,防止 Activity 已销毁时窗口仍附着导致泄漏 - tipDialog?.dismiss() - tipDialog = null EventBus.getDefault().unregister(this) super.onDestroy() } + @Suppress("DEPRECATION") private val shelfAdapter by lazy { - ShelfAdapter(homeViewModel.shelfItems).apply { + ShelfAdapter(list).apply { setOnItemClickListener { _, _, position -> - homeViewModel.selectedShelfIndex = position + shelfIndex = position lifecycleScope.launch { if (isNeedOpenLock) { - val openCmd = if (homeViewModel.shelfItems[position].deviceNo in 1..5) + val openCmd = if (list[position].deviceNo in 1..5) LEFT_SHELF_OPEN_CMD else RIGHT_SHELF_OPEN_CMD val openState = SerialPortManager.send(openCmd) } // if (openState.not()) { -// toast("${homeViewModel.shelfItems[position].goodsName}柜门开启失败") +// toast("${list[position].goodsName}柜门开启失败") // return@launch // } //SerialPortManager.send(START_CMD) launch(Intent(this@HomeActivity, ShelfActivity::class.java).also { - it.putExtra(ShelfActivity.SHELF_MODEL, homeViewModel.shelfItems[position]) + it.putExtra(ShelfActivity.SHELF_MODEL, list[position]) }) { it?.run { - // API 33 起支持带类型参数的重载,低版本回退旧 API + as? 安全转换 - val model = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - getSerializableExtra(ShelfActivity.SHELF_MODEL, ShelfModel::class.java) - } else { - @Suppress("DEPRECATION") - getSerializableExtra(ShelfActivity.SHELF_MODEL) as? ShelfModel - } ?: return@run - homeViewModel.shelfItems[position].apply { + val model = + getSerializableExtra(ShelfActivity.SHELF_MODEL) as ShelfModel + list[position].apply { goodsId = model.goodsId goodsName = model.goodsName weight = model.weight @@ -672,12 +648,12 @@ class HomeActivity : BaseActivity() { try { - val submitList = homeViewModel.shelfItems.sortedBy { item -> item.deviceNo } + val submitList = list.sortedBy { item -> item.deviceNo } val body = ShelfBody().also { body -> body.deviceId = App.deviceId ?: "" body.canteenId = App.canteenId - body.temperature = homeViewModel.temperatureC - body.humidity = homeViewModel.humidity + body.temperature = showTemperatureC + body.humidity = showHumidity body.goodsList = submitList } Log.d("mzf", "performSync: body:${body.toJsonString()}") @@ -738,7 +714,7 @@ class HomeActivity : BaseActivity() { @SuppressLint("NotifyDataSetChanged") private fun loadEmptyView() { - homeViewModel.shelfItems.clear() + list.clear() shelfAdapter.notifyDataSetChanged() binding.include?.let { @@ -759,7 +735,7 @@ class HomeActivity : BaseActivity() { @Subscribe(threadMode = ThreadMode.MAIN) public fun clearEmptyShelf(event: ClearShelfEvent) { - val item = homeViewModel.shelfItems.firstOrNull { it.deviceNo == event.shelfNo } + val item = list.firstOrNull { it.deviceNo == event.shelfNo } item?.apply { goodsId = "" goodsName = null @@ -768,7 +744,7 @@ class HomeActivity : BaseActivity() { createTime = null overdueDay = null } - shelfAdapter.notifyItemChanged(homeViewModel.shelfItems.indexOf(item)) + shelfAdapter.notifyItemChanged(list.indexOf(item)) } private val taskExecutor by lazy { IntervalExecutor() } @@ -797,14 +773,10 @@ class HomeActivity : BaseActivity() { private fun updateOverdueState() { viewModel.updateOverdueState { state, items -> if (items.isNullOrEmpty()) return@updateOverdueState - homeViewModel.shelfItems.forEachIndexed { index, model -> - // 按 deviceNo 精确匹配接口数据,避免: - // 1. 两端列表大小不一致时的 IndexOutOfBoundsException - // 2. 本地列表交叉排列与接口顺序不一致导致的状态串位 - val serverModel = items.find { it.deviceNo == model.deviceNo } - ?: return@forEachIndexed - if (serverModel.overdue != model.overdue) { - model.overdue = serverModel.overdue + list.forEachIndexed { index, model -> + //接口过期状态变化后更新状态 + if (items[index].overdue != model.overdue) { + model.overdue = items[index].overdue shelfAdapter.notifyItemChanged(index) log("更新编号为${model.deviceNo}物品:${model.goodsName}的过期状态:${model.overdue}") } @@ -813,18 +785,22 @@ class HomeActivity : BaseActivity() { } private fun saveShelfGoodsList() { - log("performSync: list:${homeViewModel.shelfItems.toJsonString()}") - val submitList = homeViewModel.shelfItems.sortedBy { it.deviceNo } + log("performSync: list:${list.toJsonString()}") + val submitList = list.sortedBy { it.deviceNo } submitList.forEach { if (it.goodsId.isNullOrBlank()) { it.weight = it.weightBak } } +// submitList.forEach { +// it.temperature = HomeActivity.showTemperatureC +// it.humidity = HomeActivity.showHumidity +// } val body = ShelfBody().also { it.deviceId = App.deviceId it.canteenId = App.canteenId - it.temperature = homeViewModel.temperatureC - it.humidity = homeViewModel.humidity + it.temperature = showTemperatureC + it.humidity = showHumidity it.goodsList = submitList } log("performSync: body:${body.toJsonString()}") diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/net/HomeViewModel.kt b/app/src/main/java/com/shuwei/intelligent/shelves/net/HomeViewModel.kt deleted file mode 100644 index d228d8b..0000000 --- a/app/src/main/java/com/shuwei/intelligent/shelves/net/HomeViewModel.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.shuwei.intelligent.shelves.net - -import androidx.lifecycle.ViewModel -import com.shuwei.intelligent.shelves.model.ShelfModel - -/** - * HomeActivity 专用 ViewModel。 - * - * 职责:管理原本散落在 HomeActivity.companion object 中的 UI 状态数据, - * 避免静态共享状态导致的生命周期污染与状态残留问题。 - * - * 包含:货架列表、重量数据、选中索引、温湿度。 - */ -class HomeViewModel : ViewModel() { - - /** - * 货架列表。 - * Adapter 持有同一个列表引用,修改后需调用 notifyDataSetChanged / notifyItemChanged 触发刷新。 - */ - val shelfItems: MutableList = mutableListOf() - - /** - * 重量数据缓存。 - * key:deviceNo(货架编号),value:重量(克)。 - * 替代原 SparseIntArray,使用更符合 Kotlin 习惯的 MutableMap。 - */ - private val weightMap: MutableMap = mutableMapOf() - - /** - * 当前选中货架的列表索引(点击 item 时更新)。 - */ - var selectedShelfIndex: Int = 0 - - /** - * 当前摄氏温度,由串口心跳数据更新,字符串格式(如 "24.5")。 - */ - var temperatureC: String = "0" - - /** - * 当前湿度,由串口心跳数据更新,字符串格式(如 "60")。 - */ - var humidity: String = "0" - - /** - * 更新指定货架编号的重量。 - * @param deviceNo 货架编号 - * @param weight 重量(克) - */ - fun putWeight(deviceNo: Int, weight: Int) { - weightMap[deviceNo] = weight - } - - /** - * 获取指定货架编号的重量。 - * @param deviceNo 货架编号 - * @return 重量(克),若无记录返回 0 - */ - fun getWeight(deviceNo: Int): Int = weightMap[deviceNo] ?: 0 -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 801716e..8c889e6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ #agp = "8.11.1" agp = "8.4.0" kotlin = "2.0.21" -coreKtx = "1.12.0" +coreKtx = "1.10.1" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1"