package com.shuwei.intelligent.shelves 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.provider.Settings import android.util.Log import android.util.SparseArray import android.view.View 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.chad.library.adapter4.util.setOnDebouncedItemClick 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.SyncTask 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.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext 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 ACTIVE_CMD = "${HEADER}000281C5D70A7D428948AD7254FADBB1516738938B4B9CBD73673E9D8E7F2E15062D${FOOTER}" //开灯(开 继电器1) const val LIGHT_CLOSE = "${HEADER}020102020102020000000000000000000000000000${FOOTER}" const val LIGHT_OPEN = "${HEADER}020102020002020000000000000000000000000000${FOOTER}" // const val LIGHT_CLOSE = "${HEADER}020102020101010000000000000000000000000000${FOOTER}" // const val LIGHT_OPEN = "${HEADER}020102020000000000000000000000000000000000${FOOTER}" // const val LIGHT_CLOSE2 = "${HEADER}020102020102020000000000000000000000000000${FOOTER}" // const val LIGHT_OPEN2 = "${HEADER}020102020002020000000000000000000000000000${FOOTER}" // const val LIGHT_CLOSE3 = "${HEADER}020102020102020000000000000000000000000000${FOOTER}" // const val LIGHT_OPEN3 = "${HEADER}020102020002020000000000000000000000000000${FOOTER}" val list: MutableList = mutableListOf() val weightArray = SparseArray() 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("1-1冷藏柜 -°C / -%") initRecyclerView() lifecycleScope.launch { val openState = SerialPortManager.open() Log.d(TAG, "onCreate: openState=$openState") if (openState) { SerialPortManager.startReceive { data -> runOnUiThread { // 更新UI显示接收数据 receiveSerialPortData(data) } } SerialPortManager.send(ACTIVE_CMD) //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.d(TAG, "onCreate: androidId=${androidId}") // if (androidId == "65e1012638f78260" || androidId == "40f89e91ee40611d") { // androidId = "d0e7a2d5f25c775c" // } // App.deviceId = androidId // //"53babf69-0d2f-3875-a649-f2e12f80ad4c1" // // val swDpValue = resources?.configuration?.smallestScreenWidthDp // Log.d(TAG, "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) // } // } lightSwitchClick() } fun switchLight(isOpen: Boolean) { val tag = getLeftView().tag?.toString()?.toBoolean() ?: true if(tag == isOpen) { return } lifecycleScope.launch { SerialPortManager.send(if (isOpen) LIGHT_OPEN else LIGHT_CLOSE) } getLeftView().tag = isOpen } private fun lightSwitchClick() { switchLight(true) addLeftClick { val tag = getLeftView().tag?.toString()?.toBoolean() ?: true switchLight(tag.not()) } } private fun getTokenSuccess(data: RespData<*>) { Log.d(TAG, "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 } 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() } private val serialReader by lazy { StringBuilder() } private fun receiveSerialPortData(srcData: String) { Log.d(TAG, "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.d(TAG, "receiveSerialPortData:serialReader: $serialReader") data = serialReader.toString().replace(",", "") } val cmd = data.substring(HEADER.length, HEADER.length + 4) when (cmd) { "0001" -> { } "0101" -> { //心跳-01 Log.d(TAG, "receiveSerialPortData:心跳01: $data") heartBeat(data) } // "8101" -> { // //心跳-81 // Log.d(TAG, "receiveSerialPortData:心跳08: $data") // heartBeat(data) // } // "0202" -> { // //开关锁反馈 // lockFeedback(data) // } "0302" -> { //获取设备状态 getDeviceInfo(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关 lifecycleScope.launch { SerialPortManager.send("${HEADER}0202${lockNo}${FOOTER}") } } val lockTwoState = data.substring(HEADER.length + 6, HEADER.length + 8) if (lockTwoState == "00") { //锁2关 lifecycleScope.launch { 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("1-1冷藏柜 [$temperatureHex] ${showTemperatureC}°C / ${percent}%") }.onFailure { it.printStackTrace() } } 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() } val realWeightDouble = realWeight / 1000.0 weightArray.put(index, realWeightDouble) if (index <= 10) { list.firstOrNull { it.deviceNo == index }?.let { it.weight = realWeightDouble val pos = list.indexOf(it) shelfAdapter.notifyItemChanged(pos) Log.d(TAG, "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) // } } private fun getDeviceInfo(data: String) { Log.d(TAG, "getDeviceInfo: data=$data") val weightStartIndex = HEADER.length + 14 val temperatureStartIndex = weightStartIndex + 12 * 8 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("1-1冷藏柜 ${showTemperatureC}°C / ${percent}%") 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 { setOnDebouncedItemClick { adapter, view, 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 // } 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.createTime.isNullOrBlank().not()) { createTime = model.createTime } } notifyItemChanged(position) try { val submitList = list.sortedBy { it.deviceNo } val body = ShelfBody().also { it.deviceId = App.deviceId ?: "" it.canteenId = App.canteenId it.temperature = showTemperatureC it.humidity = showHumidity it.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) } 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 = null createTime = null overdueDay = null } shelfAdapter.notifyItemChanged(list.indexOf(item)) } }