Newer
Older
Detector / app / src / main / assets / 演示版代码备份.kt
import android.content.res.ColorStateList
import android.graphics.Color
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.media.AudioAttributes
import android.media.SoundPool
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import androidx.core.graphics.toColorInt
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.amap.api.location.AMapLocation
import com.amap.api.maps.AMapUtils
import com.amap.api.maps.model.LatLng
import com.casic.common.detector.gd.R
import com.casic.common.detector.gd.base.BaseApplication
import com.casic.common.detector.gd.base.SerialPortBaseActivity
import com.casic.common.detector.gd.callback.OnGetLocationListener
import com.casic.common.detector.gd.databinding.ActivitySearchMarkerBinding
import com.casic.common.detector.gd.model.MarkerDistanceData
import com.casic.common.detector.gd.utils.CurrentSegment
import com.casic.common.detector.gd.utils.LocaleConstant
import com.casic.common.detector.gd.utils.LocationKit
import com.casic.common.detector.gd.utils.RuntimeCache
import com.casic.common.detector.gd.utils.SerialPortCommand
import com.casic.common.detector.gd.vm.TaskViewModel
import com.casic.common.detector.gd.widgets.MarkerDetailDialog
import com.casic.common.detector.gd.widgets.NewMarkerDetailDialog
import com.casic.common.detector.gd.widgets.RadarScanView
import com.pengxh.kt.lite.extensions.show
import com.pengxh.kt.lite.utils.LiteKitConstant
import com.pengxh.kt.lite.utils.LoadingDialog
import com.pengxh.kt.lite.utils.SaveKeyValues
import com.pengxh.kt.lite.widget.dialog.AlertMessageDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.atan2

