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() } }