@@ -3,11 +3,11 @@ package com.shuwei.intelligent.shelves.activity
import android.annotation.SuppressLint
import android.annotation.SuppressLint
import android.content.Intent
import android.content.Intent
import android.graphics.Color
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Bundle
import android.os.Handler
import android.os.Handler
import android.os.Looper
import android.os.Looper
import android.util.Log
import android.util.Log
import android.util.SparseIntArray
import androidx.activity.viewModels
import androidx.activity.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
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.ShelfModel
import com.shuwei.intelligent.shelves.model.ShelfResult
import com.shuwei.intelligent.shelves.model.ShelfResult
import com.shuwei.intelligent.shelves.model.WeightEvent
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.Loading
import com.shuwei.intelligent.shelves.net.NetViewModel
import com.shuwei.intelligent.shelves.net.NetViewModel
import com.shuwei.intelligent.shelves.net.RespData
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.ext.visible
import com.shuwei.intelligent.shelves.utils.hexToBinary
import com.shuwei.intelligent.shelves.utils.hexToBinary
import kotlinx.coroutines.Job
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.Subscribe
@@ -99,6 +97,16 @@ class HomeActivity : BaseActivity() {
const val TEMPERATURE _CTRL = " ${HEADER} 0F01040000 ${FOOTER} "
const val TEMPERATURE _CTRL = " ${HEADER} 0F01040000 ${FOOTER} "
val list : MutableList < ShelfModel > = 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
//401-false, 1楼餐厅-true
@@ -107,7 +115,6 @@ class HomeActivity : BaseActivity() {
private lateinit var binding : ActivityHomeBinding
private lateinit var binding : ActivityHomeBinding
private val viewModel : NetViewModel by viewModels ( )
private val viewModel : NetViewModel by viewModels ( )
private val homeViewModel : HomeViewModel by viewModels ( )
@SuppressLint ( " NotifyDataSetChanged " , " HardwareIds " )
@SuppressLint ( " NotifyDataSetChanged " , " HardwareIds " )
override fun onCreate ( savedInstanceState : Bundle ? ) {
override fun onCreate ( savedInstanceState : Bundle ? ) {
@@ -121,23 +128,13 @@ class HomeActivity : BaseActivity() {
updateLeftStatus ( " " )
updateLeftStatus ( " " )
initRecyclerView ( )
initRecyclerView ( )
// 串口初始化:独立协程,仅执行一次
lifecycleScope . launch {
lifecycleScope . launch {
val openState = SerialPortManager . open ( )
val openState = SerialPortManager . open ( )
log ( " onCreate: openState= $openState " )
log ( " onCreate: openState= $openState " )
if ( openState ) {
if ( openState ) {
// 串口回调在 IO 线程,通过 Channel 无锁传递数据,不再嵌套 launch
SerialPortManager . startReceive { data ->
SerialPortManager . startReceive { data ->
serialDataChannel . trySend ( data )
// 更新UI显示接收数据
}
//SerialPortManager.send(DEVICE_INFO_CMD)
}
}
// 串口数据消费:单一协程串行处理,serialReader 访问不存在并发竞争
lifecycleScope . launch {
lifecycleScope . launch {
for ( data in serialDataChannel ) {
try {
try {
receiveSerialPortData ( data )
receiveSerialPortData ( data )
} catch ( e : Exception ) {
} catch ( e : Exception ) {
@@ -145,10 +142,9 @@ class HomeActivity : BaseActivity() {
}
}
}
}
}
}
//SerialPortManager.send(DEVICE_INFO_CMD)
// ViewModel 状态观察:STARTED 状态下收集,onStop 时自动取消,onStart 时恢复
}
lifecycleScope . launch {
repeatOnLifecycle ( Lifecycle . State . CREATED ) {
repeatOnLifecycle ( Lifecycle . State . STARTED ) {
launch {
launch {
viewModel . getAccessTokenUiState . collect { state ->
viewModel . getAccessTokenUiState . collect { state ->
when ( state ) {
when ( state ) {
@@ -206,16 +202,16 @@ class HomeActivity : BaseActivity() {
}
}
private fun loadTestData ( ) {
private fun loadTestData ( ) {
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 1 , goodsName = " 鸡蛋 " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 1 , goodsName = " 鸡蛋 " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 6 , goodsName = " " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 6 , goodsName = " " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 2 , goodsName = " " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 2 , goodsName = " " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 7 , goodsName = " 生姜 " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 7 , goodsName = " 生姜 " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 3 , goodsName = " 黄瓜 " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 3 , goodsName = " 黄瓜 " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 8 , goodsName = " " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 8 , goodsName = " " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 4 , goodsName = " 梨 " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 4 , goodsName = " 梨 " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 9 , goodsName = " 梨 " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 9 , goodsName = " 梨 " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 5 , goodsName = " " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 5 , goodsName = " " , deviceId = App . deviceId ) )
homeViewModel . shelfItems . add ( ShelfModel ( deviceNo = 10 , goodsName = " " , deviceId = App . deviceId ) )
list . add ( ShelfModel ( deviceNo = 10 , goodsName = " " , deviceId = App . deviceId ) )
shelfAdapter . notifyDataSetChanged ( )
shelfAdapter . notifyDataSetChanged ( )
}
}
@@ -255,12 +251,12 @@ class HomeActivity : BaseActivity() {
// toast("数据错误")
// toast("数据错误")
// return
// return
// }
// }
homeViewModel . shelfItems . clear ( )
list . clear ( )
val subList01 = tempList . subList ( 0 , tempList . size / 2 )
val subList01 = tempList . subList ( 0 , tempList . size / 2 )
val subList02 = tempList . subList ( tempList . size / 2 , tempList . size )
val subList02 = tempList . subList ( tempList . size / 2 , tempList . size )
repeat ( tempList . size / 2 ) { index ->
repeat ( tempList . size / 2 ) { index ->
homeViewModel . shelfItems . add ( subList01 [ index ] . also { it . deviceId = App . deviceId } )
list . add ( subList01 [ index ] . also { it . deviceId = App . deviceId } )
homeViewModel . shelfItems . add ( subList02 [ index ] . also { it . deviceId = App . deviceId } )
list . add ( subList02 [ index ] . also { it . deviceId = App . deviceId } )
}
}
shelfAdapter . notifyDataSetChanged ( )
shelfAdapter . notifyDataSetChanged ( )
}
}
@@ -281,18 +277,7 @@ class HomeActivity : BaseActivity() {
*/
*/
private var terminalVersion : String = " "
private var terminalVersion : String = " "
/**
private val serialReader by lazy { StringBuilder ( ) }
* 串口数据接收通道,无界缓冲。
* IO 线程(串口回调)通过 trySend 写入,单一消费协程串行读取,
* 确保 serialReader 始终在同一协程中访问,规避并发竞争。
*/
private val serialDataChannel = Channel < String > ( Channel . UNLIMITED )
/**
* 串口数据拼包缓冲器。
* 仅由 serialDataChannel 的单一消费协程访问,线程安全。
*/
private val serialReader = StringBuilder ( )
private fun receiveSerialPortData ( srcData : String ) {
private fun receiveSerialPortData ( srcData : String ) {
log ( " receiveSerialPortData: $srcData " )
log ( " receiveSerialPortData: $srcData " )
@@ -429,7 +414,7 @@ class HomeActivity : BaseActivity() {
//D6DADEDB1001015D020000000027001E00020000001E001C00B40000000000010000000100040000001900440003000F00000000000FFFE6000200010002FFD80044000300010005001E0001FFD80056001400280000000000000000008300090000003C00D2000000000000007F003700000000A100000031323136334238423134303233303456322E313100FFFF
//D6DADEDB1001015D020000000027001E00020000001E001C00B40000000000010000000100040000001900440003000F00000000000FFFE6000200010002FFD80044000300010005001E0001FFD80056001400280000000000000000008300090000003C00D2000000000000007F003700000000A100000031323136334238423134303233303456322E313100FFFF
runOnUiThread {
runOnUiThread {
if ( tipDialog == null ) {
if ( tipDialog == null ) {
tipDialog = CommonDialog ( this ) . apply {
CommonDialog ( this ) . apply {
dialogTitle = " 温馨提示 "
dialogTitle = " 温馨提示 "
// dialogContent = logMsg
// dialogContent = logMsg
confirmBlock = {
confirmBlock = {
@@ -528,21 +513,21 @@ class HomeActivity : BaseActivity() {
var realWeight = 0
var realWeight = 0
var firstByteBinary = hexToBinary ( weightHex . substring ( 0 , 2 ) )
var firstByteBinary = hexToBinary ( weightHex . substring ( 0 , 2 ) )
if ( firstByteBinary . startsWith ( " 0 " ) ) {
if ( firstByteBinary . startsWith ( " 0 " ) ) {
//正数:按十六进制解析,失败时兜底为 0
//正数
realWeight = weightHex . toIntOrNull ( 16 ) ?: 0
realWeight = weightHex . toInt ( )
} else {
} else {
//负数:清除符号位后取绝对值,再取反
//负数
firstByteBinary = " 0 ${firstByteBinary.substring(1)} "
firstByteBinary = " 0 ${firstByteBinary.substring(1)} "
val firstByteHex = binaryToHex ( firstByteBinary )
val firstByteHex = binaryToHex ( firstByteBinary )
weightHex = firstByteHex + weightHex . substring ( 2 )
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 ) {
if ( index in 1 .. 10 && list . size == 10 ) {
homeViewModel . shelfItems . firstOrNull { it . deviceNo == index } ?. let {
list . firstOrNull { it . deviceNo == index } ?. let {
it . weight = realWeight . toDouble ( )
it . weight = realWeight . toDouble ( )
val pos = homeViewModel . shelfItems . indexOf ( it )
val pos = list . indexOf ( it )
shelfAdapter . notifyItemChanged ( pos )
shelfAdapter . notifyItemChanged ( pos )
log ( " getWeightInfo: deviceNo= ${it.deviceNo} ,realWeight= ${realWeight} " )
log ( " getWeightInfo: deviceNo= ${it.deviceNo} ,realWeight= ${realWeight} " )
EventBus . getDefault ( ) . post ( SendWeightEvent ( it . deviceNo , realWeight ) )
EventBus . getDefault ( ) . post ( SendWeightEvent ( it . deviceNo , realWeight ) )
@@ -623,44 +608,35 @@ class HomeActivity : BaseActivity() {
// TaskManager.cancelTask()
// TaskManager.cancelTask()
saveTaskJob ?. cancel ( )
saveTaskJob ?. cancel ( )
overdueTaskJob ?. cancel ( )
overdueTaskJob ?. cancel ( )
// 关闭 Channel,消费协程的 for 循环将随之结束
serialDataChannel . close ( )
// 销毁前关闭 Dialog,防止 Activity 已销毁时窗口仍附着导致泄漏
tipDialog ?. dismiss ( )
tipDialog = null
EventBus . getDefault ( ) . unregister ( this )
EventBus . getDefault ( ) . unregister ( this )
super . onDestroy ( )
super . onDestroy ( )
}
}
@Suppress ( " DEPRECATION " )
private val shelfAdapter by lazy {
private val shelfAdapter by lazy {
ShelfAdapter ( homeViewModel . shelfItems ) . apply {
ShelfAdapter ( list ) . apply {
setOnItemClickListener { _ , _ , position ->
setOnItemClickListener { _ , _ , position ->
homeViewModel . selectedShelfIndex = position
shelfIndex = position
lifecycleScope . launch {
lifecycleScope . launch {
if ( isNeedOpenLock ) {
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
LEFT _SHELF _OPEN _CMD
else
else
RIGHT _SHELF _OPEN _CMD
RIGHT _SHELF _OPEN _CMD
val openState = SerialPortManager . send ( openCmd )
val openState = SerialPortManager . send ( openCmd )
}
}
// if (openState.not()) {
// if (openState.not()) {
// toast("${homeViewModel.shelfItems [position].goodsName}柜门开启失败")
// toast("${list [position].goodsName}柜门开启失败")
// return@launch
// return@launch
// }
// }
//SerialPortManager.send(START_CMD)
//SerialPortManager.send(START_CMD)
launch ( Intent ( this @HomeActivity , ShelfActivity :: class . java ) . also {
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 {
it ?. run {
// API 33 起支持带类型参数的重载,低版本回退旧 API + as? 安全转换
val model =
val model = if ( Build . VERSION . SDK _INT >= Build . VERSION_CODES . TIRAMISU ) {
getSerializableExtra ( ShelfActivity . SHELF _MODEL ) as ShelfModel
getSerializableExtra ( ShelfActivity . SHELF _MODEL , ShelfModel :: class . java )
list [ position ] . apply {
} else {
@Suppress ( " DEPRECATION " )
getSerializableExtra ( ShelfActivity . SHELF _MODEL ) as ? ShelfModel
} ?: return @run
homeViewModel . shelfItems [ position ] . apply {
goodsId = model . goodsId
goodsId = model . goodsId
goodsName = model . goodsName
goodsName = model . goodsName
weight = model . weight
weight = model . weight
@@ -672,12 +648,12 @@ class HomeActivity : BaseActivity() {
try {
try {
val submitList = homeViewModel . shelfItems . sortedBy { item -> item . deviceNo }
val submitList = list . sortedBy { item -> item . deviceNo }
val body = ShelfBody ( ) . also { body ->
val body = ShelfBody ( ) . also { body ->
body . deviceId = App . deviceId ?: " "
body . deviceId = App . deviceId ?: " "
body . canteenId = App . canteenId
body . canteenId = App . canteenId
body . temperature = homeViewModel . temperatureC
body . temperature = showTemperatureC
body . humidity = homeViewModel . humidity
body . humidity = showHumidity
body . goodsList = submitList
body . goodsList = submitList
}
}
Log . d ( " mzf " , " performSync: body: ${body.toJsonString()} " )
Log . d ( " mzf " , " performSync: body: ${body.toJsonString()} " )
@@ -738,7 +714,7 @@ class HomeActivity : BaseActivity() {
@SuppressLint ( " NotifyDataSetChanged " )
@SuppressLint ( " NotifyDataSetChanged " )
private fun loadEmptyView ( ) {
private fun loadEmptyView ( ) {
homeViewModel . shelfItems . clear ( )
list . clear ( )
shelfAdapter . notifyDataSetChanged ( )
shelfAdapter . notifyDataSetChanged ( )
binding . include ?. let {
binding . include ?. let {
@@ -759,7 +735,7 @@ class HomeActivity : BaseActivity() {
@Subscribe ( threadMode = ThreadMode . MAIN )
@Subscribe ( threadMode = ThreadMode . MAIN )
public fun clearEmptyShelf ( event : ClearShelfEvent ) {
public fun clearEmptyShelf ( event : ClearShelfEvent ) {
val item = homeViewModel . shelfItems . firstOrNull { it . deviceNo == event . shelfNo }
val item = list . firstOrNull { it . deviceNo == event . shelfNo }
item ?. apply {
item ?. apply {
goodsId = " "
goodsId = " "
goodsName = null
goodsName = null
@@ -768,7 +744,7 @@ class HomeActivity : BaseActivity() {
createTime = null
createTime = null
overdueDay = null
overdueDay = null
}
}
shelfAdapter . notifyItemChanged ( homeViewModel . shelfItems . indexOf ( item ) )
shelfAdapter . notifyItemChanged ( list . indexOf ( item ) )
}
}
private val taskExecutor by lazy { IntervalExecutor ( ) }
private val taskExecutor by lazy { IntervalExecutor ( ) }
@@ -797,14 +773,10 @@ class HomeActivity : BaseActivity() {
private fun updateOverdueState ( ) {
private fun updateOverdueState ( ) {
viewModel . updateOverdueState { state , items ->
viewModel . updateOverdueState { state , items ->
if ( items . isNullOrEmpty ( ) ) return @updateOverdueState
if ( items . isNullOrEmpty ( ) ) return @updateOverdueState
homeViewModel . shelfItems . forEachIndexed { index , model ->
list . forEachIndexed { index , model ->
// 按 deviceNo 精确匹配接口数据,避免:
//接口过期状态变化后更新状态
// 1. 两端列表大小不一致时的 IndexOutOfBoundsException
if ( items [ index ] . overdue != model . overdue ) {
// 2. 本地列表交叉排列与接口顺序不一致导致的状态串位
model . overdue = items [ index ] . overdue
val serverModel = items . find { it . deviceNo == model . deviceNo }
?: return @forEachIndexed
if ( serverModel . overdue != model . overdue ) {
model . overdue = serverModel . overdue
shelfAdapter . notifyItemChanged ( index )
shelfAdapter . notifyItemChanged ( index )
log ( " 更新编号为 ${model.deviceNo} 物品: ${model.goodsName} 的过期状态: ${model.overdue} " )
log ( " 更新编号为 ${model.deviceNo} 物品: ${model.goodsName} 的过期状态: ${model.overdue} " )
}
}
@@ -813,18 +785,22 @@ class HomeActivity : BaseActivity() {
}
}
private fun saveShelfGoodsList ( ) {
private fun saveShelfGoodsList ( ) {
log ( " performSync: list: ${homeViewModel.shelfItems .toJsonString()} " )
log ( " performSync: list: ${list .toJsonString()} " )
val submitList = homeViewModel . shelfItems . sortedBy { it . deviceNo }
val submitList = list . sortedBy { it . deviceNo }
submitList . forEach {
submitList . forEach {
if ( it . goodsId . isNullOrBlank ( ) ) {
if ( it . goodsId . isNullOrBlank ( ) ) {
it . weight = it . weightBak
it . weight = it . weightBak
}
}
}
}
// submitList.forEach {
// it.temperature = HomeActivity.showTemperatureC
// it.humidity = HomeActivity.showHumidity
// }
val body = ShelfBody ( ) . also {
val body = ShelfBody ( ) . also {
it . deviceId = App . deviceId
it . deviceId = App . deviceId
it . canteenId = App . canteenId
it . canteenId = App . canteenId
it . temperature = homeViewModel . temperatureC
it . temperature = showTemperatureC
it . humidity = homeViewModel . humidity
it . humidity = showHumidity
it . goodsList = submitList
it . goodsList = submitList
}
}
log ( " performSync: body: ${body.toJsonString()} " )
log ( " performSync: body: ${body.toJsonString()} " )