class SearchMarkerActivity : SerialPortBaseActivity<ActivitySearchMarkerBinding>(),
    SensorEventListener {

    private val kTag = "SearchMarkerActivity"
    private val context = this
    private val locationKit by lazy { LocationKit(this) }
    private val markerPoints by lazy { ArrayList<MarkerDistanceData>() }
    private val markerBeanDao by lazy { BaseApplication.get().dataBase.markerBeanDao() }
    private val taskMarkerBeanDao by lazy { BaseApplication.get().dataBase.taskMarkerBeanDao() }
    private val taskViewModel by lazy { ViewModelProvider(this)[TaskViewModel::class.java] }
    private val sensorManager by lazy { getSystemService(SENSOR_SERVICE) as SensorManager }
    private val attr = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build()
    private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build()
    private val rotationMatrix = FloatArray(9)//旋转矩阵缓存
    private val valueArray = FloatArray(3)//方位角数值
    private var slowSoundResourceId = 0
    private var fastSoundResourceId = 0
    private var databaseMarkerId = ""//本地数据库标识器ID
    private var detectedMarkerId = ""//真实探测到标识器ID
    private var isExecuteTask = false
    private var gravity: FloatArray? = null
    private var geomagnetic: FloatArray? = null

    override fun initOnCreate(savedInstanceState: Bundle?) {
        RuntimeCache.currentSegment = CurrentSegment.SearchMarker
        val taskState = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String
        isExecuteTask = taskState != "0"

        slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1)
        fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1)

        //点位渲染,每次定位都计算当前位置与符合条件的点距离
        locationKit.getCurrentLocation(false, object : OnGetLocationListener {
            override fun onSuccess(location: AMapLocation) {
                renderDataPoint(location)
            }

            override fun onError() {
                "当前位置信号差,无法获取定位".show(context)
            }
        })

        if (isExecuteTask) {
            binding.taskStateView.visibility = View.GONE
            binding.taskStateView.isSelected = false
        } else {
            binding.taskStateView.visibility = View.VISIBLE
            binding.taskStateView.isSelected = true
        }

        //进入界面开始搜索信号强度
        sendSerialPortCommand(SerialPortCommand.SEARCH_SIGNAL)
    }

    private val handler = Handler(Looper.getMainLooper())
    private var detectDepthRunnable: DetectDepthRunnable? = null

    private inner class DetectDepthRunnable(private val command: Int) : Runnable {
        override fun run() {
            sendSerialPortCommand(command)
            //延迟三秒再发一次
            handler.postDelayed(this, 3000)
        }
    }

    override fun initEvent() {
        binding.depthButton.setOnClickListener {
            if (detectedMarkerId == "") {
                //还没探测到,直接显示当初埋深
                val result = markerBeanDao.queryMarkerById(databaseMarkerId)
                val depth = if (result.isNotEmpty()) {
                    "110厘米"
                } else {
                    result.first().markerDepth
                }
                AlertMessageDialog.Builder()
                    .setContext(this)
                    .setTitle("温馨提示")
                    .setMessage("标识器埋深:$depth")
                    .setPositiveButton("知道了")
                    .setOnDialogButtonClickListener(object :
                        AlertMessageDialog.OnDialogButtonClickListener {
                        override fun onConfirmClick() {
                            RuntimeCache.currentSegment = CurrentSegment.SearchMarker
                            sendSerialPortCommand(SerialPortCommand.SEARCH_SIGNAL)
                        }
                    }).build().show()
                return@setOnClickListener
            }

            //真实探测埋深
            RuntimeCache.currentSegment = CurrentSegment.DetectDepth
            val result = markerBeanDao.queryMarkerById(detectedMarkerId)
            if (result.isNotEmpty()) {
                val command = when (result.first().markerType) {
                    "EM30" -> SerialPortCommand.DETECT_EM30_DEPTH
                    "EM50" -> SerialPortCommand.DETECT_EM50_DEPTH
                    "EM14" -> SerialPortCommand.DETECT_EM14_DEPTH
                    else -> SerialPortCommand.STOP_SERIAL_PORT_DATA
                }
                if (command == SerialPortCommand.STOP_SERIAL_PORT_DATA) {
                    "此标识器无法读取埋深!".show(this)
                } else {
                    soundPool.autoPause()
                    LoadingDialog.show(this, "正在探测标识器埋深,请稍后...")

                    // 移除之前的 Runnable(如果存在)
                    detectDepthRunnable?.let { handler.removeCallbacks(it) }
                    detectDepthRunnable = DetectDepthRunnable(command)
                    // 发送读取标识器埋设深度指令
                    handler.post(detectDepthRunnable!!)
                }
            } else {
                "标识器未安装,安装成功后才可读取埋深!".show(this)
            }
        }

        binding.markerIdButton.setOnClickListener {
            sendSerialPortCommand(SerialPortCommand.SEARCH_MARKER_ALWAYS)
        }

        binding.markerInfoButton.setOnClickListener {
            val markerId = if (detectedMarkerId == "") {
                databaseMarkerId
            } else {
                detectedMarkerId
            }
            val result = markerBeanDao.queryMarkerById(markerId)
            if (result.isNotEmpty()) {
                MarkerDetailDialog(this).setMarker(result.first()).show()
            } else {
                NewMarkerDetailDialog(this, markerId)
            }
            //查询完之后重置ID,以便下次重走逻辑
            databaseMarkerId = ""
            detectedMarkerId = ""
        }
    }

    override fun onMarkerIdDetected(markerId: String) {
        if (RuntimeCache.currentSegment == CurrentSegment.SearchMarker) {
            binding.depthButton.isEnabled = true
            binding.depthButton.setTextColor(Color.WHITE)
            binding.depthButton.backgroundTintList =
                ColorStateList.valueOf("#004364".toColorInt())

            binding.markerInfoButton.isEnabled = true
            binding.markerInfoButton.setTextColor(Color.WHITE)
            binding.markerInfoButton.backgroundTintList =
                ColorStateList.valueOf("#FFA200".toColorInt())

            handleMarker(markerId)
        }
    }

    private fun handleMarker(id: String) {
        detectedMarkerId = id
        //自动上传标识器
        if (isExecuteTask) {
            val taskId = SaveKeyValues.getValue(LocaleConstant.TASK_ID, "") as String
            val taskCode = SaveKeyValues.getValue(LocaleConstant.TASK_CODE, "") as String
            val marker = taskMarkerBeanDao.queryTaskMarkerById(
                taskId, taskCode, id, "0"
            )
            if (marker == null) {
                return
            }
            taskViewModel.uploadMarker(
                marker.markerId,
                onLoading = {},
                onSuccess = {
                    "标识器${marker.markerId}已探测!".show(this)
                    marker.isDetected = "1"
                    taskMarkerBeanDao.updateLocalTaskMarkerState(marker)
                },
                onFailed = {}
            )
        }
    }

    override fun onMarkerDepthDetected(depth: Int) {
        if (RuntimeCache.currentSegment == CurrentSegment.DetectDepth) {
            detectDepthRunnable?.let { handler.removeCallbacks(it) }
            detectDepthRunnable = null
            LoadingDialog.dismiss()
            if (!this.isDestroyed) {
                AlertMessageDialog.Builder()
                    .setContext(context)
                    .setTitle("温馨提示")
                    .setMessage("标识器埋深:${depth}厘米")
                    .setPositiveButton("知道了")
                    .setOnDialogButtonClickListener(object :
                        AlertMessageDialog.OnDialogButtonClickListener {
                        override fun onConfirmClick() {
                            RuntimeCache.currentSegment = CurrentSegment.SearchMarker
                            sendSerialPortCommand(SerialPortCommand.SEARCH_SIGNAL)
                        }
                    }).build().show()
            } else {
                "标识器埋深:${depth}厘米".show(this)
            }
        }
    }

    override fun onMarkerSignalDetected(signalEnergy: Int) {
        if (RuntimeCache.currentSegment == CurrentSegment.SearchMarker) {
            if (signalEnergy > 1000) {
                binding.markerIdButton.isEnabled = true
                binding.markerIdButton.setTextColor("#428d00".toColorInt())
                binding.markerIdButton.backgroundTintList = ColorStateList.valueOf(
                    "#7EFF00".toColorInt()
                )

                binding.searchResultView.text = "已检测到标识器"
                binding.searchResultView.setTextColor("#428d00".toColorInt())
                binding.searchResultView.setBackgroundResource(R.mipmap.bg_small_text_green)
            }

            //声音
            val soundResourceId = if (signalEnergy >= 4000)
                fastSoundResourceId else slowSoundResourceId
            soundPool.play(soundResourceId, 1f, 1f, 0, 0, 1f)

            //信号强度显示
            binding.energyPgBar.progress = signalEnergy
            binding.energyValueView.text = "${signalEnergy}dB"

            //搜索信号文字显示
            updateTextResult(signalEnergy)
        }
    }

    private fun updateTextResult(signalEnergy: Int) {
        when {
            signalEnergy <= 700 -> {
                setEnergyTip(
                    "信号较弱,可能距离较远",
                    "#8D1717".toColorInt(),
                    R.mipmap.bg_large_text_red
                )
                binding.searchResultView.text = "未检测到标识器"
                binding.searchResultView.setTextColor("#8D1717".toColorInt())
                binding.searchResultView.setBackgroundResource(R.mipmap.bg_small_text_red)
            }

            signalEnergy >= 4100 -> {
                setEnergyTip(
                    "信号极强,接近标识器正上方",
                    "#428d00".toColorInt(),
                    R.mipmap.bg_large_text_green
                )
            }

            else -> {
                setEnergyTip(
                    "已靠近,请继续移动位置",
                    "#8C5700".toColorInt(),
                    R.mipmap.bg_large_text_yellow
                )
            }
        }
    }

    private fun setEnergyTip(text: String, textColorRes: Int, backgroundRes: Int) {
        binding.energyTipsView.text = text
        binding.energyTipsView.setTextColor(textColorRes)
        binding.energyTipsView.setBackgroundResource(backgroundRes)
    }

    /**
     * 计算并渲染数据点。点太多采用协程计算,不然会有点卡顿。默认2s计算一次
     * @param location 定位点(RTK获取)
     * */
    private fun renderDataPoint(location: AMapLocation) {
        val longitude = location.longitude
        val latitude = location.latitude
        lifecycleScope.launch(Dispatchers.IO) {
            val dataPoints = ArrayList<RadarScanView.DataPoint>()
            markerBeanDao.loadAll().forEach {
                val distance = AMapUtils.calculateLineDistance(
                    LatLng(it.lat.toDouble(), it.lng.toDouble()), LatLng(latitude, longitude)
                )
                val formatDistance = "%.2f".format(distance).toFloat()
                markerPoints.add(MarkerDistanceData(it.markerId, formatDistance))

                if (formatDistance <= LocaleConstant.MAX_DISTANCE) {
                    val angle = atan2(
                        (it.lat.toDouble() - latitude), (it.lng.toDouble() - longitude)
                    ) + Math.PI
                    val formatAngle = "%.2f".format(angle).toDouble()

                    dataPoints.add(
                        RadarScanView.DataPoint(it.markerId, formatAngle, formatDistance)
                    )
                }
            }
            withContext(Dispatchers.Main) {
                binding.radarScanView.renderPointData(
                    dataPoints,
                    onGetNearestPoint = { point ->
                        if (point == null) {
                            binding.distanceValueView.text = "大于5.5m"
                            binding.distancePgBar.progress = 100
                        } else {
                            databaseMarkerId = point.markerId

                            binding.depthButton.isEnabled = true
                            binding.depthButton.setTextColor(Color.WHITE)
                            binding.depthButton.backgroundTintList =
                                ColorStateList.valueOf("#004364".toColorInt())

                            binding.markerInfoButton.isEnabled = true
                            binding.markerInfoButton.setTextColor(Color.WHITE)
                            binding.markerInfoButton.backgroundTintList =
                                ColorStateList.valueOf("#FFA200".toColorInt())

                            binding.distanceValueView.text = "${point.distance}m"
                            val progress = if (point.distance > LocaleConstant.MAX_DISTANCE) {
                                100
                            } else {
                                (point.distance / LocaleConstant.MAX_DISTANCE) * 100
                            }
                            binding.distancePgBar.progress = progress.toInt()
                        }
                    }
                )
            }
        }
    }

    override fun initViewBinding(): ActivitySearchMarkerBinding {
        return ActivitySearchMarkerBinding.inflate(layoutInflater)
    }

    override fun setupTopBarLayout() {

    }

    override fun onResume() {
        super.onResume()
        //注册加速度传感器监听
        val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL)

        //注册磁场传感器监听
        val magnetic = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
        sensorManager.registerListener(this, magnetic, SensorManager.SENSOR_DELAY_NORMAL)
    }

    override fun onPause() {
        super.onPause()
        sensorManager.unregisterListener(this)
    }

    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
        //精度发生变化时触发
    }

    override fun onSensorChanged(event: SensorEvent?) {
        //值发生变化时触发
        val type = event?.sensor?.type

        if (type == Sensor.TYPE_ACCELEROMETER) {
            gravity = event.values
        } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
            geomagnetic = event.values
        }

        if (gravity == null || geomagnetic == null) {
            return
        }

        if (SensorManager.getRotationMatrix(rotationMatrix, null, gravity, geomagnetic)) {
            SensorManager.getOrientation(rotationMatrix, valueArray)

            val degree = ((360f + valueArray[0] * 180f / Math.PI) % 360).toInt()
            //更新罗盘角度
            binding.radarScanView.setDegreeValue(degree)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        soundPool.autoPause()
        locationKit.stopLocation()
    }
}