diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/serial/ScaleManager.kt b/app/src/main/java/com/shuwei/intelligent/shelves/serial/ScaleManager.kt index e6aa076..4411d28 100644 --- a/app/src/main/java/com/shuwei/intelligent/shelves/serial/ScaleManager.kt +++ b/app/src/main/java/com/shuwei/intelligent/shelves/serial/ScaleManager.kt @@ -76,8 +76,8 @@ object ScaleManager { /** 当前设备激活码,非空时收到激活请求自动应答 */ private var activateCode: String = "" - /** 心跳应答:心跳上报间隔(秒),默认5秒 */ - var heartbeatInterval: Int = 5 + /** 心跳应答:心跳上报间隔(秒),默认1秒,保证重量更新实时性 */ + var heartbeatInterval: Int = 1 /** 心跳应答:开锁后未开门自动落锁时间(秒),默认6秒 */ var lockTimeout: Int = 6 diff --git a/app/src/main/java/com/sw/scalefusion/shelf/HomeV3Activity.kt b/app/src/main/java/com/sw/scalefusion/shelf/HomeV3Activity.kt index b70f599..b1da6f5 100644 --- a/app/src/main/java/com/sw/scalefusion/shelf/HomeV3Activity.kt +++ b/app/src/main/java/com/sw/scalefusion/shelf/HomeV3Activity.kt @@ -46,12 +46,20 @@ import org.greenrobot.eventbus.ThreadMode class HomeV3Activity : BaseActivity() { - companion object {} + companion object { + /** 重量同步后台间隔(秒) */ + private const val SYNC_INTERVAL_SECONDS = 30L + /** 首次同步延迟(秒) */ + private const val SYNC_INITIAL_DELAY_SECONDS = 10L + } override val enableRightStatusMenu: Boolean = true private val list: MutableList = mutableListOf() + /** 秤号(slotNo 数字部分)→ list 索引 的快速查找表,心跳回调中 O(1) 定位 */ + private val scaleIndexToPosition = mutableMapOf() + private var shelfIndex = 0 private fun isNeedOpenLock() = App.deviceId == "4787e213-90ab-3e32-88e0-ac271a937751" @@ -72,15 +80,12 @@ class HomeV3Activity : BaseActivity() { .init(activateCode = ProtocolConstants.ACTIVE_MAP[App.deviceId] ?: "") .onHeartbeat { _, data -> data.weights.forEach { weight -> - val shelfNo = weight.index - val pos = list.indexOfFirst { - it.slotNo.filter { c -> c.isDigit() }.toIntOrNull() == shelfNo - } - val model = list.getOrNull(pos) ?: return@forEach + val pos = scaleIndexToPosition[weight.index] ?: return@forEach + val model = list[pos] model.weight = weight.grams / 1000.0 shelfAdapter.notifyItemChanged(pos) log("getWeightInfo: slotNo=${model.slotNo}, realWeight=${weight.grams}, temp=${data.temperature}") - EventBus.getDefault().post(SendWeightEvent(shelfNo, weight.grams)) + EventBus.getDefault().post(SendWeightEvent(weight.index, weight.grams)) } } .onTempReport { frame -> @@ -158,7 +163,8 @@ class HomeV3Activity : BaseActivity() { log("updateUI: slots=${slots.size}, rows=$rows, columns=$columns") list.clear() - list.addAll(slots) + list.addAll(reorderSlotsByDeviceLayout(slots, columns, rows)) + buildScaleIndexMap() val displayMetrics = resources.displayMetrics // 减去 RecyclerView + item 自身的 margin @@ -172,6 +178,64 @@ class HomeV3Activity : BaseActivity() { } } + /** + * 按设备物理布局重排格口列表。 + * + * API 返回的 slots 按 slotNo 升序排列。FlexboxLayoutManager(FlexDirection.COLUMN) + * 按列填充(先填满第一列 5 行,再换第二列),3 列设备需要重排才能正确显示: + * + * 期望显示: 列填充需要的列表序: + * [1] [6] [7] [1, 2, 3, 4, 5, ← 第1列 + * [2] [8] [9] 6, 8, 10, 12, 14, ← 第2列 + * [3][10] [11] 7, 9, 11, 13, 15] ← 第3列 + * [4][12] [13] + * [5][14] [15] + * + * 2 列设备 API 原序 [1..10] 列填充后恰好正确,无需重排。 + * + * @param slots API 返回的格口列表(按 slotNo 升序) + * @param columns 列数(API horizontalRows) + * @param rows 行数(API verticalCount) + */ + private fun reorderSlotsByDeviceLayout( + slots: List, + columns: Int, + rows: Int + ): List { + // 2 列及以下无需重排,API 原序列填充后恰好正确 + if (columns <= 2) return ArrayList(slots) + + // 按 slotNo 数字部分建立查找表 + val slotMap = HashMap(slots.size) + for (slot in slots) { + val num = slot.slotNo.filter { it.isDigit() }.toIntOrNull() ?: continue + slotMap[num] = slot + } + + val result = ArrayList(slots.size) + for (displayCol in 0 until columns) { + for (displayRow in 0 until rows) { + // 物理布局 → FlexDirection.COLUMN 列填充所需的列表位置 + val slotNo = when (displayCol) { + 0 -> displayRow + 1 // 左列: 1,2,3,4,5 + 1 -> rows + 1 + displayRow * 2 // 中列: 6,8,10,12,14 + else -> rows + 2 + displayRow * 2 // 右列: 7,9,11,13,15 + } + slotMap[slotNo]?.let { result.add(it) } + } + } + return result + } + + /** 预建秤号→列表索引映射表,供心跳回调 O(1) 查找 */ + private fun buildScaleIndexMap() { + scaleIndexToPosition.clear() + list.forEachIndexed { pos, model -> + val scaleNo = model.slotNo.filter { it.isDigit() }.toIntOrNull() ?: return@forEachIndexed + scaleIndexToPosition[scaleNo] = pos + } + } + private fun showError(message: String) { Loading.dismiss() toast(message) @@ -302,6 +366,7 @@ class HomeV3Activity : BaseActivity() { @SuppressLint("NotifyDataSetChanged") private fun loadEmptyView() { list.clear() + scaleIndexToPosition.clear() shelfAdapter.notifyDataSetChanged() binding.include?.let { it.root.visible() @@ -319,10 +384,7 @@ class HomeV3Activity : BaseActivity() { @Subscribe(threadMode = ThreadMode.MAIN) fun clearEmptyShelf(event: ClearShelfEvent) { - val index = list.indexOfFirst { - it.slotNo?.filter { c -> c.isDigit() }?.toIntOrNull() == event.shelfNo - } - if (index < 0) return + val index = scaleIndexToPosition[event.shelfNo] ?: return list[index] = list[index].copy(materName = "") shelfAdapter.notifyItemChanged(index) } @@ -332,7 +394,10 @@ class HomeV3Activity : BaseActivity() { private fun saveGoodsTask() { saveTaskJob = - taskExecutor.startIntervalTaskWithInitialDelay(10 * 1000L, 1 * 60 * 1000L) { + taskExecutor.startIntervalTaskWithInitialDelay( + SYNC_INITIAL_DELAY_SECONDS * 1000L, + SYNC_INTERVAL_SECONDS * 1000L + ) { syncShelfGoodsToServer() } }