优化并增加日志

This commit is contained in:
马增飞
2025-11-18 18:49:46 +08:00
parent 79d14ca300
commit 096499cd65
17 changed files with 482 additions and 155 deletions
@@ -0,0 +1,651 @@
package com.shuwei.intelligent.shelves.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
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
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager
import com.shuwei.intelligent.shelves.App
import com.shuwei.intelligent.shelves.R
import com.shuwei.intelligent.shelves.adapter.ShelfAdapter
import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityHomeBinding
import com.shuwei.intelligent.shelves.model.ClearShelfEvent
import com.shuwei.intelligent.shelves.model.SendWeightEvent
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.Loading
import com.shuwei.intelligent.shelves.net.NetViewModel
import com.shuwei.intelligent.shelves.net.RespData
import com.shuwei.intelligent.shelves.net.UiState
import com.shuwei.intelligent.shelves.net.apiService
import com.shuwei.intelligent.shelves.serial.SerialPortManager
import com.shuwei.intelligent.shelves.task.TaskManager
import com.shuwei.intelligent.shelves.utils.binaryToHex
import com.shuwei.intelligent.shelves.utils.ext.gone
import com.shuwei.intelligent.shelves.utils.ext.toJsonString
import com.shuwei.intelligent.shelves.utils.ext.toast
import com.shuwei.intelligent.shelves.utils.ext.visible
import com.shuwei.intelligent.shelves.utils.f2C
import com.shuwei.intelligent.shelves.utils.hexToBinary
import com.shuwei.intelligent.shelves.utils.hexToDec
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class HomeActivity : BaseActivity() {
companion object {
private const val TAG = "HomeActivity"
private const val MM_DD_EEEE__HH_MM_SS = "MM月dd日 EEEE HH:mm:ss"
//协议头
const val HEADER = "D6DADEDB"
//协议尾
const val FOOTER = "FFFF"
// const val ACTIVE_CMD =
// "${HEADER}000281C5D70A7D428A4FA67B50F7DACA241938938B4B9CBD73673E9C8E7F2E15062D${FOOTER}"
const val LEFT_SHELF_OPEN_CMD =
"${HEADER}020101000000000000000000000000000000000000$FOOTER"
const val RIGHT_SHELF_OPEN_CMD =
"${HEADER}020100010000000000000000000000000000000000$FOOTER"
//const val TEMPERATURE_CMD = "${HEADER}1001${FOOTER}"
const val DEVICE_INFO_CMD = "${HEADER}0301000000000000000000000000$FOOTER"
//const val START_CMD= "${HEADER}0F01360000${FOOTER}"
//const val START_CMD= "${HEADER}0F01050020${FOOTER}"
const val ACTIVE_CMD =
"${HEADER}000281C5D70A7D428948AD7254FADBB1516738938B4B9CBD73673E9D8E7F2E15062D$FOOTER"
//开灯(开 继电器1
const val LIGHT_CLOSE = "${HEADER}020102020102020000000000000000000000000000$FOOTER"
const val LIGHT_OPEN = "${HEADER}020102020002020000000000000000000000000000$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"
}
private lateinit var binding: ActivityHomeBinding
private val viewModel: NetViewModel by viewModels()
@SuppressLint("NotifyDataSetChanged", "HardwareIds")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
EventBus.getDefault().register(this)
setBackground()
setContentView(binding.root)
updateLeftStatus("-°C / -%")
initRecyclerView()
lifecycleScope.launch {
val openState = SerialPortManager.open()
log("onCreate: openState=$openState")
if (openState) {
SerialPortManager.startReceive { data ->
// 更新UI显示接收数据
lifecycleScope.launch {
try {
receiveSerialPortData(data)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
//SerialPortManager.send(DEVICE_INFO_CMD)
}
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.getAccessTokenUiState.collect { state ->
when (state) {
is UiState.Loading -> {}
is UiState.Success<*> -> getTokenSuccess(state.data)
is UiState.Error -> toast(state.msg)
else -> {}
}
}
}
launch {
viewModel.getShelfListUiState.collect { state ->
when (state) {
is UiState.Loading -> showProgress()
is UiState.Success<*> -> updateUI(state.data)
is UiState.Error -> showError(state.msg)
else -> {}
}
}
}
}
}
TaskManager.startTask()
// var androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
//// androidId = "d0e7a2d5f25c775c"
// log("onCreate: androidId=${androidId}")
// if (androidId == "65e1012638f78260" || androidId == "40f89e91ee40611d") {
// androidId = "d0e7a2d5f25c775c"
// }
// App.deviceId = androidId
// //"53babf69-0d2f-3875-a649-f2e12f80ad4c1"
//
// val swDpValue = resources?.configuration?.smallestScreenWidthDp
// log("onCreate: swDpValue=$swDpValue")
// viewModel.getAccessToken(App.deviceId)
viewModel.getShelfList(deviceId = App.deviceId)
// repeat(100) {
// lifecycleScope.launch {
// val data = "D6DADEDB01010C6102000000000000010000000500008000000000060000000500000004000080060000000100000010190080701800800000000000000000000000000000000000000000000000005037414645333633333334333731373056322E313069FFFF"
// receiveSerialPortData(data)
// delay(500)
// }
// }
// lifecycleScope.launch {
// repeat(100) {
// val sb = StringBuilder().apply {
//// 610200000000000001000000050000800000000006000000050000000400008006000000010000001019008070180080
// repeat(12) {
// val num = (0..99).random()
// val num2 = (0..99).random()
// val num3 = (0..99).random()
// val num4 = (0..1).random()
// val numStr = if (num <= 9) "0${num}" else "" + num
// val numStr2 = if (num2 <= 9) "0${num2}" else "" + num2
// val numStr3 = if (num3 <= 9) "0${num3}" else "" + num3
// val numStr4 = if (num4 == 1) "80" else "00"
// append(numStr).append(numStr2).append(numStr3).append(numStr4)
// }
// }
// val data =
// "${HEADER}0302000000000C${sb}5037414645333633333334333731373056322E313069${FOOTER}"
// receiveSerialPortData(data)
// delay(500)
// }
// }
}
private fun getTokenSuccess(data: RespData<*>) {
log("getTokenSuccess: $data")
data.data?.let {
App.accessToken = it.toString()
//UIUtils.toast(it.toString())
viewModel.getShelfList(deviceId = App.deviceId)
}
}
private fun showProgress() {
Loading.show(this)
}
@SuppressLint("NotifyDataSetChanged")
private fun updateUI(data: RespData<*>) {
binding.include?.root?.gone()
window?.decorView?.postDelayed({ Loading.dismiss() }, 500)
if (data.data !is ShelfResult) {
loadEmptyView()
return
}
deviceName = data.data.deviceName
App.canteenId = data.data.placeId
val tempList = data.data.containerGoodsList
if (tempList.isNullOrEmpty()) {
loadEmptyView()
return
}
// if (tempList.size < 10) {
// toast("数据错误")
// return
// }
list.clear()
val subList01 = tempList.subList(0, tempList.size / 2)
val subList02 = tempList.subList(tempList.size / 2, tempList.size)
repeat(tempList.size / 2) { index ->
list.add(subList01[index].also { it.deviceId = App.deviceId })
list.add(subList02[index].also { it.deviceId = App.deviceId })
}
shelfAdapter.notifyDataSetChanged()
}
private fun showError(message: String) {
Loading.dismiss()
toast(message)
loadEmptyView()
}
/**
* 终端id
*/
private var terminalId: String = ""
/**
* 固件版本
*/
private var terminalVersion: String = ""
private val serialReader by lazy { StringBuilder() }
private suspend fun receiveSerialPortData(srcData: String) {
log("receiveSerialPortData: $srcData")
var data = ""
if (srcData.startsWith(HEADER) && srcData.contains(FOOTER)) {
serialReader.clear()
val endIndex = srcData.indexOf(FOOTER) + FOOTER.length
serialReader.append(srcData.substring(0, endIndex))
data = serialReader.toString()
} else if (srcData.startsWith(HEADER) && srcData.contains(FOOTER).not()) {
serialReader.clear()
serialReader.append(srcData).append(",")
return
} else if (srcData.startsWith(HEADER).not() && srcData.contains(FOOTER).not()) {
serialReader.append(srcData).append(",")
return
} else {
val endIndex = srcData.indexOf(FOOTER) + FOOTER.length
serialReader.append(srcData.substring(0, endIndex))
log("receiveSerialPortData:serialReader: $serialReader")
data = serialReader.toString().replace(",", "")
}
val cmd = data.substring(HEADER.length, HEADER.length + 4)
when (cmd) {
"0001" -> {
//终端发起激活指令,拿到固定数据体(终端ID-15字节、固件版本-5字节、命令次数-1字节)
val reqBody = data.substring(HEADER.length + cmd.length, data.length - HEADER.length)
if (reqBody.length == 64) {
terminalId = reqBody.substring(2, 32)
terminalVersion = reqBody.substring(32, 42)
log("receiveSerialPortData:收到[激活]${cmd.substring(0,2)}指令:${data},终端id:${terminalId},固件版本:${terminalVersion},发送激活命令:$ACTIVE_CMD")
sendCmd(ACTIVE_CMD)
} else {
runOnUiThread {
toast("收到终端激活指令错误")
}
sendCmd(ACTIVE_CMD)
}
}
"0101","8101" -> {
//心跳0102${p1}${p2}${version}
//心跳81(终端发起,当终端发送此命令时需要服务端存储串口屏显示信息)
val respCmd = "${HEADER}01020106${terminalVersion}$FOOTER"
log("receiveSerialPortData:收到[心跳]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
sendCmd(respCmd)
heartBeat(data)
}
"0202" -> {
//开关锁反馈020200${deviceId}${version}
val respCmd = "${HEADER}020200${terminalId}${terminalVersion}$FOOTER"
log("receiveSerialPortData:收到[开锁]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
sendCmd(respCmd)
//lockFeedback(data)
}
"0302" -> {
//获取设备状态
getDeviceInfo(data)
}
"0501" -> {
//卡号上发
val respCmd = "${HEADER}050203$FOOTER"
log("receiveSerialPortData:收到[卡号上发]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
sendCmd(respCmd)
}
"0B01" -> {
//操作记录上传
val respCmd = "${HEADER}0B0203$FOOTER"
log("receiveSerialPortData:收到[操作记录上传]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
sendCmd(respCmd)
}
"0C01" -> {
//对时
val timeLong = System.currentTimeMillis() / 1000 + (8 * 3600)
val timeStr = "0000000" + String.format("%X", timeLong)
val time = timeStr.substring(timeStr.length - 8).chunked(2).reversed().joinToString("")
val respCmd = "${HEADER}0B02${time}$FOOTER"
log("receiveSerialPortData:收到[对时]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
sendCmd(respCmd)
}
"0E01" -> {
//终端位置上报
val respCmd = "${HEADER}0E0200$FOOTER"
log("receiveSerialPortData:收到[终端位置上报]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
sendCmd(respCmd)
}
"1001" -> {
//温控信息上报
val respCmd = "${HEADER}100200$FOOTER"
log("receiveSerialPortData:收到[温控信息上报]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
sendCmd(respCmd)
}
//"1002" -> {
// log("receiveSerialPortData,温控信息上报1002数据:${data}")
//}
//"0F02" -> {
// log("receiveSerialPortData,温控信息应答数据:${data}")
//}
else -> {}
}
}
private var fixedDataBody = ""
// private fun lockFeedback(data: String) {
// runCatching {
// val fixedDataBodyEndIndex = data.length - FOOTER.length
// val fixedDataBodyStartIndex = HEADER.length + "0202".length + 42
// fixedDataBody = data.substring(fixedDataBodyStartIndex, fixedDataBodyEndIndex)
//
// val lockNo = if (list[shelfIndex].deviceNo in 1..5) "01" else "02"
// val lockOneState = data.substring(HEADER.length + 4, HEADER.length + 6)
// if (lockOneState == "00") {
// //锁1关
// SerialPortManager.send("${HEADER}0202${lockNo}${FOOTER}")
// }
// val lockTwoState = data.substring(HEADER.length + 6, HEADER.length + 8)
// if (lockTwoState == "00") {
// //锁2关
// SerialPortManager.send("${HEADER}0202${lockNo}${FOOTER}")
// }
// }.onFailure {
// it.printStackTrace()
// }
// }
private fun heartBeat(data: String) {
runCatching {
if (data.length < 200) {
return
}
val weightStartIndex = 14
val weightEndIndex = weightStartIndex + 12 * 8
getWeightInfo(
start = weightStartIndex,
end = weightEndIndex,
data = data
)
val temperatureStartIndex = 158
val temperatureEndIndex = temperatureStartIndex + 2
val temperatureHex = data.substring(temperatureStartIndex, temperatureEndIndex)
val temperature = hexToDec(temperatureHex)
val tempC = f2C(temperature.toDouble())
showTemperatureC = "%.1f".format(tempC)
//val percent = 10 * list.count { it.goodsName.isNullOrBlank().not() }
updateLeftStatus("${deviceName} [$temperatureHex] ${showTemperatureC}°C / -%")
}.onFailure {
it.printStackTrace()
}
}
private var deviceName = ""
private fun getWeightInfo(start: Int, end: Int, data: String) {
var index = 1
var count = start
while (count < end) {
val weightInfo = data.substring(count, count + 8)
var weightHex = ""
weightInfo.run {
weightHex =
substring(6, 8) + substring(4, 6) + substring(2, 4) + substring(0, 2)
}
var realWeight = 0
var firstByteBinary = hexToBinary(weightHex.substring(0, 2))
if (firstByteBinary.startsWith("0")) {
//正数
realWeight = weightHex.toInt()
} else {
//负数
firstByteBinary = "0${firstByteBinary.substring(1)}"
val firstByteHex = binaryToHex(firstByteBinary)
weightHex = firstByteHex + weightHex.substring(2)
realWeight = -1 * weightHex.toInt()
}
weightArray.put(index, realWeight)
if (index <= 10) {
list.firstOrNull { it.deviceNo == index }?.let {
it.weight = realWeight.toDouble()
val pos = list.indexOf(it)
shelfAdapter.notifyItemChanged(pos)
log("getWeightInfo: deviceNo=${it.deviceNo},realWeight=${realWeight}")
EventBus.getDefault().post(SendWeightEvent(it.deviceNo, realWeight))
}
}
count += 8
index++
}
// ShelfActivity.updateWeight(getRealWeight())
// if (list.isNotEmpty()) {
// val intent = Intent(ShelfActivity.RECEIVER_DEVICE_INFO + list[shelfIndex].deviceNo)
// intent.putExtra(ShelfActivity.SHELF_WEIGHT, weightArray[shelfIndex])
// LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
// }
val temperatureEndIndex = end
if (terminalId.isBlank() || terminalVersion.isBlank()) {
try {
val fixDataBodyStartIndex = temperatureEndIndex + 1
val fixDataBody = data.substring(fixDataBodyStartIndex, data.length- FOOTER.length)
if (fixDataBody.length == 42) {
terminalId = fixDataBody.substring(0, 30)
terminalVersion = fixDataBody.substring(30, 40)
log("receiveSerialPortData:从设备状态获取数据,终端id:${terminalId},固件版本:${terminalVersion}")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun getDeviceInfo(data: String) {
log("getDeviceInfo: data=$data")
val weightStartIndex = HEADER.length + 14
val temperatureStartIndex = weightStartIndex + 12 * 8
val temperatureEndIndex = temperatureStartIndex + 2
val temperatureHex = try {
data.substring(temperatureStartIndex, temperatureEndIndex)
} catch (e: Exception) {
e.printStackTrace()
return
}
val temperature = hexToDec(temperatureHex)
val tempC = f2C(temperature.toDouble())
showTemperatureC = "%.1f".format(tempC)
//val percent = 10 * list.count { it.goodsName.isNullOrBlank().not() }
updateLeftStatus("${deviceName} ${showTemperatureC}°C / -%")
getWeightInfo(
start = weightStartIndex,
end = temperatureStartIndex,
data = data
)
}
override fun onDestroy() {
lifecycleScope.launch {
SerialPortManager.close()
}
TaskManager.cancelTask()
EventBus.getDefault().unregister(this)
super.onDestroy()
}
@Suppress("DEPRECATION")
private val shelfAdapter by lazy {
ShelfAdapter(list).apply {
setOnItemClickListener { _, _, position ->
shelfIndex = position
lifecycleScope.launch {
// 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("${list[position].goodsName}柜门开启失败")
// return@launch
// }
//SerialPortManager.send(START_CMD)
launch(Intent(this@HomeActivity, ShelfActivity::class.java).also {
it.putExtra(ShelfActivity.SHELF_MODEL, list[position])
}) {
it?.run {
val model =
getSerializableExtra(ShelfActivity.SHELF_MODEL) as ShelfModel
list[position].apply {
goodsId = model.goodsId
goodsName = model.goodsName
weight = model.weight
if (model.putCabinetDay.isNullOrBlank().not()) {
putCabinetDay = model.putCabinetDay
}
}
notifyItemChanged(position)
try {
val submitList = list.sortedBy { item -> item.deviceNo }
val body = ShelfBody().also { body ->
body.deviceId = App.deviceId ?: ""
body.canteenId = App.canteenId
body.temperature = showTemperatureC
body.humidity = showHumidity
body.goodsList = submitList
}
Log.d("mzf", "performSync: body:${body.toJsonString()}")
lifecycleScope.launch {
val resp = apiService.saveShelfGoodsList(body = body)
Log.d("mzf", "performSync: resp:${resp.toJsonString()}")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
}
}
}
private fun initRecyclerView() {
binding.rvShelf.run {
layoutManager =
GridLayoutManager(this@HomeActivity, 2, GridLayoutManager.VERTICAL, false)
adapter = shelfAdapter
itemAnimator.let {
if (it is DefaultItemAnimator) {
it.supportsChangeAnimations = false
}
}
}
}
private fun updateDateTime() {
val sdf = SimpleDateFormat(MM_DD_EEEE__HH_MM_SS, Locale.CHINA)
val dateTime = sdf.format(Date())
updateRightStatus(dateTime)
}
private val handler = Handler(Looper.getMainLooper())
private val updateTask = object : Runnable {
override fun run() {
updateDateTime()
handler.postDelayed(this, 1000)
}
}
override fun onResume() {
super.onResume()
hideStatusBar()
handler.post(updateTask)
}
override fun onPause() {
super.onPause()
handler.removeCallbacks(updateTask)
}
@SuppressLint("NotifyDataSetChanged")
private fun loadEmptyView() {
list.clear()
shelfAdapter.notifyDataSetChanged()
binding.include?.let {
it.root.visible()
it.root.setOnClickListener {
//viewModel.getAccessToken(App.deviceId)
viewModel.getShelfList(deviceId = App.deviceId)
}
it.ivEmptyIcon.setImageResource(R.drawable.ic_empty_white)
it.tvEmptyContent.setTextColor(Color.WHITE)
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public fun weightEvent(event: WeightEvent) {
}
@Subscribe(threadMode = ThreadMode.MAIN)
public fun clearEmptyShelf(event: ClearShelfEvent) {
val item = list.firstOrNull { it.deviceNo == event.shelfNo }
item?.apply {
goodsId = ""
goodsName = null
weight = 0.0
weightBak = if (event.weight <= 0.0) 0.0 else -1 * event.weight.toDouble()
createTime = null
overdueDay = null
}
shelfAdapter.notifyItemChanged(list.indexOf(item))
}
}
@@ -0,0 +1,162 @@
package com.shuwei.intelligent.shelves.activity
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import androidx.activity.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.shuwei.intelligent.shelves.App
import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityInitBinding
import com.shuwei.intelligent.shelves.model.DeviceConfigInfo
import com.shuwei.intelligent.shelves.net.NetViewModel
import com.shuwei.intelligent.shelves.net.UiState
import com.shuwei.intelligent.shelves.net.UrlConfig
import com.shuwei.intelligent.shelves.utils.AppUtil
import com.shuwei.intelligent.shelves.utils.QRCodeUtil
import com.shuwei.intelligent.shelves.utils.SpTool
import com.shuwei.intelligent.shelves.utils.ext.dp
import com.shuwei.intelligent.shelves.utils.ext.invisible
import com.shuwei.intelligent.shelves.utils.ext.startActivity
import com.shuwei.intelligent.shelves.utils.ext.toJsonString
import com.shuwei.intelligent.shelves.utils.ext.toObject
import com.shuwei.intelligent.shelves.utils.ext.toast
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlin.collections.forEach
import kotlin.getValue
import kotlin.ranges.downTo
import kotlin.text.isBlank
class InitActivity : BaseActivity() {
companion object {
const val TAG = "InitActivity"
}
private lateinit var binding: ActivityInitBinding
private val viewModel: NetViewModel by viewModels()
@SuppressLint("HardwareIds")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityInitBinding.inflate(layoutInflater)
setContentView(binding.root)
App.deviceId = AppUtil.getUDID( this)
SpTool.put(SpTool.DEVICE_ID, App.deviceId)
App.configUrl = UrlConfig.BASE_URL
App.canteenId = "0"
// var deviceId = AppUtil.getUDID(this)
// Log.d(TAG, "onCreate: deviceId=$deviceId")
//// deviceId = "39a7abdd06b3c7ab"
//// deviceId = "3ea47dc0-3cf0-3c2f-909c-265a9a65572e"
// App.deviceId = deviceId
//
// SpTool.put(SpTool.DEVICE_ID, deviceId)
// val deviceConfigCache = SpTool.getString(SpTool.DEVICE_CONFIG_CACHE)
// val checkResult = checkConfigData(deviceConfigCache)
// if (checkResult.not()) {
// binding.ivQrCode.visible()
// binding.btnInit.visible()
// //进行初始化操作
// initConfig()
// return
// }
binding.ivQrCode.invisible()
binding.btnInit.invisible()
countDown()
}
private fun countDown() {
lifecycleScope.launch {
flow {
(2 downTo 1).forEach {
delay(1000)
emit(it)
}
}.collect {
// 倒计时结束执行跳转
if (it == 1) {
startActivity<HomeActivity>()
finish()
}
}
}
}
private fun initConfig() {
binding.ivQrCode.setImageBitmap(
QRCodeUtil.generateQRCode(
content = App.deviceId,
size = 200.dp
)
)
binding.btnInit.setOnClickListener {
viewModel.getDeviceToken(App.deviceId)
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.getDeviceTokenUiState.collect { state ->
when (state) {
is UiState.Loading -> {}
is UiState.Success<*> -> {
state.data.data?.let { deviceToken ->
Log.d(TAG, "initConfig: $deviceToken")
viewModel.getDeviceConfig(
deviceId = App.deviceId,
deviceToken = deviceToken.toString()
)
}
}
is UiState.Error -> toast(state.msg)
else -> {}
}
}
}
launch {
viewModel.getDeviceConfigUiState.collect { state ->
when (state) {
is UiState.Loading -> {}
is UiState.Success<*> -> {
state.data.data?.let {
if (it is DeviceConfigInfo) {
SpTool.put(SpTool.DEVICE_CONFIG_CACHE, it.toJsonString())
App.configUrl = it.appPackageUrl?:""
App.canteenId = it.canteenId?:""
startActivity<HomeActivity>()
finish()
}
}
}
is UiState.Error -> toast(message = state.msg)
else -> {}
}
}
}
}
}
}
private fun checkConfigData(data: String): Boolean {
if (data.isBlank()) {
return false
}
val config = data.toObject<DeviceConfigInfo?>()
if (config == null) {
return false
}
App.configUrl = config.appPackageUrl?:""
App.canteenId = config.canteenId?:""
return true
}
}
@@ -0,0 +1,57 @@
package com.shuwei.intelligent.shelves.activity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.shuwei.intelligent.shelves.adapter.LogAdapter
import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityLogListBinding
import com.shuwei.intelligent.shelves.utils.ext.startActivity
class LogActivity : BaseActivity() {
companion object {
const val IS_LOG_DIR = "isLogDir"
const val LOG_DATE = "logDate"
}
private lateinit var binding:ActivityLogListBinding
private var isLogDir = false
private var logDate = ""
private val logList = mutableListOf<String>()
private val logAdapter by lazy {
LogAdapter(logList).apply {
setOnItemClickListener { _, _, position ->
if (isLogDir.not()) {
return@setOnItemClickListener
}
startActivity<LogActivity> {
putExtra(IS_LOG_DIR, false)
putExtra(LOG_DATE, logList[position])
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLogListBinding.inflate(layoutInflater)
setBackground()
setContentView(binding.root)
isLogDir = intent.getBooleanExtra(IS_LOG_DIR, false)
logDate = intent.getStringExtra(LOG_DATE) ?: ""
binding.ivBack.setOnClickListener { finish() }
binding.tvTitle.text = if (isLogDir) "全部日志" else logDate
if (isLogDir) {
logList.addAll(fileLogger.getLogFiles())
} else {
logList.addAll(fileLogger.readLogsByName(logDate))
}
binding.rvLogList.let {
it.layoutManager = LinearLayoutManager(this)
it.adapter = logAdapter
}
}
}
@@ -0,0 +1,66 @@
package com.shuwei.intelligent.shelves.activity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivitySettingBinding
import com.shuwei.intelligent.shelves.utils.KeyboardUtil
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class SettingActivity: BaseActivity() {
private lateinit var binding: ActivitySettingBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySettingBinding.inflate(layoutInflater)
setBackground()
setContentView(binding.root)
binding.btnSendCmd.setOnClickListener{
val cmd = binding.etInputCmd.text.toString().trim()
if (cmd.isBlank()) {
return@setOnClickListener
}
var record = binding.tvCmdRecord.text.toString().trim()
record = cmd+"\n"+record
binding.tvCmdRecord.text = record
val realCmd = "${HomeActivity.HEADER}0F01${cmd}${HomeActivity.FOOTER}"
sendCmd(realCmd)
}
binding.ivBack.setOnClickListener { finish() }
binding.root.setOnClickListener { v ->
KeyboardUtil.hideKeyboard(v)
}
}
private fun updateDateTime() {
val sdf = SimpleDateFormat(ShelfActivity.YYYY_MM_DD__EEEE_HH_MM_SS, Locale.CHINA)
val dateTime = sdf.format(Date())
val arr = dateTime.split("***")
updateLeftStatus(arr[0])
updateRightStatus(arr[1])
}
private val handler = Handler(Looper.getMainLooper())
private val updateTask = object : Runnable {
override fun run() {
updateDateTime()
handler.postDelayed(this, 1000)
}
}
override fun onResume() {
super.onResume()
hideStatusBar()
handler.post(updateTask)
}
override fun onPause() {
super.onPause()
handler.removeCallbacks(updateTask)
}
}
@@ -0,0 +1,390 @@
package com.shuwei.intelligent.shelves.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.LinearLayout
import androidx.activity.viewModels
import androidx.core.view.updateLayoutParams
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.GridLayoutManager
import com.chad.library.adapter4.util.setOnDebouncedItemClick
import com.scwang.smart.refresh.layout.constant.RefreshState
import com.shuwei.intelligent.shelves.App
import com.shuwei.intelligent.shelves.R
import com.shuwei.intelligent.shelves.activity.HomeActivity.Companion.DEVICE_INFO_CMD
import com.shuwei.intelligent.shelves.adapter.SearchAdapter
import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityShelfBinding
import com.shuwei.intelligent.shelves.model.ClearShelfEvent
import com.shuwei.intelligent.shelves.model.GoodsModel
import com.shuwei.intelligent.shelves.model.SendWeightEvent
import com.shuwei.intelligent.shelves.model.ShelfModel
import com.shuwei.intelligent.shelves.net.Loading
import com.shuwei.intelligent.shelves.net.NetViewModel
import com.shuwei.intelligent.shelves.net.RespData
import com.shuwei.intelligent.shelves.net.UiState
import com.shuwei.intelligent.shelves.task.TaskManager
import com.shuwei.intelligent.shelves.utils.DateTimeUtil
import com.shuwei.intelligent.shelves.utils.KeyboardUtil
import com.shuwei.intelligent.shelves.utils.ext.addOnActionSearchListener
import com.shuwei.intelligent.shelves.utils.ext.clickWithDebounce
import com.shuwei.intelligent.shelves.utils.ext.dp
import com.shuwei.intelligent.shelves.utils.ext.gone
import com.shuwei.intelligent.shelves.utils.ext.toJsonString
import com.shuwei.intelligent.shelves.utils.ext.toast
import com.shuwei.intelligent.shelves.utils.ext.visible
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@SuppressLint("NotifyDataSetChanged")
class ShelfActivity : BaseActivity() {
companion object {
private const val TAG = "ShelfActivity"
const val YYYY_MM_DD__EEEE_HH_MM_SS = "yyyy年MM月dd日 EEEE***HH:mm:ss"
const val SHELF_MODEL = "shelfModel"
const val RECEIVER_DEVICE_INFO = "receiverDeviceInfo"
const val SHELF_WEIGHT = "shelfWeight"
const val SHELF_INDEX = "shelfIndex"
const val PAGE_SIZE = 100
}
private lateinit var binding: ActivityShelfBinding
private var shelfModel: ShelfModel? = null
private var clickIndex: Int = -1
private var realWeight: Int = 0
private var pageNo: Int = 1
private val viewModel: NetViewModel by viewModels()
@SuppressLint("UnspecifiedRegisterReceiverFlag")
@Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityShelfBinding.inflate(layoutInflater)
setBackground()
setContentView(binding.root)
EventBus.getDefault().register(this)
// tvFoodWeight =binding.tvFoodWeight
shelfModel = intent.getSerializableExtra(SHELF_MODEL) as ShelfModel?
shelfModel?.let {
binding.tvShelfName.text = it.shelfName
binding.tvFoodName.text = if (it.goodsName.isNullOrBlank()) "-" else it.goodsName
realWeight = it.weight?.toInt()?:0
binding.tvFoodWeight.text = if(realWeight < 1000) "${realWeight}" else "%.3f千克".format(realWeight/1000.0)
startTime = System.currentTimeMillis()
}
initRecyclerView()
binding.etInputFood.run {
addOnActionSearchListener {
if (text.isNullOrBlank()) {
toast(hint.toString())
return@addOnActionSearchListener
}
pageNo = 1
getGoodsList()
KeyboardUtil.hideKeyboard(context, this)
}
addTextChangedListener(afterTextChanged = { editable ->
if (editable.isNullOrBlank()) {
pageNo = 1
getGoodsList()
}
})
}
binding.btnConfirm.clickWithDebounce {
if (clickIndex < 0) {
toast("请选择食材")
return@clickWithDebounce
}
val currentGoodsId = list[clickIndex].goodsId ?: ""
val data = ShelfModel(
shelfName = shelfModel?.shelfName,
goodsId = currentGoodsId,
goodsName = list[clickIndex].goodsName,
weight = realWeight.toDouble(),
overdueDay = list[clickIndex].overdueDay,
// createTime = if (shelfModel?.goodsId == currentGoodsId) null else DateTimeUtil.formatDateTime(dateTime = Date())
putCabinetDay = DateTimeUtil.formatDateTime(dateTime = Date())
)
val intent = Intent().apply {
putExtra(SHELF_MODEL, data)
}
setResult(RESULT_OK, intent)
finish()
}
binding.root.setOnClickListener { v ->
KeyboardUtil.hideKeyboard(v)
}
binding.btnClearZero.setOnClickListener {
clearZero()
}
binding.btnClearEmpty.setOnClickListener {
val tempWeight = shelfModel?.weight?.toInt()?:0
clearZero()
binding.tvFoodName.text = "-"
//binding.tvFoodWeight.text = "0克"
EventBus.getDefault().post(ClearShelfEvent(shelfModel!!.deviceNo, tempWeight))
}
sendCmd(DEVICE_INFO_CMD)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.getGoodsListUiState.collect { state ->
when (state) {
is UiState.Loading -> showProgress()
is UiState.Success<*> -> updateUI(state.data)
is UiState.Error -> showError(state.msg)
else -> {}
}
}
}
}
// registerReceiver(receiver, IntentFilter(RECEIVER_DEVICE_INFO + shelfModel?.deviceNo))
binding.ivBack?.setOnClickListener { finish() }
binding.refreshLayout.let {
it.setEnableRefresh(true)
it.setEnableLoadMore(false)
it.setOnRefreshListener {
pageNo = 1
getGoodsList()
}
it.setOnLoadMoreListener { getGoodsList() }
}
getGoodsList()
TaskManager.startDeviceTask()
}
private fun finishRefresh() {
binding.refreshLayout.let {
if (it.state == RefreshState.Refreshing) {
it.finishRefresh(500)
} else if (binding.refreshLayout.state == RefreshState.Loading) {
it.finishLoadMore(500)
} else {
}
}
}
private fun getGoodsList() {
viewModel.getGoodsList(
canteenId = App.canteenId,
goodsName = binding.etInputFood.text.trim().toString(),
pageNo = pageNo,
pageSize = PAGE_SIZE
)
}
private fun showProgress() {
Loading.show(this)
}
@SuppressLint("NotifyDataSetChanged")
private fun updateUI(data: RespData<*>) {
binding.include?.root?.gone()
finishRefresh()
binding.root.postDelayed({ Loading.dismiss() }, 200)
Log.d(TAG, "updateUI: ${data.toJsonString()}")
val goodsList = data.data as MutableList<GoodsModel>
if (data.data.isNullOrEmpty()) {
if (pageNo == 1) {
loadEmptyView()
}
return
}
if(pageNo == 1){
list.clear()
}
// var firstOne:GoodsModel?=null
// if (pageNo == 1) {
// if (shelfModel?.goodsId.isNullOrBlank().not()) {
// firstOne = list.firstOrNull { it.goodsId == shelfModel?.goodsId }
// if (firstOne == null) {
// firstOne = GoodsModel(
// goodsId = shelfModel?.goodsId,
// goodsName = shelfModel?.goodsName,
// isClicked = true
// )
// }
// }
// list.clear()
// }
// val records = data.data.records
// if (pageNo == 1) {
// firstOne?.let { one ->
// val filterResult = records.firstOrNull{one.goodsId == it.goodsId }
// if (filterResult != null) {
// records.remove(filterResult)
// }
// clickIndex = 0
// list.add(one)
// }
// }
list.addAll(goodsList)
val enableLoadMore = goodsList.size >= PAGE_SIZE
binding.refreshLayout.setEnableLoadMore(enableLoadMore)
if (enableLoadMore) {
pageNo++
}
searchAdapter.notifyDataSetChanged()
}
private fun showError(message: String) {
finishRefresh()
Loading.dismiss()
toast(message)
if (pageNo == 1) {
loadEmptyView()
}
}
// private val receiver = object : BroadcastReceiver() {
// override fun onReceive(context: Context?, intent: Intent?) {
// intent?.let {
// if (it.action == RECEIVER_DEVICE_INFO + shelfModel?.deviceNo) {
// val weight = it.getDoubleExtra(SHELF_WEIGHT, 0.0)
// Log.d(TAG, "getDeviceInfo:onReceive: weight=$weight")
// realWeight = (weight * 1000).toInt()
// binding.tvFoodWeight.text = "${realWeight}克"
// }
// }
// }
// }
override fun onDestroy() {
// unregisterReceiver(receiver)
EventBus.getDefault().unregister(this)
TaskManager.cancelDeviceTask()
super.onDestroy()
}
private val list: MutableList<GoodsModel> = mutableListOf()
private val searchAdapter by lazy {
SearchAdapter(list).apply {
setOnDebouncedItemClick { adapter, v, position ->
this@ShelfActivity.clickIndex = position
list.forEach { it.isClicked = false }
list[position].isClicked = true
notifyDataSetChanged()
binding.tvFoodName.text = list[position].goodsName
KeyboardUtil.hideKeyboard(v.context, v)
}
}
}
private fun initRecyclerView() {
binding.rvSearch.run {
layoutManager = GridLayoutManager(this@ShelfActivity, 2)
adapter = searchAdapter
}
}
private fun updateDateTime() {
val sdf = SimpleDateFormat(YYYY_MM_DD__EEEE_HH_MM_SS, Locale.CHINA)
val dateTime = sdf.format(Date())
val arr = dateTime.split("***")
updateLeftStatus(arr[0])
updateRightStatus(arr[1])
}
private val handler = Handler(Looper.getMainLooper())
private val updateTask = object : Runnable {
override fun run() {
updateDateTime()
handler.postDelayed(this, 1000)
}
}
override fun onResume() {
super.onResume()
hideStatusBar()
handler.post(updateTask)
}
override fun onPause() {
super.onPause()
handler.removeCallbacks(updateTask)
}
@SuppressLint("NotifyDataSetChanged")
private fun loadEmptyView() {
list.clear()
searchAdapter.notifyDataSetChanged()
binding.include?.let {
it.root.visible()
it.root.setOnClickListener {
pageNo = 1
getGoodsList()
}
it.ivEmptyIcon.setImageResource(R.drawable.ic_empty_gray)
it.ivEmptyIcon.updateLayoutParams<LinearLayout.LayoutParams> {
val swDpValue = resources?.configuration?.smallestScreenWidthDp ?: 0
width = if (swDpValue > 600) 200.dp else 120.dp
}
}
binding.refreshLayout.run {
setEnableRefresh(false)
setEnableLoadMore(false)
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public fun receiveWeightEvent(event: SendWeightEvent) {
if (event.shelfNo == (shelfModel?.deviceNo ?: 0)) {
val intervalTime = System.currentTimeMillis() - startTime
val weight = event.weight
log("编号${event.shelfNo}货架获取重量成功:${weight}克,数据获取间隔时间为:${intervalTime}")
binding.tvFoodWeight.text = if(weight < 1000) "${weight}" else "%.3f千克".format(weight/1000.0)
startTime = System.currentTimeMillis()
window.decorView.postDelayed({ Loading.dismiss() }, 2000)
}
}
private fun clearZero() {
lifecycleScope.launch {
val shelfNo = shelfModel?.deviceNo ?: 0
val hex = shelfNo.toString(16).padStart(2, '0').uppercase()
Log.d(TAG, "onCreate: hex=$hex")
val zeroClearingCmd = "${HomeActivity.HEADER}0401${hex}${HomeActivity.FOOTER}"
log("编号${shelfNo}货架执行清零指令:${zeroClearingCmd}")
sendCmd(zeroClearingCmd)
//清零
realWeight = 0
// binding.tvFoodWeight.text = "${realWeight}克"
sendCmd(DEVICE_INFO_CMD)
startTime = System.currentTimeMillis()
Loading.show(this@ShelfActivity)
// window.decorView.postDelayed({ Loading.dismiss() }, 5000)
}
KeyboardUtil.hideKeyboard(window.decorView)
}
private var startTime = 0L
}