采集向量数据接口调试

This commit is contained in:
2026-01-06 18:49:33 +08:00
parent fc04db9268
commit 378e6088e7
20 changed files with 468 additions and 241 deletions
@@ -4,10 +4,10 @@ import android.annotation.SuppressLint
import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.R
import com.sw.dualscreen.adapter.CollectedFoodNewAdapter
import com.sw.dualscreen.databinding.ActivityCollectedFoodBinding
import com.sw.dualscreen.databinding.LayoutEmptySearchBinding
import com.sw.dualscreen.dialog.WarnDialog
import com.sw.dualscreen.ext.addOnActionSearchListener
import com.sw.dualscreen.ext.gone
@@ -15,12 +15,10 @@ import com.sw.dualscreen.ext.hideKeyboard
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.objbox.CollectedFoodInfo
import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.ObjectBox
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.ToastUtils
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
@@ -42,7 +40,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
CollectedFoodNewAdapter(list).apply {
// isStateViewEnable = true
addOnItemChildClickListener(R.id.ivDeleteFood) { _, _, position ->
deleteGoods(position)
loadDeleteDialog(position)
}
}
}
@@ -182,17 +180,22 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
//}
}
private fun deleteGoods(position: Int) {
private fun loadDeleteDialog(position: Int) {
WarnDialog(
context = context,
content = "请确认是否删除菜品:${list[position].foodName}",
confirmBlock = {
deleteGoods(position)
}).show()
}
private fun deleteGoods(position: Int) {
val food = list[position]
showWaitingDialog("加载中……")
viewModel.deleteCollectFood(food.foodId, GlobalData.foodModelVersion) { deleteSuccess ->
if (deleteSuccess) {
Thread {
runOnUiThread {
showWaitingDialog("加载中……")
}
val name = list[position].foodName
val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
val filterIdList = box?.all?.filter { it.foodName == food.foodName }?.map { it.id }
box?.removeByIds(filterIdList)
runOnUiThread {
hideWaitingDialog()
@@ -203,7 +206,8 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
}
}
}.start()
}).show()
}
}
}
@@ -1,37 +1,86 @@
package com.sw.dualscreen.activity
import android.content.Intent
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.lifecycleScope
import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.databinding.ActivityInitBinding
import com.sw.dualscreen.utils.QRCodeUtil
import com.sw.dualscreen.model.response.FoodVector
import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.FoodModule
import com.sw.dualscreen.objbox.ObjectBox
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.DeviceViewModel
import com.sw.plate.utils.AppUtil
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import com.sw.dualscreen.viewmodel.UserViewModel
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
class InitActivity : BaseActivity<ActivityInitBinding>() {
private val viewModel by viewModels<DeviceViewModel>()
companion object {
private const val PAGE_SIZE = 100
}
private val userViewModel by viewModels<UserViewModel>()
private var pageNum = 1
override fun getViewModel(): BaseViewModel {
return viewModel
return userViewModel
}
override fun inflateViewBinding(): ActivityInitBinding {
return ActivityInitBinding.inflate(layoutInflater)
}
private fun getCollectedFoodVector(block:()-> Unit) {
userViewModel.getCollectedFoodVector(param = mutableMapOf(
"pageNum" to "$pageNum",
"pageSize" to "$PAGE_SIZE",
"version" to GlobalData.foodModelVersion
)) { items ->
val list = items ?: emptyList()
if (pageNum == 1 && list.isEmpty()) {
block()
return@getCollectedFoodVector
}
if (list.size >= PAGE_SIZE) {
saveFoodVector(list)
pageNum++
getCollectedFoodVector(block)
return@getCollectedFoodVector
}
if (list.isNotEmpty()) {
saveFoodVector(list)
block()
}
}
}
private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
private fun saveFoodVector(list: List<FoodVector>) {
val vectorList = list.map { vt ->
val foodVector = vt.foodVector?.removeSurrounding("[", "]")?.split(",")?.map { it.toFloatOrNull()?:0.0f }?.toFloatArray()
Food(
collectId = vt.id,
foodId = vt.foodId,
foodName = vt.foodName,
version = vt.version,
foodVector = foodVector
)
}
box.put(vectorList)
}
override fun initialize() {
super.initialize()
binding.root.postDelayed({
goMainActivity()
}, 1000)
showWaitingDialog("加载中……")
FoodModule.init(this) {
getCollectedFoodVector {
runOnUiThread {
hideWaitingDialog()
binding.root.postDelayed({
goMainActivity()
}, 1000)
}
}
}
// val isSuccess = viewModel.checkEquipmentInfo()
// if (isSuccess) {
// goMainActivity()
@@ -50,12 +99,12 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.deviceInfoResult.drop(1).collect {
if (it != true) return@collect
goMainActivity()
}
}
// lifecycleScope.launch {
// viewModel.deviceInfoResult.drop(1).collect {
// if (it != true) return@collect
// goMainActivity()
// }
// }
}
private fun goMainActivity() {
@@ -137,7 +137,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
super.initialize()
isPageVisible = true
addBackEventListener()
FoodModule.init(this)
//FoodModule.init(this)
initView()
viewModel.getUserFaceCache(pageNo = 1)
//setupSecondaryDisplay()
@@ -353,77 +353,37 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
LightManager.closeRedLight()
LightManager.openGreenLight()
showWaitingDialog("识别中,请稍后……")
takePhoto(successCallback = { photoUri ->
lastPhotoUri = photoUri
Timber.tag(TAG).d("main,takePhoto耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
Timber.d("registerDataChange photoUri = ${photoUri.path}")
// viewModel.getIdentifiedFoodList()
ImageUtil.uriToBitmap(this, photoUri)?.let { bitmap ->
Timber.tag(TAG).d("main,uriToBitmap耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
Timber.d("registerDataChange photoUri 拿到bitmap")
// val bmp = BitmapCropper.cropCenter(bitmap, 1300, 900)
Timber.d("registerDataChange photoUri bitmap裁剪完成")
val file = BitmapSaver.saveToAppFilesDir(
bitmap,
this,
"IMG_CROP_${System.currentTimeMillis()}.jpg"
)
Timber.tag(TAG)
.d("main,saveToAppFilesDir耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
Timber.d("registerDataChange photoUri bitmap保存文件路径:${file?.absolutePath}")
//val nameList = FoodModule.queryFood(bitmap)
val scoreList = FoodModule.getFoodScoreList(bitmap)
val recDataJson = GsonUtils.toJson(scoreList)
Timber.tag(TAG)
.d("main,getFoodScoreList耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
Timber.d("registerDataChange photoUri 拿到识别数据:$recDataJson")
val nameList = scoreList.map { it.name }
val foodName = nameList.joinToString(separator = ",")
hideWaitingDialog()
Timber.d("registerDataChange photoUri 识别数据名称:$foodName")
if (TextUtils.isEmpty(foodName)) {
LightManager.closeGreenLight()
LightManager.closeRedLight()
//binding.layoutRescan.visibility = View.VISIBLE
binding.flPay.visible()
binding.tvToSearch.let {
it.text = "未识别到,去手动搜索"
it.visible()
}
} else {
//binding.layoutRescan.visibility = View.GONE
showWaitingDialog("正在查询菜品信息,请稍后……")
viewModel.getFoodInfo(foodName) { list ->
runOnUiThread {
hideWaitingDialog()
Timber.tag(TAG)
.d("main,getFoodInfo耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
updateFoodInfo(list.toMutableList(), scoreList)
}
takePhoto(
successCallback = { photoUri ->
onTakePhotoSuccess(photoUri)
failCount = 0
}, failureCallback = {
runOnUiThread {
if (failCount >= 10) {
ToastUtils.showToast("相机异常,请稍后重试")
shutdownCamera()
setupCamera()
return@runOnUiThread
}
}
}
}, failureCallback = {
hideWaitingDialog()
failCount++
hideWaitingDialog()
// ToastUtils.showToast("拍照异常,请手动放置后重新识别")
shutdownCamera()
setupCamera()
shutdownCamera()
setupCamera()
binding.root.postDelayed({
recognizeFood()
}, 500)
})
binding.root.postDelayed({
recognizeFood()
}, 1000)
})
}
/**
* 识别相机异常次数
*/
private var failCount = 0
@SuppressLint("NotifyDataSetChanged")
private fun updateFoodInfo(list: MutableList<FoodInfo>, scoreList: List<IdNameScore>) {
val queryData = GsonUtils.toJson(list)
@@ -434,12 +394,19 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
val score = scoreItem?.score ?: 0.0
foodInfo.score = ((1 - score) * 10000).roundToInt()
}
val list2 = try {
val orderList = scoreList.map { it.name.trim() }
list.sortedBy { orderList.indexOf(it.foodName) }
} catch (e: Exception) {
e.printStackTrace()
list
}
searchFoodList.clear()
searchFoodList.addAll(list)
searchFoodList.addAll(list2)
adapter.notifyDataSetChanged()
//adapter.updateData(list)
if (list.isNotEmpty()) {
checkedItem = list[0]
if (list2.isNotEmpty()) {
checkedItem = list2[0]
checkedItem!!.photoUri = lastPhotoUri
checkedItem!!.isChecked = true
adapter.notifyItemChanged(0)
@@ -616,7 +583,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun onError(exception: ImageCaptureException) {
Timber.e("拍照失败: ${exception.message}")
failureCallback()
ToastUtils.showToast("拍照失败: ${exception.message}")
//ToastUtils.showToast("拍照失败: ${exception.message}")
// 恢复图像分析
isAnalyzing = true
}
@@ -1090,4 +1057,64 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
// }.start()
// }
private fun onTakePhotoSuccess(photoUri: Uri) {
lastPhotoUri = photoUri
Timber.tag(TAG).d("main,takePhoto耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
Timber.d("registerDataChange photoUri = ${photoUri.path}")
// viewModel.getIdentifiedFoodList()
ImageUtil.uriToBitmap(this, photoUri)?.let { bitmap ->
Timber.tag(TAG).d("main,uriToBitmap耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
Timber.d("registerDataChange photoUri 拿到bitmap")
// val bmp = BitmapCropper.cropCenter(bitmap, 1300, 900)
Timber.d("registerDataChange photoUri bitmap裁剪完成")
val file = BitmapSaver.saveToAppFilesDir(
bitmap,
this,
"IMG_CROP_${System.currentTimeMillis()}.jpg"
)
Timber.tag(TAG)
.d("main,saveToAppFilesDir耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
Timber.d("registerDataChange photoUri bitmap保存文件路径:${file?.absolutePath}")
//val nameList = FoodModule.queryFood(bitmap)
val scoreList = FoodModule.getFoodScoreList(bitmap)
val recDataJson = GsonUtils.toJson(scoreList)
Timber.tag(TAG)
.d("main,getFoodScoreList耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
Timber.d("registerDataChange photoUri 拿到识别数据:$recDataJson")
val nameList = scoreList.map { it.name }
val foodName = nameList.joinToString(separator = ",")
hideWaitingDialog()
Timber.d("registerDataChange photoUri 识别数据名称:$foodName")
if (TextUtils.isEmpty(foodName)) {
LightManager.closeGreenLight()
LightManager.closeRedLight()
//binding.layoutRescan.visibility = View.VISIBLE
binding.flPay.visible()
binding.tvToSearch.let {
it.text = "未识别到,去手动搜索"
it.visible()
}
} else {
//binding.layoutRescan.visibility = View.GONE
showWaitingDialog("正在查询菜品信息,请稍后……")
viewModel.getFoodInfo(foodName) { list ->
runOnUiThread {
hideWaitingDialog()
Timber.tag(TAG)
.d("main,getFoodInfo耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
updateFoodInfo(list.toMutableList(), scoreList)
}
}
}
}
}
}
@@ -8,11 +8,11 @@ import android.view.LayoutInflater
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.camera.view.PreviewView
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.CollectedFoodActivity
import com.sw.dualscreen.activity.SettingActivity
@@ -51,7 +51,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
private var selectedFoodId: String? = ""
private var selectedFoodName: String? = ""
private var box: Box<Food>? = null
private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
private val foodCollectionList = mutableListOf<FoodCollectionBean>().apply {
repeat(MAX_COUNT) {
add(FoodCollectionBean(isShowCamera = true))
@@ -126,7 +126,6 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
// targetWidth = 1300, targetHeight = 900,
// //offsetX = 30, offsetY = 100
// )
initBox()
val imageVector = try {
FoodModule.bitmap2FloatArray(bitmap)
} catch (e: Exception) {
@@ -236,9 +235,12 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
it.layoutManager = GridLayoutManager(context, 2)
it.adapter = searchFoodAdapter
}
searchInfo()
}
@SuppressLint("NotifyDataSetChanged")
private fun upload() {
lifecycleScope.launch {
val totalFileCount = foodCollectionList.count { it.imageFile != null }
@@ -247,31 +249,53 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
//params["placeId"] = restId.toRequestBody()
params["foodId"] = checkedItem!!.foodId.toRequestBody()
params["foodName"] = checkedItem!!.foodName!!.toRequestBody()
//params["foodVector"] = foodVector.toRequestBody()
params["version"] = GlobalData.foodModelVersion.toRequestBody()
ImageUploader(
totalList = foodCollectionList,
uploadImage = { batch ->
val files = batch.map { it.imageFile }
(requireActivity() as SettingActivity).viewModel.uploadCollectFoodPics(
files,
params
)
val foodVectorList =
batch.filter { it.imageVector != null }.map { it.imageVector!!.joinToString(separator = ",", prefix = "[", postfix = "]")}
val foodVectorJson =
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
Timber.tag(TAG).d("json=$foodVectorJson")
params["foodVector"] = foodVectorJson.toRequestBody()
settingActivity?.viewModel?.uploadCollectFoodPics(files, params)
},
onProgress = { count, batch ->
onProgress = { count, batch, idList->
activity?.runOnUiThread {
settingActivity?.showWaitingDialog2("图片上传中$count/$totalFileCount")
batch.forEach {
it.uploadSuccess = true
}
//batch.forEach {
// it.uploadSuccess = true
//}
Thread {
val foodList = batch.mapIndexed { index, it ->
Food(
collectId = if (index < idList.size) idList[index] else null,
foodId = checkedItem!!.foodId,
foodName = checkedItem!!.foodName,
foodVector = it.imageVector,
version = GlobalData.foodModelVersion
)
}
box.put(foodList)
activity?.runOnUiThread {
batch.forEach { it.uploadSuccess = true }
collectionAdapter.notifyDataSetChanged()
}
}.start()
}
},
onError = {
settingActivity?.hideWaitingDialog()
activity?.runOnUiThread {
ToastUtils.showToast("上传失败,请稍后重试")
}
},
onComplete = {
vectorThread()
//vectorThread()
settingActivity?.hideWaitingDialog()
}
).processUploads()
}
@@ -316,33 +340,34 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
}.start()
}
private fun initBox() {
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
}
private fun image2VectorTask(imageVector: FloatArray, position: Int) {
initBox()
// val imageVector = FoodModule.bitmap2FloatArray(item.bitmap!!)
//
//// val base64Str = FloatBase64Utils.floatArrayToBase64(imageVector)
//// Timber.tag("mzf1").e(base64Str)
////// viewModel.postImageData(
////// context,
////// foodId = selectedFoodId.toString(),
////// foodName = selectedFoodName.toString(),
////// foodVector = base64Str,
////// uri = item.imageUri!!
////// )
box?.put(Food(name = checkedItem!!.foodName, foodIdx = 0, foodVector = imageVector))
foodCollectionList[position].let {
// it.imageVector = imageVector
it.isFinish = true
}
activity?.runOnUiThread {
collectionAdapter.notifyItemChanged(position)
}
private fun image2VectorTask(imageVector: FloatArray?, position: Int) {
if (imageVector == null) return
//// val imageVector = FoodModule.bitmap2FloatArray(item.bitmap!!)
////
////// val base64Str = FloatBase64Utils.floatArrayToBase64(imageVector)
////// Timber.tag("mzf1").e(base64Str)
//////// viewModel.postImageData(
//////// context,
//////// foodId = selectedFoodId.toString(),
//////// foodName = selectedFoodName.toString(),
//////// foodVector = base64Str,
//////// uri = item.imageUri!!
//////// )
// box.put(
// Food(
// collectId = null,
// foodId = checkedItem!!.foodId,
// foodName = checkedItem!!.foodName,
// foodVector = imageVector,
// version = "1.0.0"
// )
// )
// if (position > -1) {
// foodCollectionList[position].isFinish = true
// activity?.runOnUiThread {
// collectionAdapter.notifyItemChanged(position)
// }
// }
}