diff --git a/app/src/main/java/com/casic/detector/view/MainActivity.kt b/app/src/main/java/com/casic/detector/view/MainActivity.kt index 7c6446a..0ce360a 100644 --- a/app/src/main/java/com/casic/detector/view/MainActivity.kt +++ b/app/src/main/java/com/casic/detector/view/MainActivity.kt @@ -1,5 +1,9 @@ package com.casic.detector.view +import android.annotation.SuppressLint +import android.app.DatePickerDialog +import android.app.Dialog +import android.content.DialogInterface import android.content.Intent import android.graphics.BitmapFactory import android.graphics.Color @@ -8,10 +12,13 @@ import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle +import android.os.CountDownTimer import android.provider.Settings import android.util.Log import android.view.KeyEvent import android.view.View +import android.view.animation.Animation +import android.view.animation.RotateAnimation import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation @@ -25,19 +32,29 @@ import com.amap.api.maps.model.MarkerOptions import com.amap.api.maps.model.MyLocationStyle import com.casic.detector.R +import com.casic.detector.adapter.EditableImageAdapter import com.casic.detector.bean.LabelBean import com.casic.detector.bean.TaskBean import com.casic.detector.callback.ILocationListener +import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.cluster.ClusterItem import com.casic.detector.cluster.ClusterOverlay import com.casic.detector.cluster.RegionItem import com.casic.detector.databinding.ActivityMainBinding +import com.casic.detector.databinding.DialogInstallMarkerBinding +import com.casic.detector.databinding.DialogSearchMarkerBinding import com.casic.detector.extensions.appendDownloadUrl +import com.casic.detector.extensions.compressImage import com.casic.detector.extensions.drawCircle +import com.casic.detector.extensions.getDefaultValue import com.casic.detector.extensions.hexToString import com.casic.detector.extensions.initImmersionBar import com.casic.detector.extensions.isNumber +import com.casic.detector.extensions.setDefaultValue +import com.casic.detector.extensions.show +import com.casic.detector.extensions.toColor import com.casic.detector.extensions.toHex +import com.casic.detector.extensions.toObjectType import com.casic.detector.model.TaskModel import com.casic.detector.utils.DataBaseManager import com.casic.detector.utils.ExcelHub @@ -53,13 +70,24 @@ import com.casic.detector.widgets.SamplePopupWindow import com.google.gson.Gson import com.google.gson.reflect.TypeToken +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.appendZero +import com.pengxh.kt.lite.extensions.binding +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.convertDrawable import com.pengxh.kt.lite.extensions.createDownloadFileDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.initDialogLayoutParams import com.pengxh.kt.lite.extensions.isNetworkConnected import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.extensions.timestampToTime import com.pengxh.kt.lite.extensions.toJson import com.pengxh.kt.lite.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState @@ -73,6 +101,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File +import java.text.DecimalFormat +import java.util.Calendar +import java.util.Date class MainActivity : KotlinBaseActivity(), @@ -87,18 +118,23 @@ private val locationHub by lazy { LocationHub(this) } private val regionRadius by lazy { LocaleConstant.RADIUS_SIZE.dp2px(this) } private val backDrawables by lazy { HashMap() } - private val audioAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_ALARM) - .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) - .build() + private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes( + AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build() + ).build() + private var readMarkerSoundResourceId = 0 + private var slowSoundResourceId = 0 + private var fastSoundResourceId = 0 private var clickTime: Long = 0 private var labelBeans = ArrayList() private var clusterOverlay: ClusterOverlay? = null private var latitude: Double = 0.0 private var longitude: Double = 0.0 private var ids = ArrayList() + private lateinit var markerId: String private lateinit var aMap: AMap - private lateinit var soundPool: SoundPool private lateinit var taskViewModel: TaskViewModel private lateinit var taskBean: TaskBean private lateinit var taskId: String @@ -114,6 +150,10 @@ override fun initOnCreate(savedInstanceState: Bundle?) { initMapConfig(savedInstanceState) + readMarkerSoundResourceId = soundPool.load(this, R.raw.ring3, 1) + slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1) + fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1) + //调高串口电位 gpioManager.setGpioHigh("18") @@ -211,7 +251,11 @@ //安装。上传,然后存入本地库 binding.installButton.setOnClickListener { - navigatePageTo() + /** + * 改为Dialog方式,避免频繁打开/关闭串口 + * */ +// navigatePageTo() + installDialog.show() } //查看 @@ -318,7 +362,11 @@ //探测 binding.detectionButton.setOnClickListener { - navigatePageTo() + /** + * 改为Dialog方式,避免频繁打开/关闭串口 + * */ +// navigatePageTo() + searchDialog.show() } //自由巡检 @@ -331,8 +379,8 @@ .setOnDialogButtonClickListener(object : AlertMessageDialog.OnDialogButtonClickListener { override fun onConfirmClick() { - soundPool.release() - serialPortManager.closeSerialPort() + isFreeTask = false + soundPool.stop(readMarkerSoundResourceId) taskViewModel.uploadTaskMarker(taskId, ids) binding.stopFreeTaskButton.visibility = View.GONE @@ -342,6 +390,13 @@ } } + private val installDialog by lazy { InstallMarkerDialog(this) } + private val searchDialog by lazy { SearchMarkerDialog(this) } + private var isFreeTask = false + private var taskLabel: TaskBean? = null + private var isPlaying = false + private val decimal by lazy { DecimalFormat("0.0") } + private fun startFreeTask() { AlertInputDialog.Builder() .setContext(this) @@ -354,15 +409,9 @@ val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(userId, value) - soundPool = SoundPool.Builder() - .setMaxStreams(16) - .setAudioAttributes(audioAttributes) - .build() - soundPool.load(context, R.raw.ring3, 1) - soundPool.setOnLoadCompleteListener { soundPool, sampleId, _ -> - soundPool.play(sampleId, 1f, 1f, 0, -1, 1f) - } + soundPool.play(readMarkerSoundResourceId, 1f, 1f, 0, -1, 1f) + isFreeTask = true //自由巡检 serialPortManager.searchMarkerSignal() @@ -375,8 +424,142 @@ override fun onDataReceived(buffer: ByteArray) { val hex = buffer.toHex() -// Log.d(kTag, "$kTag => $hex") - if (hex.length >= 20) { + Log.d(kTag, "$kTag => $hex") +// if (hex.length >= 20) { +// val markerId = hex.take(20).hexToString() +// if (markerId.isNumber()) { +// "标识器${markerId}已探测".show(this) +// ids.add(markerId) +// } +// } + if (searchDialog.isDetectMarker) { + if (hex.startsWith("4E")) { + try { + //4E转为String为N,代表能量值 + //用能量值转动表盘 + val energyResponse = hex.take(10).hexToString() + val energy = energyResponse.substring(1).toInt() + if (energy >= 4000) { + isPlaying = if (!isPlaying) { + soundPool.play(fastSoundResourceId, 1f, 1f, 0, 0, 1f) + true + } else { + soundPool.stop(fastSoundResourceId) + false + } + } else { + isPlaying = if (!isPlaying) { + soundPool.play(slowSoundResourceId, 1f, 1f, 0, 0, 1f) + true + } else { + soundPool.stop(slowSoundResourceId) + false + } + } + + searchDialog.binding.resultTextView.text = "信号能量值:${energyResponse}" + + //转换为转动的角度 + val degree = (energy.toFloat() / (50 * 100)) * 180 + searchDialog.binding.energyValueView.text = decimal.format(degree) + + searchDialog.degreeCache["lastDegree"]?.apply { + val animation = RotateAnimation( + this, degree, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f + ) + animation.duration = 0 + animation.fillAfter = true + searchDialog.binding.needleView.startAnimation(animation) + } + + //保存旋转后的角度 + searchDialog.degreeCache["lastDegree"] = degree + + if (energy <= 700) {//18° + searchDialog.binding.energyTipsView.text = "标识器信号较弱,可能距离较远" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#8D1717")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_red) + + searchDialog.binding.depthButton.isEnabled = false + searchDialog.binding.depthButton.setTextColor(Color.parseColor("#CCCCCC")) + searchDialog.binding.depthButton.setBackgroundResource(R.mipmap.left_button_disable) + searchDialog.binding.markerInfoButton.isEnabled = false + searchDialog.binding.markerInfoButton.setTextColor(Color.parseColor("#CCCCCC")) + searchDialog.binding.markerInfoButton.setBackgroundResource(R.mipmap.right_button_disable) + + searchDialog.binding.searchResultView.text = "未检测到标识器" + searchDialog.binding.searchResultView.setTextColor(Color.parseColor("#8D1717")) + searchDialog.binding.searchResultView.setBackgroundResource(R.mipmap.bg_small_text_red) + } else if (energy >= 4100) {//148° + searchDialog.binding.energyTipsView.text = "标识器信号极强,可能在正下方" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#428d00")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_green) + } else {//[18°,148°] + searchDialog.binding.energyTipsView.text = "已靠近标识器,请继续移动位置" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#8C5700")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_yellow) + } + } catch (e: NumberFormatException) { + e.printStackTrace() + } + } else if (hex.startsWith("53")) { + LoadingDialogHub.dismiss() + try { + val depthResponse = hex.take(10).hexToString() + val depth = depthResponse.drop(4) + AlertMessageDialog.Builder() + .setContext(this) + .setTitle("温馨提示") + .setMessage("标识器埋深:${depth}厘米") + .setPositiveButton("知道了") + .setOnDialogButtonClickListener(object : + AlertMessageDialog.OnDialogButtonClickListener { + override fun onConfirmClick() { + + } + }).build().show() + } catch (e: NumberFormatException) { + e.printStackTrace() + } + } else { + val id = hex.take(20).hexToString() + if (id.isNumber()) { + markerId = id + + searchDialog.binding.depthButton.isEnabled = true + searchDialog.binding.depthButton.setTextColor(Color.WHITE) + searchDialog.binding.depthButton.setBackgroundResource(R.mipmap.left_button_enable) + searchDialog.binding.markerInfoButton.isEnabled = true + searchDialog.binding.markerInfoButton.setTextColor(Color.WHITE) + searchDialog.binding.markerInfoButton.setBackgroundResource(R.mipmap.right_button_enable) + + searchDialog.binding.searchResultView.text = "已检测到标识器" + searchDialog.binding.searchResultView.setTextColor(Color.parseColor("#428d00")) + searchDialog.binding.searchResultView.setBackgroundResource(R.mipmap.bg_small_text_green) + + //自动上传标识器 + taskLabel = DataBaseManager.get.queryTaskLabelByIdAndState( + markerId, "未开始" + ) + taskLabel?.apply { + /** + * 此taskCode是 [com.casic.detector.model.TaskModel.MessageModel.TaskDetailInfosModel] 里面的主键,也就是这个标签在数据库里面的主键 + * */ + taskViewModel.uploadMarker(taskCode) + } + } + } + } else if (installDialog.isReadMarker) { + val markerId = hex.take(20).hexToString() + if (markerId.isNumber()) { + LoadingDialogHub.dismiss() + soundPool.stop(readMarkerSoundResourceId) + installDialog.isReadMarker = false + installDialog.binding.identifierInclude.identifierIdView.text = markerId + } + } else if (isFreeTask) { val markerId = hex.take(20).hexToString() if (markerId.isNumber()) { "标识器${markerId}已探测".show(this) @@ -388,8 +571,21 @@ override fun observeRequestState() { taskViewModel.loadState.observe(this) { when (it) { - LoadState.Loading -> LoadingDialogHub.show(this, "提交工单中,请稍后") - else -> LoadingDialogHub.dismiss() + LoadState.Loading -> { + if (installDialog.isInstallMarker) { + LoadingDialogHub.show(this, "标识器安装中,请稍后...") + } else { + LoadingDialogHub.show(this, "提交工单中,请稍后") + } + } + + else -> { + if (installDialog.isInstallMarker) { + installDialog.dismiss() + installDialog.isInstallMarker = false + } + LoadingDialogHub.dismiss() + } } detectRedrawGraphic(taskBean.taskId) } @@ -774,10 +970,423 @@ } override fun onDestroy() { - super.onDestroy() - binding.mapView.onDestroy() locationHub.stopLocation() + serialPortManager.closeSerialPort() //降低串口电位 gpioManager.setGpioLow("18") + super.onDestroy() + binding.mapView.onDestroy() + } + + /**安装标识器对话框******************************************************************************/ + @SuppressLint("SetTextI18n") + class InstallMarkerDialog(private val activity: MainActivity) : Dialog(activity) { + + val binding: DialogInstallMarkerBinding by binding() + var isReadMarker = false + var isInstallMarker = false + + private val calendar by lazy { Calendar.getInstance() } + private val realPaths = ArrayList() //真实图片路径 + private lateinit var imageAdapter: EditableImageAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.initDialogLayoutParams(1f) + setCancelable(false) + setCanceledOnTouchOutside(false) + + //初始化数据 + initDefaultData() + + //返回 + binding.titleInclude.leftBackView.setOnClickListener { + activity.soundPool.stop(activity.readMarkerSoundResourceId) + dismiss() + } + + //安装 + binding.installButton.setOnClickListener { + val companyId = SaveKeyValues.getValue(LocaleConstant.USER_COMPANY_ID, "") as String + val objectId = SaveKeyValues.getValue(LocaleConstant.OBJECT_ID, "") as String + if (binding.objectInclude.markerObjectTypeView.text.isNullOrBlank()) { + when (binding.objectInclude.objectTypeSpinner.selectedItem.toString()) { + "管线" -> { + "请输入管线种类!".show(activity) + } + + "管线附属物" -> { + "请输入附属物名称!".show(activity) + } + + "管线特征管点" -> { + "请输入特征管点!".show(activity) + } + + "交叉穿越点" -> { + "请输入上层管种类!".show(activity) + } + } + return@setOnClickListener + } + + if (binding.objectInclude.pipelineDiameterView.text.isNullOrBlank()) { + "请输入管径".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.buryDeepView.text.isNullOrBlank()) { + "请输入埋深".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.downPipeDiameterView.text.isNullOrBlank()) { + "请输入下层管管径".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.areaView.text.isNullOrBlank()) { + "请输入所属区域".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.lineView.text.isNullOrBlank()) { + "请输入所属线路".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.roadView.text.isNullOrBlank()) { + "请输入所属道路".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.constructDateView.text.isNullOrBlank()) { + "请选择建设年代".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.ownerView.text.isNullOrBlank()) { + "请输入权属单位".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.identifierIdView.text.isNullOrBlank()) { + "请先读取标识器获取ID".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.identifierDeepView.text.isNullOrBlank()) { + "请输入标识器埋深".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.personDeptView.text.isNullOrBlank()) { + "请输入标识器安装部门".show(activity) + return@setOnClickListener + } + + //先存本地再上传服务器 + saveLabelInLocal() + + isInstallMarker = true + activity.taskViewModel.installLabel( + companyId, + binding.objectInclude.objectTypeSpinner.selectedItem.toString().toObjectType(), + binding.objectInclude.markerObjectTypeView.text.toString(), + binding.objectInclude.materialSpinner.selectedItem.toString(), + "${binding.objectInclude.pipelineDiameterView.text}mm", + "${binding.objectInclude.buryDeepView.text}mm", + binding.objectInclude.downPipeTypeSpinner.selectedItem.toString(), + binding.objectInclude.downPipeMaterialSpinner.selectedItem.toString(), + "${binding.objectInclude.downPipeDiameterView.text}mm", + "${binding.objectInclude.downPointDeepView.text}mm", + binding.objectInclude.buryMethodSpinner.selectedItem.toString(), + binding.objectInclude.areaView.text.toString(), + binding.objectInclude.lineView.text.toString(), + binding.objectInclude.roadView.text.toString(), + binding.objectInclude.constructDateView.text.toString(), + binding.objectInclude.ownerView.text.toString(), + objectId, + binding.identifierInclude.identifierIdView.text.toString(), + binding.identifierInclude.identifierTypeSpinner.selectedItem.toString(), + "${binding.identifierInclude.identifierDeepView.text}mm", + binding.identifierInclude.personDeptView.text.toString(), + binding.identifierInclude.installTimeView.text.toString(), + binding.identifierInclude.lngView.text.toString(), + binding.identifierInclude.latView.text.toString(), + binding.identifierInclude.colorSpinner.selectedItem.toString().toColor(), + binding.remarkView.text.toString(), + realPaths + ) + + //保存默认值 + "markerObjectTypeView".setDefaultValue(binding.objectInclude.markerObjectTypeView.text.toString()) + "pipelineDiameterView".setDefaultValue(binding.objectInclude.pipelineDiameterView.text.toString()) + "buryDeepView".setDefaultValue(binding.objectInclude.buryDeepView.text.toString()) + "downPipeDiameterView".setDefaultValue(binding.objectInclude.downPipeDiameterView.text.toString()) + "downPointDeepView".setDefaultValue(binding.objectInclude.downPointDeepView.text.toString()) + "areaView".setDefaultValue(binding.objectInclude.areaView.text.toString()) + "lineView".setDefaultValue(binding.objectInclude.lineView.text.toString()) + "roadView".setDefaultValue(binding.objectInclude.roadView.text.toString()) + "ownerView".setDefaultValue(binding.objectInclude.ownerView.text.toString()) + "identifierDeepView".setDefaultValue(binding.identifierInclude.identifierDeepView.text.toString()) + "personDeptView".setDefaultValue(binding.identifierInclude.personDeptView.text.toString()) + } + + //读标识器 + binding.readMarkerButton.setOnClickListener { + LoadingDialogHub.show(activity, "标识器读取中,请稍后...") + + isReadMarker = true + + activity.soundPool.play(activity.readMarkerSoundResourceId, 1f, 1f, 0, -1, 1f) + + activity.serialPortManager.searchMarkerSignal() + } + } + + private fun initDefaultData() { + binding.titleInclude.titleView.text = "安装新标识器" + binding.titleInclude.titleView.setTextColor(R.color.themeColor.convertColor(activity)) + + imageAdapter = EditableImageAdapter(activity, 3, 3) + binding.cameraInclude.addImageRecyclerView.adapter = imageAdapter + + //设置默认值 + binding.objectInclude.markerObjectTypeView.setText("markerObjectTypeView".getDefaultValue()) + binding.objectInclude.pipelineDiameterView.setText("pipelineDiameterView".getDefaultValue()) + binding.objectInclude.buryDeepView.setText("buryDeepView".getDefaultValue()) + binding.objectInclude.downPipeDiameterView.setText("downPipeDiameterView".getDefaultValue()) + binding.objectInclude.downPointDeepView.setText("downPointDeepView".getDefaultValue()) + binding.objectInclude.areaView.setText("areaView".getDefaultValue()) + binding.objectInclude.lineView.setText("lineView".getDefaultValue()) + binding.objectInclude.roadView.setText("roadView".getDefaultValue()) + binding.objectInclude.ownerView.setText("ownerView".getDefaultValue()) + binding.identifierInclude.identifierDeepView.setText("identifierDeepView".getDefaultValue()) + binding.identifierInclude.personDeptView.setText("personDeptView".getDefaultValue()) + + /**************************************************************************************/ + binding.objectInclude.objectTypeSpinner.show( + activity, LocaleConstant.POINT_TYPE_ARRAY, 0 + ) + binding.objectInclude.materialSpinner.show( + activity, LocaleConstant.PIPE_MATERIAL_ARRAY, 0 + ) + binding.objectInclude.downPipeTypeSpinner.show( + activity, LocaleConstant.DOWN_PIPE_TYPE_ARRAY, 0 + ) + binding.objectInclude.downPipeMaterialSpinner.show( + activity, LocaleConstant.PIPE_MATERIAL_ARRAY, 0 + ) + binding.objectInclude.buryMethodSpinner.show( + activity, LocaleConstant.BURY_METHOD_ARRAY, 0 + ) + + binding.objectInclude.constructDateView.setOnClickListener { + val datePicker = DatePickerDialog( + activity, null, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH) + ) + datePicker.show() + + datePicker.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener { + val year = datePicker.datePicker.year + val month = datePicker.datePicker.month + 1 + val day = datePicker.datePicker.dayOfMonth + val selectedDate = String.format( + "%s-%s-%s", year, month.appendZero(), day.appendZero() + ) + + //当前时间 + val current = System.currentTimeMillis().timestampToTime() + val today = "$selectedDate $current".dateToTimestamp() + if (Date(today).after(Date())) { + "建设年代不能早于当前日期".show(activity) + } else { + datePicker.dismiss() + binding.objectInclude.constructDateView.text = selectedDate + } + } + } + + binding.identifierInclude.identifierTypeSpinner.show( + activity, LocaleConstant.IDENTIFIER_TYPE_ARRAY, 0 + ) + + binding.identifierInclude.installTimeView.text = + System.currentTimeMillis().timestampToCompleteDate() + activity.locationHub.getCurrentLocation(true, object : ILocationListener { + override fun onAMapLocationGet(location: AMapLocation?) { + if (location != null) { + binding.identifierInclude.lngView.text = location.longitude.toString() + binding.identifierInclude.latView.text = location.latitude.toString() + } else { + "当前位置信号差,无法获取定位".show(activity) + } + } + }) + + binding.identifierInclude.colorSpinner.show(activity, LocaleConstant.COLOR_ARRAY, 0) + + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + takePicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(activity) + } else { + activity.navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imageAdapter.deleteImage(position) + } + }) + /**************************************************************************************/ + } + + private fun saveLabelInLocal() { + val objectId = SaveKeyValues.getValue(LocaleConstant.OBJECT_ID, "") as String + + val labelBean = LabelBean() + labelBean.objectType = binding.objectInclude.objectTypeSpinner.selectedItem.toString() + labelBean.pipelineType = binding.objectInclude.markerObjectTypeView.text.toString() + labelBean.pipelineMaterial = + binding.objectInclude.materialSpinner.selectedItem.toString() + labelBean.pipelineDiameter = "${binding.objectInclude.pipelineDiameterView.text}mm" + labelBean.buryDeep = "${binding.objectInclude.buryDeepView.text}mm" + labelBean.lowerType = binding.objectInclude.downPipeTypeSpinner.selectedItem.toString() + labelBean.lowerMaterial = + binding.objectInclude.downPipeMaterialSpinner.selectedItem.toString() + labelBean.lowerDiameter = "${binding.objectInclude.downPipeDiameterView.text}mm" + labelBean.lowerDeep = "${binding.objectInclude.downPointDeepView.text}mm" + labelBean.buryMethod = binding.objectInclude.buryMethodSpinner.selectedItem.toString() + labelBean.area = binding.objectInclude.areaView.text.toString() + labelBean.line = binding.objectInclude.lineView.text.toString() + labelBean.road = binding.objectInclude.roadView.text.toString() + labelBean.constructTime = binding.objectInclude.constructDateView.text.toString() + labelBean.owner = binding.objectInclude.ownerView.text.toString() + labelBean.objectId = objectId + labelBean.identifierId = binding.identifierInclude.identifierIdView.text.toString() + labelBean.identifierType = + binding.identifierInclude.identifierTypeSpinner.selectedItem.toString() + labelBean.identifierDeep = "${binding.identifierInclude.identifierDeepView.text}mm" + labelBean.person = binding.identifierInclude.personDeptView.text.toString() + labelBean.installTime = binding.identifierInclude.installTimeView.text.toString() + labelBean.lng = binding.identifierInclude.lngView.text.toString() + labelBean.lat = binding.identifierInclude.latView.text.toString() + labelBean.color = + binding.identifierInclude.colorSpinner.selectedItem.toString().toColor() + labelBean.remark = binding.remarkView.text.toString() + labelBean.imagePath = realPaths.toJson() + + DataBaseManager.get.insertTaskLabel(labelBean) + } + + private fun takePicture() { + PictureSelector.create(activity).openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: java.util.ArrayList?) { + if (result == null) { + "拍照失败,请重试".show(activity) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 + result.realPath.compressImage(activity, object : OnImageCompressListener { + override fun onSuccess(file: File) { + realPaths.add(file.absolutePath) + imageAdapter.setupImage(realPaths) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + } + + /**探测标识器对话框******************************************************************************/ + class SearchMarkerDialog(private val activity: MainActivity) : Dialog(activity) { + + val binding: DialogSearchMarkerBinding by binding() + val degreeCache by lazy { HashMap() } + var isDetectMarker = false + var isDetectDepth = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.initDialogLayoutParams(1f) + + isDetectMarker = true + activity.serialPortManager.detectMarker() + + //角度 + degreeCache["lastDegree"] = 0f + + binding.depthButton.setOnClickListener { + val result = DataBaseManager.get.queryLabelById(activity.markerId) + if (result.isNotEmpty()) { + val tag = when (result.first().identifierType) { + "EM30" -> '7' + "EM50" -> '8' + "EM14" -> '9' + else -> '1' + } + if (tag == '1') { + "此标识器无法读取埋深!".show(activity) + } else { + // 发送读取标识器埋设深度指令 + isDetectDepth = true + LoadingDialogHub.show(activity, "正在测距,请稍后...") + activity.serialPortManager.detectDepth(tag) + object : CountDownTimer(15 * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + isDetectDepth = false + LoadingDialogHub.dismiss() + "请重试".show(activity) + } + }.start() + } + } else { + "标识器未安装,安装成功后才可读取埋深!".show(activity) + } + } + + binding.markerInfoButton.setOnClickListener { + //查库 + val result = DataBaseManager.get.queryLabelById(activity.markerId) + if (result.isNotEmpty()) { + activity.navigatePageTo(result.first().toJson()) + } else { + activity.navigatePageTo(activity.markerId) + } + } + } + + override fun dismiss() { + isDetectMarker = false + activity.soundPool.stop(activity.slowSoundResourceId) + activity.soundPool.stop(activity.fastSoundResourceId) + super.dismiss() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/MainActivity.kt b/app/src/main/java/com/casic/detector/view/MainActivity.kt index 7c6446a..0ce360a 100644 --- a/app/src/main/java/com/casic/detector/view/MainActivity.kt +++ b/app/src/main/java/com/casic/detector/view/MainActivity.kt @@ -1,5 +1,9 @@ package com.casic.detector.view +import android.annotation.SuppressLint +import android.app.DatePickerDialog +import android.app.Dialog +import android.content.DialogInterface import android.content.Intent import android.graphics.BitmapFactory import android.graphics.Color @@ -8,10 +12,13 @@ import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle +import android.os.CountDownTimer import android.provider.Settings import android.util.Log import android.view.KeyEvent import android.view.View +import android.view.animation.Animation +import android.view.animation.RotateAnimation import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation @@ -25,19 +32,29 @@ import com.amap.api.maps.model.MarkerOptions import com.amap.api.maps.model.MyLocationStyle import com.casic.detector.R +import com.casic.detector.adapter.EditableImageAdapter import com.casic.detector.bean.LabelBean import com.casic.detector.bean.TaskBean import com.casic.detector.callback.ILocationListener +import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.cluster.ClusterItem import com.casic.detector.cluster.ClusterOverlay import com.casic.detector.cluster.RegionItem import com.casic.detector.databinding.ActivityMainBinding +import com.casic.detector.databinding.DialogInstallMarkerBinding +import com.casic.detector.databinding.DialogSearchMarkerBinding import com.casic.detector.extensions.appendDownloadUrl +import com.casic.detector.extensions.compressImage import com.casic.detector.extensions.drawCircle +import com.casic.detector.extensions.getDefaultValue import com.casic.detector.extensions.hexToString import com.casic.detector.extensions.initImmersionBar import com.casic.detector.extensions.isNumber +import com.casic.detector.extensions.setDefaultValue +import com.casic.detector.extensions.show +import com.casic.detector.extensions.toColor import com.casic.detector.extensions.toHex +import com.casic.detector.extensions.toObjectType import com.casic.detector.model.TaskModel import com.casic.detector.utils.DataBaseManager import com.casic.detector.utils.ExcelHub @@ -53,13 +70,24 @@ import com.casic.detector.widgets.SamplePopupWindow import com.google.gson.Gson import com.google.gson.reflect.TypeToken +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.appendZero +import com.pengxh.kt.lite.extensions.binding +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.convertDrawable import com.pengxh.kt.lite.extensions.createDownloadFileDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.initDialogLayoutParams import com.pengxh.kt.lite.extensions.isNetworkConnected import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.extensions.timestampToTime import com.pengxh.kt.lite.extensions.toJson import com.pengxh.kt.lite.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState @@ -73,6 +101,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File +import java.text.DecimalFormat +import java.util.Calendar +import java.util.Date class MainActivity : KotlinBaseActivity(), @@ -87,18 +118,23 @@ private val locationHub by lazy { LocationHub(this) } private val regionRadius by lazy { LocaleConstant.RADIUS_SIZE.dp2px(this) } private val backDrawables by lazy { HashMap() } - private val audioAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_ALARM) - .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) - .build() + private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes( + AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build() + ).build() + private var readMarkerSoundResourceId = 0 + private var slowSoundResourceId = 0 + private var fastSoundResourceId = 0 private var clickTime: Long = 0 private var labelBeans = ArrayList() private var clusterOverlay: ClusterOverlay? = null private var latitude: Double = 0.0 private var longitude: Double = 0.0 private var ids = ArrayList() + private lateinit var markerId: String private lateinit var aMap: AMap - private lateinit var soundPool: SoundPool private lateinit var taskViewModel: TaskViewModel private lateinit var taskBean: TaskBean private lateinit var taskId: String @@ -114,6 +150,10 @@ override fun initOnCreate(savedInstanceState: Bundle?) { initMapConfig(savedInstanceState) + readMarkerSoundResourceId = soundPool.load(this, R.raw.ring3, 1) + slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1) + fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1) + //调高串口电位 gpioManager.setGpioHigh("18") @@ -211,7 +251,11 @@ //安装。上传,然后存入本地库 binding.installButton.setOnClickListener { - navigatePageTo() + /** + * 改为Dialog方式,避免频繁打开/关闭串口 + * */ +// navigatePageTo() + installDialog.show() } //查看 @@ -318,7 +362,11 @@ //探测 binding.detectionButton.setOnClickListener { - navigatePageTo() + /** + * 改为Dialog方式,避免频繁打开/关闭串口 + * */ +// navigatePageTo() + searchDialog.show() } //自由巡检 @@ -331,8 +379,8 @@ .setOnDialogButtonClickListener(object : AlertMessageDialog.OnDialogButtonClickListener { override fun onConfirmClick() { - soundPool.release() - serialPortManager.closeSerialPort() + isFreeTask = false + soundPool.stop(readMarkerSoundResourceId) taskViewModel.uploadTaskMarker(taskId, ids) binding.stopFreeTaskButton.visibility = View.GONE @@ -342,6 +390,13 @@ } } + private val installDialog by lazy { InstallMarkerDialog(this) } + private val searchDialog by lazy { SearchMarkerDialog(this) } + private var isFreeTask = false + private var taskLabel: TaskBean? = null + private var isPlaying = false + private val decimal by lazy { DecimalFormat("0.0") } + private fun startFreeTask() { AlertInputDialog.Builder() .setContext(this) @@ -354,15 +409,9 @@ val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(userId, value) - soundPool = SoundPool.Builder() - .setMaxStreams(16) - .setAudioAttributes(audioAttributes) - .build() - soundPool.load(context, R.raw.ring3, 1) - soundPool.setOnLoadCompleteListener { soundPool, sampleId, _ -> - soundPool.play(sampleId, 1f, 1f, 0, -1, 1f) - } + soundPool.play(readMarkerSoundResourceId, 1f, 1f, 0, -1, 1f) + isFreeTask = true //自由巡检 serialPortManager.searchMarkerSignal() @@ -375,8 +424,142 @@ override fun onDataReceived(buffer: ByteArray) { val hex = buffer.toHex() -// Log.d(kTag, "$kTag => $hex") - if (hex.length >= 20) { + Log.d(kTag, "$kTag => $hex") +// if (hex.length >= 20) { +// val markerId = hex.take(20).hexToString() +// if (markerId.isNumber()) { +// "标识器${markerId}已探测".show(this) +// ids.add(markerId) +// } +// } + if (searchDialog.isDetectMarker) { + if (hex.startsWith("4E")) { + try { + //4E转为String为N,代表能量值 + //用能量值转动表盘 + val energyResponse = hex.take(10).hexToString() + val energy = energyResponse.substring(1).toInt() + if (energy >= 4000) { + isPlaying = if (!isPlaying) { + soundPool.play(fastSoundResourceId, 1f, 1f, 0, 0, 1f) + true + } else { + soundPool.stop(fastSoundResourceId) + false + } + } else { + isPlaying = if (!isPlaying) { + soundPool.play(slowSoundResourceId, 1f, 1f, 0, 0, 1f) + true + } else { + soundPool.stop(slowSoundResourceId) + false + } + } + + searchDialog.binding.resultTextView.text = "信号能量值:${energyResponse}" + + //转换为转动的角度 + val degree = (energy.toFloat() / (50 * 100)) * 180 + searchDialog.binding.energyValueView.text = decimal.format(degree) + + searchDialog.degreeCache["lastDegree"]?.apply { + val animation = RotateAnimation( + this, degree, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f + ) + animation.duration = 0 + animation.fillAfter = true + searchDialog.binding.needleView.startAnimation(animation) + } + + //保存旋转后的角度 + searchDialog.degreeCache["lastDegree"] = degree + + if (energy <= 700) {//18° + searchDialog.binding.energyTipsView.text = "标识器信号较弱,可能距离较远" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#8D1717")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_red) + + searchDialog.binding.depthButton.isEnabled = false + searchDialog.binding.depthButton.setTextColor(Color.parseColor("#CCCCCC")) + searchDialog.binding.depthButton.setBackgroundResource(R.mipmap.left_button_disable) + searchDialog.binding.markerInfoButton.isEnabled = false + searchDialog.binding.markerInfoButton.setTextColor(Color.parseColor("#CCCCCC")) + searchDialog.binding.markerInfoButton.setBackgroundResource(R.mipmap.right_button_disable) + + searchDialog.binding.searchResultView.text = "未检测到标识器" + searchDialog.binding.searchResultView.setTextColor(Color.parseColor("#8D1717")) + searchDialog.binding.searchResultView.setBackgroundResource(R.mipmap.bg_small_text_red) + } else if (energy >= 4100) {//148° + searchDialog.binding.energyTipsView.text = "标识器信号极强,可能在正下方" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#428d00")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_green) + } else {//[18°,148°] + searchDialog.binding.energyTipsView.text = "已靠近标识器,请继续移动位置" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#8C5700")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_yellow) + } + } catch (e: NumberFormatException) { + e.printStackTrace() + } + } else if (hex.startsWith("53")) { + LoadingDialogHub.dismiss() + try { + val depthResponse = hex.take(10).hexToString() + val depth = depthResponse.drop(4) + AlertMessageDialog.Builder() + .setContext(this) + .setTitle("温馨提示") + .setMessage("标识器埋深:${depth}厘米") + .setPositiveButton("知道了") + .setOnDialogButtonClickListener(object : + AlertMessageDialog.OnDialogButtonClickListener { + override fun onConfirmClick() { + + } + }).build().show() + } catch (e: NumberFormatException) { + e.printStackTrace() + } + } else { + val id = hex.take(20).hexToString() + if (id.isNumber()) { + markerId = id + + searchDialog.binding.depthButton.isEnabled = true + searchDialog.binding.depthButton.setTextColor(Color.WHITE) + searchDialog.binding.depthButton.setBackgroundResource(R.mipmap.left_button_enable) + searchDialog.binding.markerInfoButton.isEnabled = true + searchDialog.binding.markerInfoButton.setTextColor(Color.WHITE) + searchDialog.binding.markerInfoButton.setBackgroundResource(R.mipmap.right_button_enable) + + searchDialog.binding.searchResultView.text = "已检测到标识器" + searchDialog.binding.searchResultView.setTextColor(Color.parseColor("#428d00")) + searchDialog.binding.searchResultView.setBackgroundResource(R.mipmap.bg_small_text_green) + + //自动上传标识器 + taskLabel = DataBaseManager.get.queryTaskLabelByIdAndState( + markerId, "未开始" + ) + taskLabel?.apply { + /** + * 此taskCode是 [com.casic.detector.model.TaskModel.MessageModel.TaskDetailInfosModel] 里面的主键,也就是这个标签在数据库里面的主键 + * */ + taskViewModel.uploadMarker(taskCode) + } + } + } + } else if (installDialog.isReadMarker) { + val markerId = hex.take(20).hexToString() + if (markerId.isNumber()) { + LoadingDialogHub.dismiss() + soundPool.stop(readMarkerSoundResourceId) + installDialog.isReadMarker = false + installDialog.binding.identifierInclude.identifierIdView.text = markerId + } + } else if (isFreeTask) { val markerId = hex.take(20).hexToString() if (markerId.isNumber()) { "标识器${markerId}已探测".show(this) @@ -388,8 +571,21 @@ override fun observeRequestState() { taskViewModel.loadState.observe(this) { when (it) { - LoadState.Loading -> LoadingDialogHub.show(this, "提交工单中,请稍后") - else -> LoadingDialogHub.dismiss() + LoadState.Loading -> { + if (installDialog.isInstallMarker) { + LoadingDialogHub.show(this, "标识器安装中,请稍后...") + } else { + LoadingDialogHub.show(this, "提交工单中,请稍后") + } + } + + else -> { + if (installDialog.isInstallMarker) { + installDialog.dismiss() + installDialog.isInstallMarker = false + } + LoadingDialogHub.dismiss() + } } detectRedrawGraphic(taskBean.taskId) } @@ -774,10 +970,423 @@ } override fun onDestroy() { - super.onDestroy() - binding.mapView.onDestroy() locationHub.stopLocation() + serialPortManager.closeSerialPort() //降低串口电位 gpioManager.setGpioLow("18") + super.onDestroy() + binding.mapView.onDestroy() + } + + /**安装标识器对话框******************************************************************************/ + @SuppressLint("SetTextI18n") + class InstallMarkerDialog(private val activity: MainActivity) : Dialog(activity) { + + val binding: DialogInstallMarkerBinding by binding() + var isReadMarker = false + var isInstallMarker = false + + private val calendar by lazy { Calendar.getInstance() } + private val realPaths = ArrayList() //真实图片路径 + private lateinit var imageAdapter: EditableImageAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.initDialogLayoutParams(1f) + setCancelable(false) + setCanceledOnTouchOutside(false) + + //初始化数据 + initDefaultData() + + //返回 + binding.titleInclude.leftBackView.setOnClickListener { + activity.soundPool.stop(activity.readMarkerSoundResourceId) + dismiss() + } + + //安装 + binding.installButton.setOnClickListener { + val companyId = SaveKeyValues.getValue(LocaleConstant.USER_COMPANY_ID, "") as String + val objectId = SaveKeyValues.getValue(LocaleConstant.OBJECT_ID, "") as String + if (binding.objectInclude.markerObjectTypeView.text.isNullOrBlank()) { + when (binding.objectInclude.objectTypeSpinner.selectedItem.toString()) { + "管线" -> { + "请输入管线种类!".show(activity) + } + + "管线附属物" -> { + "请输入附属物名称!".show(activity) + } + + "管线特征管点" -> { + "请输入特征管点!".show(activity) + } + + "交叉穿越点" -> { + "请输入上层管种类!".show(activity) + } + } + return@setOnClickListener + } + + if (binding.objectInclude.pipelineDiameterView.text.isNullOrBlank()) { + "请输入管径".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.buryDeepView.text.isNullOrBlank()) { + "请输入埋深".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.downPipeDiameterView.text.isNullOrBlank()) { + "请输入下层管管径".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.areaView.text.isNullOrBlank()) { + "请输入所属区域".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.lineView.text.isNullOrBlank()) { + "请输入所属线路".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.roadView.text.isNullOrBlank()) { + "请输入所属道路".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.constructDateView.text.isNullOrBlank()) { + "请选择建设年代".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.ownerView.text.isNullOrBlank()) { + "请输入权属单位".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.identifierIdView.text.isNullOrBlank()) { + "请先读取标识器获取ID".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.identifierDeepView.text.isNullOrBlank()) { + "请输入标识器埋深".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.personDeptView.text.isNullOrBlank()) { + "请输入标识器安装部门".show(activity) + return@setOnClickListener + } + + //先存本地再上传服务器 + saveLabelInLocal() + + isInstallMarker = true + activity.taskViewModel.installLabel( + companyId, + binding.objectInclude.objectTypeSpinner.selectedItem.toString().toObjectType(), + binding.objectInclude.markerObjectTypeView.text.toString(), + binding.objectInclude.materialSpinner.selectedItem.toString(), + "${binding.objectInclude.pipelineDiameterView.text}mm", + "${binding.objectInclude.buryDeepView.text}mm", + binding.objectInclude.downPipeTypeSpinner.selectedItem.toString(), + binding.objectInclude.downPipeMaterialSpinner.selectedItem.toString(), + "${binding.objectInclude.downPipeDiameterView.text}mm", + "${binding.objectInclude.downPointDeepView.text}mm", + binding.objectInclude.buryMethodSpinner.selectedItem.toString(), + binding.objectInclude.areaView.text.toString(), + binding.objectInclude.lineView.text.toString(), + binding.objectInclude.roadView.text.toString(), + binding.objectInclude.constructDateView.text.toString(), + binding.objectInclude.ownerView.text.toString(), + objectId, + binding.identifierInclude.identifierIdView.text.toString(), + binding.identifierInclude.identifierTypeSpinner.selectedItem.toString(), + "${binding.identifierInclude.identifierDeepView.text}mm", + binding.identifierInclude.personDeptView.text.toString(), + binding.identifierInclude.installTimeView.text.toString(), + binding.identifierInclude.lngView.text.toString(), + binding.identifierInclude.latView.text.toString(), + binding.identifierInclude.colorSpinner.selectedItem.toString().toColor(), + binding.remarkView.text.toString(), + realPaths + ) + + //保存默认值 + "markerObjectTypeView".setDefaultValue(binding.objectInclude.markerObjectTypeView.text.toString()) + "pipelineDiameterView".setDefaultValue(binding.objectInclude.pipelineDiameterView.text.toString()) + "buryDeepView".setDefaultValue(binding.objectInclude.buryDeepView.text.toString()) + "downPipeDiameterView".setDefaultValue(binding.objectInclude.downPipeDiameterView.text.toString()) + "downPointDeepView".setDefaultValue(binding.objectInclude.downPointDeepView.text.toString()) + "areaView".setDefaultValue(binding.objectInclude.areaView.text.toString()) + "lineView".setDefaultValue(binding.objectInclude.lineView.text.toString()) + "roadView".setDefaultValue(binding.objectInclude.roadView.text.toString()) + "ownerView".setDefaultValue(binding.objectInclude.ownerView.text.toString()) + "identifierDeepView".setDefaultValue(binding.identifierInclude.identifierDeepView.text.toString()) + "personDeptView".setDefaultValue(binding.identifierInclude.personDeptView.text.toString()) + } + + //读标识器 + binding.readMarkerButton.setOnClickListener { + LoadingDialogHub.show(activity, "标识器读取中,请稍后...") + + isReadMarker = true + + activity.soundPool.play(activity.readMarkerSoundResourceId, 1f, 1f, 0, -1, 1f) + + activity.serialPortManager.searchMarkerSignal() + } + } + + private fun initDefaultData() { + binding.titleInclude.titleView.text = "安装新标识器" + binding.titleInclude.titleView.setTextColor(R.color.themeColor.convertColor(activity)) + + imageAdapter = EditableImageAdapter(activity, 3, 3) + binding.cameraInclude.addImageRecyclerView.adapter = imageAdapter + + //设置默认值 + binding.objectInclude.markerObjectTypeView.setText("markerObjectTypeView".getDefaultValue()) + binding.objectInclude.pipelineDiameterView.setText("pipelineDiameterView".getDefaultValue()) + binding.objectInclude.buryDeepView.setText("buryDeepView".getDefaultValue()) + binding.objectInclude.downPipeDiameterView.setText("downPipeDiameterView".getDefaultValue()) + binding.objectInclude.downPointDeepView.setText("downPointDeepView".getDefaultValue()) + binding.objectInclude.areaView.setText("areaView".getDefaultValue()) + binding.objectInclude.lineView.setText("lineView".getDefaultValue()) + binding.objectInclude.roadView.setText("roadView".getDefaultValue()) + binding.objectInclude.ownerView.setText("ownerView".getDefaultValue()) + binding.identifierInclude.identifierDeepView.setText("identifierDeepView".getDefaultValue()) + binding.identifierInclude.personDeptView.setText("personDeptView".getDefaultValue()) + + /**************************************************************************************/ + binding.objectInclude.objectTypeSpinner.show( + activity, LocaleConstant.POINT_TYPE_ARRAY, 0 + ) + binding.objectInclude.materialSpinner.show( + activity, LocaleConstant.PIPE_MATERIAL_ARRAY, 0 + ) + binding.objectInclude.downPipeTypeSpinner.show( + activity, LocaleConstant.DOWN_PIPE_TYPE_ARRAY, 0 + ) + binding.objectInclude.downPipeMaterialSpinner.show( + activity, LocaleConstant.PIPE_MATERIAL_ARRAY, 0 + ) + binding.objectInclude.buryMethodSpinner.show( + activity, LocaleConstant.BURY_METHOD_ARRAY, 0 + ) + + binding.objectInclude.constructDateView.setOnClickListener { + val datePicker = DatePickerDialog( + activity, null, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH) + ) + datePicker.show() + + datePicker.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener { + val year = datePicker.datePicker.year + val month = datePicker.datePicker.month + 1 + val day = datePicker.datePicker.dayOfMonth + val selectedDate = String.format( + "%s-%s-%s", year, month.appendZero(), day.appendZero() + ) + + //当前时间 + val current = System.currentTimeMillis().timestampToTime() + val today = "$selectedDate $current".dateToTimestamp() + if (Date(today).after(Date())) { + "建设年代不能早于当前日期".show(activity) + } else { + datePicker.dismiss() + binding.objectInclude.constructDateView.text = selectedDate + } + } + } + + binding.identifierInclude.identifierTypeSpinner.show( + activity, LocaleConstant.IDENTIFIER_TYPE_ARRAY, 0 + ) + + binding.identifierInclude.installTimeView.text = + System.currentTimeMillis().timestampToCompleteDate() + activity.locationHub.getCurrentLocation(true, object : ILocationListener { + override fun onAMapLocationGet(location: AMapLocation?) { + if (location != null) { + binding.identifierInclude.lngView.text = location.longitude.toString() + binding.identifierInclude.latView.text = location.latitude.toString() + } else { + "当前位置信号差,无法获取定位".show(activity) + } + } + }) + + binding.identifierInclude.colorSpinner.show(activity, LocaleConstant.COLOR_ARRAY, 0) + + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + takePicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(activity) + } else { + activity.navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imageAdapter.deleteImage(position) + } + }) + /**************************************************************************************/ + } + + private fun saveLabelInLocal() { + val objectId = SaveKeyValues.getValue(LocaleConstant.OBJECT_ID, "") as String + + val labelBean = LabelBean() + labelBean.objectType = binding.objectInclude.objectTypeSpinner.selectedItem.toString() + labelBean.pipelineType = binding.objectInclude.markerObjectTypeView.text.toString() + labelBean.pipelineMaterial = + binding.objectInclude.materialSpinner.selectedItem.toString() + labelBean.pipelineDiameter = "${binding.objectInclude.pipelineDiameterView.text}mm" + labelBean.buryDeep = "${binding.objectInclude.buryDeepView.text}mm" + labelBean.lowerType = binding.objectInclude.downPipeTypeSpinner.selectedItem.toString() + labelBean.lowerMaterial = + binding.objectInclude.downPipeMaterialSpinner.selectedItem.toString() + labelBean.lowerDiameter = "${binding.objectInclude.downPipeDiameterView.text}mm" + labelBean.lowerDeep = "${binding.objectInclude.downPointDeepView.text}mm" + labelBean.buryMethod = binding.objectInclude.buryMethodSpinner.selectedItem.toString() + labelBean.area = binding.objectInclude.areaView.text.toString() + labelBean.line = binding.objectInclude.lineView.text.toString() + labelBean.road = binding.objectInclude.roadView.text.toString() + labelBean.constructTime = binding.objectInclude.constructDateView.text.toString() + labelBean.owner = binding.objectInclude.ownerView.text.toString() + labelBean.objectId = objectId + labelBean.identifierId = binding.identifierInclude.identifierIdView.text.toString() + labelBean.identifierType = + binding.identifierInclude.identifierTypeSpinner.selectedItem.toString() + labelBean.identifierDeep = "${binding.identifierInclude.identifierDeepView.text}mm" + labelBean.person = binding.identifierInclude.personDeptView.text.toString() + labelBean.installTime = binding.identifierInclude.installTimeView.text.toString() + labelBean.lng = binding.identifierInclude.lngView.text.toString() + labelBean.lat = binding.identifierInclude.latView.text.toString() + labelBean.color = + binding.identifierInclude.colorSpinner.selectedItem.toString().toColor() + labelBean.remark = binding.remarkView.text.toString() + labelBean.imagePath = realPaths.toJson() + + DataBaseManager.get.insertTaskLabel(labelBean) + } + + private fun takePicture() { + PictureSelector.create(activity).openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: java.util.ArrayList?) { + if (result == null) { + "拍照失败,请重试".show(activity) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 + result.realPath.compressImage(activity, object : OnImageCompressListener { + override fun onSuccess(file: File) { + realPaths.add(file.absolutePath) + imageAdapter.setupImage(realPaths) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + } + + /**探测标识器对话框******************************************************************************/ + class SearchMarkerDialog(private val activity: MainActivity) : Dialog(activity) { + + val binding: DialogSearchMarkerBinding by binding() + val degreeCache by lazy { HashMap() } + var isDetectMarker = false + var isDetectDepth = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.initDialogLayoutParams(1f) + + isDetectMarker = true + activity.serialPortManager.detectMarker() + + //角度 + degreeCache["lastDegree"] = 0f + + binding.depthButton.setOnClickListener { + val result = DataBaseManager.get.queryLabelById(activity.markerId) + if (result.isNotEmpty()) { + val tag = when (result.first().identifierType) { + "EM30" -> '7' + "EM50" -> '8' + "EM14" -> '9' + else -> '1' + } + if (tag == '1') { + "此标识器无法读取埋深!".show(activity) + } else { + // 发送读取标识器埋设深度指令 + isDetectDepth = true + LoadingDialogHub.show(activity, "正在测距,请稍后...") + activity.serialPortManager.detectDepth(tag) + object : CountDownTimer(15 * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + isDetectDepth = false + LoadingDialogHub.dismiss() + "请重试".show(activity) + } + }.start() + } + } else { + "标识器未安装,安装成功后才可读取埋深!".show(activity) + } + } + + binding.markerInfoButton.setOnClickListener { + //查库 + val result = DataBaseManager.get.queryLabelById(activity.markerId) + if (result.isNotEmpty()) { + activity.navigatePageTo(result.first().toJson()) + } else { + activity.navigatePageTo(activity.markerId) + } + } + } + + override fun dismiss() { + isDetectMarker = false + activity.soundPool.stop(activity.slowSoundResourceId) + activity.soundPool.stop(activity.fastSoundResourceId) + super.dismiss() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 426aed3..3d8bb94 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -38,7 +38,7 @@ private val degreeCache by lazy { HashMap() } private val locationHub by lazy { LocationHub(this) } private val audioAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_MEDIA) + .setUsage(AudioAttributes.USAGE_ALARM) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build() private lateinit var taskViewModel: TaskViewModel diff --git a/app/src/main/java/com/casic/detector/view/MainActivity.kt b/app/src/main/java/com/casic/detector/view/MainActivity.kt index 7c6446a..0ce360a 100644 --- a/app/src/main/java/com/casic/detector/view/MainActivity.kt +++ b/app/src/main/java/com/casic/detector/view/MainActivity.kt @@ -1,5 +1,9 @@ package com.casic.detector.view +import android.annotation.SuppressLint +import android.app.DatePickerDialog +import android.app.Dialog +import android.content.DialogInterface import android.content.Intent import android.graphics.BitmapFactory import android.graphics.Color @@ -8,10 +12,13 @@ import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle +import android.os.CountDownTimer import android.provider.Settings import android.util.Log import android.view.KeyEvent import android.view.View +import android.view.animation.Animation +import android.view.animation.RotateAnimation import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation @@ -25,19 +32,29 @@ import com.amap.api.maps.model.MarkerOptions import com.amap.api.maps.model.MyLocationStyle import com.casic.detector.R +import com.casic.detector.adapter.EditableImageAdapter import com.casic.detector.bean.LabelBean import com.casic.detector.bean.TaskBean import com.casic.detector.callback.ILocationListener +import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.cluster.ClusterItem import com.casic.detector.cluster.ClusterOverlay import com.casic.detector.cluster.RegionItem import com.casic.detector.databinding.ActivityMainBinding +import com.casic.detector.databinding.DialogInstallMarkerBinding +import com.casic.detector.databinding.DialogSearchMarkerBinding import com.casic.detector.extensions.appendDownloadUrl +import com.casic.detector.extensions.compressImage import com.casic.detector.extensions.drawCircle +import com.casic.detector.extensions.getDefaultValue import com.casic.detector.extensions.hexToString import com.casic.detector.extensions.initImmersionBar import com.casic.detector.extensions.isNumber +import com.casic.detector.extensions.setDefaultValue +import com.casic.detector.extensions.show +import com.casic.detector.extensions.toColor import com.casic.detector.extensions.toHex +import com.casic.detector.extensions.toObjectType import com.casic.detector.model.TaskModel import com.casic.detector.utils.DataBaseManager import com.casic.detector.utils.ExcelHub @@ -53,13 +70,24 @@ import com.casic.detector.widgets.SamplePopupWindow import com.google.gson.Gson import com.google.gson.reflect.TypeToken +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.appendZero +import com.pengxh.kt.lite.extensions.binding +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.convertDrawable import com.pengxh.kt.lite.extensions.createDownloadFileDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.initDialogLayoutParams import com.pengxh.kt.lite.extensions.isNetworkConnected import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.extensions.timestampToTime import com.pengxh.kt.lite.extensions.toJson import com.pengxh.kt.lite.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState @@ -73,6 +101,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File +import java.text.DecimalFormat +import java.util.Calendar +import java.util.Date class MainActivity : KotlinBaseActivity(), @@ -87,18 +118,23 @@ private val locationHub by lazy { LocationHub(this) } private val regionRadius by lazy { LocaleConstant.RADIUS_SIZE.dp2px(this) } private val backDrawables by lazy { HashMap() } - private val audioAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_ALARM) - .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) - .build() + private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes( + AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build() + ).build() + private var readMarkerSoundResourceId = 0 + private var slowSoundResourceId = 0 + private var fastSoundResourceId = 0 private var clickTime: Long = 0 private var labelBeans = ArrayList() private var clusterOverlay: ClusterOverlay? = null private var latitude: Double = 0.0 private var longitude: Double = 0.0 private var ids = ArrayList() + private lateinit var markerId: String private lateinit var aMap: AMap - private lateinit var soundPool: SoundPool private lateinit var taskViewModel: TaskViewModel private lateinit var taskBean: TaskBean private lateinit var taskId: String @@ -114,6 +150,10 @@ override fun initOnCreate(savedInstanceState: Bundle?) { initMapConfig(savedInstanceState) + readMarkerSoundResourceId = soundPool.load(this, R.raw.ring3, 1) + slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1) + fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1) + //调高串口电位 gpioManager.setGpioHigh("18") @@ -211,7 +251,11 @@ //安装。上传,然后存入本地库 binding.installButton.setOnClickListener { - navigatePageTo() + /** + * 改为Dialog方式,避免频繁打开/关闭串口 + * */ +// navigatePageTo() + installDialog.show() } //查看 @@ -318,7 +362,11 @@ //探测 binding.detectionButton.setOnClickListener { - navigatePageTo() + /** + * 改为Dialog方式,避免频繁打开/关闭串口 + * */ +// navigatePageTo() + searchDialog.show() } //自由巡检 @@ -331,8 +379,8 @@ .setOnDialogButtonClickListener(object : AlertMessageDialog.OnDialogButtonClickListener { override fun onConfirmClick() { - soundPool.release() - serialPortManager.closeSerialPort() + isFreeTask = false + soundPool.stop(readMarkerSoundResourceId) taskViewModel.uploadTaskMarker(taskId, ids) binding.stopFreeTaskButton.visibility = View.GONE @@ -342,6 +390,13 @@ } } + private val installDialog by lazy { InstallMarkerDialog(this) } + private val searchDialog by lazy { SearchMarkerDialog(this) } + private var isFreeTask = false + private var taskLabel: TaskBean? = null + private var isPlaying = false + private val decimal by lazy { DecimalFormat("0.0") } + private fun startFreeTask() { AlertInputDialog.Builder() .setContext(this) @@ -354,15 +409,9 @@ val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(userId, value) - soundPool = SoundPool.Builder() - .setMaxStreams(16) - .setAudioAttributes(audioAttributes) - .build() - soundPool.load(context, R.raw.ring3, 1) - soundPool.setOnLoadCompleteListener { soundPool, sampleId, _ -> - soundPool.play(sampleId, 1f, 1f, 0, -1, 1f) - } + soundPool.play(readMarkerSoundResourceId, 1f, 1f, 0, -1, 1f) + isFreeTask = true //自由巡检 serialPortManager.searchMarkerSignal() @@ -375,8 +424,142 @@ override fun onDataReceived(buffer: ByteArray) { val hex = buffer.toHex() -// Log.d(kTag, "$kTag => $hex") - if (hex.length >= 20) { + Log.d(kTag, "$kTag => $hex") +// if (hex.length >= 20) { +// val markerId = hex.take(20).hexToString() +// if (markerId.isNumber()) { +// "标识器${markerId}已探测".show(this) +// ids.add(markerId) +// } +// } + if (searchDialog.isDetectMarker) { + if (hex.startsWith("4E")) { + try { + //4E转为String为N,代表能量值 + //用能量值转动表盘 + val energyResponse = hex.take(10).hexToString() + val energy = energyResponse.substring(1).toInt() + if (energy >= 4000) { + isPlaying = if (!isPlaying) { + soundPool.play(fastSoundResourceId, 1f, 1f, 0, 0, 1f) + true + } else { + soundPool.stop(fastSoundResourceId) + false + } + } else { + isPlaying = if (!isPlaying) { + soundPool.play(slowSoundResourceId, 1f, 1f, 0, 0, 1f) + true + } else { + soundPool.stop(slowSoundResourceId) + false + } + } + + searchDialog.binding.resultTextView.text = "信号能量值:${energyResponse}" + + //转换为转动的角度 + val degree = (energy.toFloat() / (50 * 100)) * 180 + searchDialog.binding.energyValueView.text = decimal.format(degree) + + searchDialog.degreeCache["lastDegree"]?.apply { + val animation = RotateAnimation( + this, degree, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f + ) + animation.duration = 0 + animation.fillAfter = true + searchDialog.binding.needleView.startAnimation(animation) + } + + //保存旋转后的角度 + searchDialog.degreeCache["lastDegree"] = degree + + if (energy <= 700) {//18° + searchDialog.binding.energyTipsView.text = "标识器信号较弱,可能距离较远" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#8D1717")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_red) + + searchDialog.binding.depthButton.isEnabled = false + searchDialog.binding.depthButton.setTextColor(Color.parseColor("#CCCCCC")) + searchDialog.binding.depthButton.setBackgroundResource(R.mipmap.left_button_disable) + searchDialog.binding.markerInfoButton.isEnabled = false + searchDialog.binding.markerInfoButton.setTextColor(Color.parseColor("#CCCCCC")) + searchDialog.binding.markerInfoButton.setBackgroundResource(R.mipmap.right_button_disable) + + searchDialog.binding.searchResultView.text = "未检测到标识器" + searchDialog.binding.searchResultView.setTextColor(Color.parseColor("#8D1717")) + searchDialog.binding.searchResultView.setBackgroundResource(R.mipmap.bg_small_text_red) + } else if (energy >= 4100) {//148° + searchDialog.binding.energyTipsView.text = "标识器信号极强,可能在正下方" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#428d00")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_green) + } else {//[18°,148°] + searchDialog.binding.energyTipsView.text = "已靠近标识器,请继续移动位置" + searchDialog.binding.energyTipsView.setTextColor(Color.parseColor("#8C5700")) + searchDialog.binding.energyTipsView.setBackgroundResource(R.mipmap.bg_large_text_yellow) + } + } catch (e: NumberFormatException) { + e.printStackTrace() + } + } else if (hex.startsWith("53")) { + LoadingDialogHub.dismiss() + try { + val depthResponse = hex.take(10).hexToString() + val depth = depthResponse.drop(4) + AlertMessageDialog.Builder() + .setContext(this) + .setTitle("温馨提示") + .setMessage("标识器埋深:${depth}厘米") + .setPositiveButton("知道了") + .setOnDialogButtonClickListener(object : + AlertMessageDialog.OnDialogButtonClickListener { + override fun onConfirmClick() { + + } + }).build().show() + } catch (e: NumberFormatException) { + e.printStackTrace() + } + } else { + val id = hex.take(20).hexToString() + if (id.isNumber()) { + markerId = id + + searchDialog.binding.depthButton.isEnabled = true + searchDialog.binding.depthButton.setTextColor(Color.WHITE) + searchDialog.binding.depthButton.setBackgroundResource(R.mipmap.left_button_enable) + searchDialog.binding.markerInfoButton.isEnabled = true + searchDialog.binding.markerInfoButton.setTextColor(Color.WHITE) + searchDialog.binding.markerInfoButton.setBackgroundResource(R.mipmap.right_button_enable) + + searchDialog.binding.searchResultView.text = "已检测到标识器" + searchDialog.binding.searchResultView.setTextColor(Color.parseColor("#428d00")) + searchDialog.binding.searchResultView.setBackgroundResource(R.mipmap.bg_small_text_green) + + //自动上传标识器 + taskLabel = DataBaseManager.get.queryTaskLabelByIdAndState( + markerId, "未开始" + ) + taskLabel?.apply { + /** + * 此taskCode是 [com.casic.detector.model.TaskModel.MessageModel.TaskDetailInfosModel] 里面的主键,也就是这个标签在数据库里面的主键 + * */ + taskViewModel.uploadMarker(taskCode) + } + } + } + } else if (installDialog.isReadMarker) { + val markerId = hex.take(20).hexToString() + if (markerId.isNumber()) { + LoadingDialogHub.dismiss() + soundPool.stop(readMarkerSoundResourceId) + installDialog.isReadMarker = false + installDialog.binding.identifierInclude.identifierIdView.text = markerId + } + } else if (isFreeTask) { val markerId = hex.take(20).hexToString() if (markerId.isNumber()) { "标识器${markerId}已探测".show(this) @@ -388,8 +571,21 @@ override fun observeRequestState() { taskViewModel.loadState.observe(this) { when (it) { - LoadState.Loading -> LoadingDialogHub.show(this, "提交工单中,请稍后") - else -> LoadingDialogHub.dismiss() + LoadState.Loading -> { + if (installDialog.isInstallMarker) { + LoadingDialogHub.show(this, "标识器安装中,请稍后...") + } else { + LoadingDialogHub.show(this, "提交工单中,请稍后") + } + } + + else -> { + if (installDialog.isInstallMarker) { + installDialog.dismiss() + installDialog.isInstallMarker = false + } + LoadingDialogHub.dismiss() + } } detectRedrawGraphic(taskBean.taskId) } @@ -774,10 +970,423 @@ } override fun onDestroy() { - super.onDestroy() - binding.mapView.onDestroy() locationHub.stopLocation() + serialPortManager.closeSerialPort() //降低串口电位 gpioManager.setGpioLow("18") + super.onDestroy() + binding.mapView.onDestroy() + } + + /**安装标识器对话框******************************************************************************/ + @SuppressLint("SetTextI18n") + class InstallMarkerDialog(private val activity: MainActivity) : Dialog(activity) { + + val binding: DialogInstallMarkerBinding by binding() + var isReadMarker = false + var isInstallMarker = false + + private val calendar by lazy { Calendar.getInstance() } + private val realPaths = ArrayList() //真实图片路径 + private lateinit var imageAdapter: EditableImageAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.initDialogLayoutParams(1f) + setCancelable(false) + setCanceledOnTouchOutside(false) + + //初始化数据 + initDefaultData() + + //返回 + binding.titleInclude.leftBackView.setOnClickListener { + activity.soundPool.stop(activity.readMarkerSoundResourceId) + dismiss() + } + + //安装 + binding.installButton.setOnClickListener { + val companyId = SaveKeyValues.getValue(LocaleConstant.USER_COMPANY_ID, "") as String + val objectId = SaveKeyValues.getValue(LocaleConstant.OBJECT_ID, "") as String + if (binding.objectInclude.markerObjectTypeView.text.isNullOrBlank()) { + when (binding.objectInclude.objectTypeSpinner.selectedItem.toString()) { + "管线" -> { + "请输入管线种类!".show(activity) + } + + "管线附属物" -> { + "请输入附属物名称!".show(activity) + } + + "管线特征管点" -> { + "请输入特征管点!".show(activity) + } + + "交叉穿越点" -> { + "请输入上层管种类!".show(activity) + } + } + return@setOnClickListener + } + + if (binding.objectInclude.pipelineDiameterView.text.isNullOrBlank()) { + "请输入管径".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.buryDeepView.text.isNullOrBlank()) { + "请输入埋深".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.downPipeDiameterView.text.isNullOrBlank()) { + "请输入下层管管径".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.areaView.text.isNullOrBlank()) { + "请输入所属区域".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.lineView.text.isNullOrBlank()) { + "请输入所属线路".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.roadView.text.isNullOrBlank()) { + "请输入所属道路".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.constructDateView.text.isNullOrBlank()) { + "请选择建设年代".show(activity) + return@setOnClickListener + } + + if (binding.objectInclude.ownerView.text.isNullOrBlank()) { + "请输入权属单位".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.identifierIdView.text.isNullOrBlank()) { + "请先读取标识器获取ID".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.identifierDeepView.text.isNullOrBlank()) { + "请输入标识器埋深".show(activity) + return@setOnClickListener + } + + if (binding.identifierInclude.personDeptView.text.isNullOrBlank()) { + "请输入标识器安装部门".show(activity) + return@setOnClickListener + } + + //先存本地再上传服务器 + saveLabelInLocal() + + isInstallMarker = true + activity.taskViewModel.installLabel( + companyId, + binding.objectInclude.objectTypeSpinner.selectedItem.toString().toObjectType(), + binding.objectInclude.markerObjectTypeView.text.toString(), + binding.objectInclude.materialSpinner.selectedItem.toString(), + "${binding.objectInclude.pipelineDiameterView.text}mm", + "${binding.objectInclude.buryDeepView.text}mm", + binding.objectInclude.downPipeTypeSpinner.selectedItem.toString(), + binding.objectInclude.downPipeMaterialSpinner.selectedItem.toString(), + "${binding.objectInclude.downPipeDiameterView.text}mm", + "${binding.objectInclude.downPointDeepView.text}mm", + binding.objectInclude.buryMethodSpinner.selectedItem.toString(), + binding.objectInclude.areaView.text.toString(), + binding.objectInclude.lineView.text.toString(), + binding.objectInclude.roadView.text.toString(), + binding.objectInclude.constructDateView.text.toString(), + binding.objectInclude.ownerView.text.toString(), + objectId, + binding.identifierInclude.identifierIdView.text.toString(), + binding.identifierInclude.identifierTypeSpinner.selectedItem.toString(), + "${binding.identifierInclude.identifierDeepView.text}mm", + binding.identifierInclude.personDeptView.text.toString(), + binding.identifierInclude.installTimeView.text.toString(), + binding.identifierInclude.lngView.text.toString(), + binding.identifierInclude.latView.text.toString(), + binding.identifierInclude.colorSpinner.selectedItem.toString().toColor(), + binding.remarkView.text.toString(), + realPaths + ) + + //保存默认值 + "markerObjectTypeView".setDefaultValue(binding.objectInclude.markerObjectTypeView.text.toString()) + "pipelineDiameterView".setDefaultValue(binding.objectInclude.pipelineDiameterView.text.toString()) + "buryDeepView".setDefaultValue(binding.objectInclude.buryDeepView.text.toString()) + "downPipeDiameterView".setDefaultValue(binding.objectInclude.downPipeDiameterView.text.toString()) + "downPointDeepView".setDefaultValue(binding.objectInclude.downPointDeepView.text.toString()) + "areaView".setDefaultValue(binding.objectInclude.areaView.text.toString()) + "lineView".setDefaultValue(binding.objectInclude.lineView.text.toString()) + "roadView".setDefaultValue(binding.objectInclude.roadView.text.toString()) + "ownerView".setDefaultValue(binding.objectInclude.ownerView.text.toString()) + "identifierDeepView".setDefaultValue(binding.identifierInclude.identifierDeepView.text.toString()) + "personDeptView".setDefaultValue(binding.identifierInclude.personDeptView.text.toString()) + } + + //读标识器 + binding.readMarkerButton.setOnClickListener { + LoadingDialogHub.show(activity, "标识器读取中,请稍后...") + + isReadMarker = true + + activity.soundPool.play(activity.readMarkerSoundResourceId, 1f, 1f, 0, -1, 1f) + + activity.serialPortManager.searchMarkerSignal() + } + } + + private fun initDefaultData() { + binding.titleInclude.titleView.text = "安装新标识器" + binding.titleInclude.titleView.setTextColor(R.color.themeColor.convertColor(activity)) + + imageAdapter = EditableImageAdapter(activity, 3, 3) + binding.cameraInclude.addImageRecyclerView.adapter = imageAdapter + + //设置默认值 + binding.objectInclude.markerObjectTypeView.setText("markerObjectTypeView".getDefaultValue()) + binding.objectInclude.pipelineDiameterView.setText("pipelineDiameterView".getDefaultValue()) + binding.objectInclude.buryDeepView.setText("buryDeepView".getDefaultValue()) + binding.objectInclude.downPipeDiameterView.setText("downPipeDiameterView".getDefaultValue()) + binding.objectInclude.downPointDeepView.setText("downPointDeepView".getDefaultValue()) + binding.objectInclude.areaView.setText("areaView".getDefaultValue()) + binding.objectInclude.lineView.setText("lineView".getDefaultValue()) + binding.objectInclude.roadView.setText("roadView".getDefaultValue()) + binding.objectInclude.ownerView.setText("ownerView".getDefaultValue()) + binding.identifierInclude.identifierDeepView.setText("identifierDeepView".getDefaultValue()) + binding.identifierInclude.personDeptView.setText("personDeptView".getDefaultValue()) + + /**************************************************************************************/ + binding.objectInclude.objectTypeSpinner.show( + activity, LocaleConstant.POINT_TYPE_ARRAY, 0 + ) + binding.objectInclude.materialSpinner.show( + activity, LocaleConstant.PIPE_MATERIAL_ARRAY, 0 + ) + binding.objectInclude.downPipeTypeSpinner.show( + activity, LocaleConstant.DOWN_PIPE_TYPE_ARRAY, 0 + ) + binding.objectInclude.downPipeMaterialSpinner.show( + activity, LocaleConstant.PIPE_MATERIAL_ARRAY, 0 + ) + binding.objectInclude.buryMethodSpinner.show( + activity, LocaleConstant.BURY_METHOD_ARRAY, 0 + ) + + binding.objectInclude.constructDateView.setOnClickListener { + val datePicker = DatePickerDialog( + activity, null, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH) + ) + datePicker.show() + + datePicker.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener { + val year = datePicker.datePicker.year + val month = datePicker.datePicker.month + 1 + val day = datePicker.datePicker.dayOfMonth + val selectedDate = String.format( + "%s-%s-%s", year, month.appendZero(), day.appendZero() + ) + + //当前时间 + val current = System.currentTimeMillis().timestampToTime() + val today = "$selectedDate $current".dateToTimestamp() + if (Date(today).after(Date())) { + "建设年代不能早于当前日期".show(activity) + } else { + datePicker.dismiss() + binding.objectInclude.constructDateView.text = selectedDate + } + } + } + + binding.identifierInclude.identifierTypeSpinner.show( + activity, LocaleConstant.IDENTIFIER_TYPE_ARRAY, 0 + ) + + binding.identifierInclude.installTimeView.text = + System.currentTimeMillis().timestampToCompleteDate() + activity.locationHub.getCurrentLocation(true, object : ILocationListener { + override fun onAMapLocationGet(location: AMapLocation?) { + if (location != null) { + binding.identifierInclude.lngView.text = location.longitude.toString() + binding.identifierInclude.latView.text = location.latitude.toString() + } else { + "当前位置信号差,无法获取定位".show(activity) + } + } + }) + + binding.identifierInclude.colorSpinner.show(activity, LocaleConstant.COLOR_ARRAY, 0) + + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + takePicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(activity) + } else { + activity.navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imageAdapter.deleteImage(position) + } + }) + /**************************************************************************************/ + } + + private fun saveLabelInLocal() { + val objectId = SaveKeyValues.getValue(LocaleConstant.OBJECT_ID, "") as String + + val labelBean = LabelBean() + labelBean.objectType = binding.objectInclude.objectTypeSpinner.selectedItem.toString() + labelBean.pipelineType = binding.objectInclude.markerObjectTypeView.text.toString() + labelBean.pipelineMaterial = + binding.objectInclude.materialSpinner.selectedItem.toString() + labelBean.pipelineDiameter = "${binding.objectInclude.pipelineDiameterView.text}mm" + labelBean.buryDeep = "${binding.objectInclude.buryDeepView.text}mm" + labelBean.lowerType = binding.objectInclude.downPipeTypeSpinner.selectedItem.toString() + labelBean.lowerMaterial = + binding.objectInclude.downPipeMaterialSpinner.selectedItem.toString() + labelBean.lowerDiameter = "${binding.objectInclude.downPipeDiameterView.text}mm" + labelBean.lowerDeep = "${binding.objectInclude.downPointDeepView.text}mm" + labelBean.buryMethod = binding.objectInclude.buryMethodSpinner.selectedItem.toString() + labelBean.area = binding.objectInclude.areaView.text.toString() + labelBean.line = binding.objectInclude.lineView.text.toString() + labelBean.road = binding.objectInclude.roadView.text.toString() + labelBean.constructTime = binding.objectInclude.constructDateView.text.toString() + labelBean.owner = binding.objectInclude.ownerView.text.toString() + labelBean.objectId = objectId + labelBean.identifierId = binding.identifierInclude.identifierIdView.text.toString() + labelBean.identifierType = + binding.identifierInclude.identifierTypeSpinner.selectedItem.toString() + labelBean.identifierDeep = "${binding.identifierInclude.identifierDeepView.text}mm" + labelBean.person = binding.identifierInclude.personDeptView.text.toString() + labelBean.installTime = binding.identifierInclude.installTimeView.text.toString() + labelBean.lng = binding.identifierInclude.lngView.text.toString() + labelBean.lat = binding.identifierInclude.latView.text.toString() + labelBean.color = + binding.identifierInclude.colorSpinner.selectedItem.toString().toColor() + labelBean.remark = binding.remarkView.text.toString() + labelBean.imagePath = realPaths.toJson() + + DataBaseManager.get.insertTaskLabel(labelBean) + } + + private fun takePicture() { + PictureSelector.create(activity).openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: java.util.ArrayList?) { + if (result == null) { + "拍照失败,请重试".show(activity) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 + result.realPath.compressImage(activity, object : OnImageCompressListener { + override fun onSuccess(file: File) { + realPaths.add(file.absolutePath) + imageAdapter.setupImage(realPaths) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + } + + /**探测标识器对话框******************************************************************************/ + class SearchMarkerDialog(private val activity: MainActivity) : Dialog(activity) { + + val binding: DialogSearchMarkerBinding by binding() + val degreeCache by lazy { HashMap() } + var isDetectMarker = false + var isDetectDepth = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.initDialogLayoutParams(1f) + + isDetectMarker = true + activity.serialPortManager.detectMarker() + + //角度 + degreeCache["lastDegree"] = 0f + + binding.depthButton.setOnClickListener { + val result = DataBaseManager.get.queryLabelById(activity.markerId) + if (result.isNotEmpty()) { + val tag = when (result.first().identifierType) { + "EM30" -> '7' + "EM50" -> '8' + "EM14" -> '9' + else -> '1' + } + if (tag == '1') { + "此标识器无法读取埋深!".show(activity) + } else { + // 发送读取标识器埋设深度指令 + isDetectDepth = true + LoadingDialogHub.show(activity, "正在测距,请稍后...") + activity.serialPortManager.detectDepth(tag) + object : CountDownTimer(15 * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + isDetectDepth = false + LoadingDialogHub.dismiss() + "请重试".show(activity) + } + }.start() + } + } else { + "标识器未安装,安装成功后才可读取埋深!".show(activity) + } + } + + binding.markerInfoButton.setOnClickListener { + //查库 + val result = DataBaseManager.get.queryLabelById(activity.markerId) + if (result.isNotEmpty()) { + activity.navigatePageTo(result.first().toJson()) + } else { + activity.navigatePageTo(activity.markerId) + } + } + } + + override fun dismiss() { + isDetectMarker = false + activity.soundPool.stop(activity.slowSoundResourceId) + activity.soundPool.stop(activity.fastSoundResourceId) + super.dismiss() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 426aed3..3d8bb94 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -38,7 +38,7 @@ private val degreeCache by lazy { HashMap() } private val locationHub by lazy { LocationHub(this) } private val audioAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_MEDIA) + .setUsage(AudioAttributes.USAGE_ALARM) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build() private lateinit var taskViewModel: TaskViewModel diff --git a/app/src/main/res/layout/dialog_install_marker.xml b/app/src/main/res/layout/dialog_install_marker.xml new file mode 100644 index 0000000..65c2262 --- /dev/null +++ b/app/src/main/res/layout/dialog_install_marker.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +