Newer
Older
Endoscope / app / src / main / java / com / casic / endoscope / view / MainActivity.kt
package com.casic.endoscope.view

import android.annotation.SuppressLint
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.content.Intent
import android.graphics.Color
import android.graphics.PixelFormat
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Message
import android.provider.Settings
import android.util.Log
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.casic.endoscope.R
import com.casic.endoscope.databinding.ActivityMainBinding
import com.casic.endoscope.extensions.check
import com.casic.endoscope.extensions.convertValue
import com.casic.endoscope.extensions.createHorizontalCommand
import com.casic.endoscope.extensions.createVerticalCommand
import com.casic.endoscope.extensions.getChannel
import com.casic.endoscope.extensions.init
import com.casic.endoscope.extensions.toTime
import com.casic.endoscope.service.VideoTranscodeService
import com.casic.endoscope.utils.DataBaseManager
import com.casic.endoscope.utils.FileManager
import com.casic.endoscope.utils.ProjectConstant
import com.casic.endoscope.utils.ble.BleDeviceManager
import com.casic.endoscope.utils.ble.OnDeviceConnectListener
import com.casic.endoscope.utils.ble.OnDeviceDiscoveredListener
import com.casic.endoscope.utils.hk.MessageCodeHub
import com.casic.endoscope.utils.hk.SDKGuider
import com.casic.endoscope.widgets.AlertControlDialog
import com.casic.endoscope.widgets.BluetoothDeviceDialog
import com.casic.endoscope.widgets.LineChartMarkerView
import com.casic.endoscope.widgets.PreviewImageDialog
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
import com.gyf.immersionbar.ImmersionBar
import com.hikvision.netsdk.HCNetSDK
import com.hikvision.netsdk.NET_DVR_JPEGPARA
import com.hikvision.netsdk.NET_DVR_PREVIEWINFO
import com.hikvision.netsdk.NET_DVR_SERIALSTART_V40
import com.hikvision.netsdk.PTZCommand
import com.pengxh.kt.lite.adapter.NormalRecyclerAdapter
import com.pengxh.kt.lite.adapter.ViewHolder
import com.pengxh.kt.lite.base.KotlinBaseActivity
import com.pengxh.kt.lite.divider.RecyclerViewItemOffsets
import com.pengxh.kt.lite.extensions.dp2px
import com.pengxh.kt.lite.extensions.getStatusBarHeight
import com.pengxh.kt.lite.extensions.navigatePageTo
import com.pengxh.kt.lite.extensions.show
import com.pengxh.kt.lite.extensions.timestampToTime
import com.pengxh.kt.lite.extensions.toJson
import com.pengxh.kt.lite.extensions.writeToFile
import com.pengxh.kt.lite.utils.LoadingDialogHub
import com.pengxh.kt.lite.utils.WeakReferenceHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.Timer
import java.util.TimerTask


@SuppressLint("all")
class MainActivity : KotlinBaseActivity<ActivityMainBinding>(), SurfaceHolder.Callback,
    Handler.Callback {

    private val kTag = "MainActivity"
    private val context = this@MainActivity
    private val hkSDK by lazy { HCNetSDK.getInstance() }
    private val bleDeviceManager by lazy { BleDeviceManager(this) }
    private val timeFormat by lazy { SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA) }
    private val marginOffset by lazy { 10.dp2px(this) }
    private val selectedSpeed = 7
    private val messageCode = 2024021901
    private val requestManageFilesAccess = 2024041301
    private val bluetoothDevices = ArrayList<BluetoothDevice>()
    private val xAxisLabels = ArrayList<String>()
    private val densityEntries = ArrayList<Entry>()
    private val lineDataSets = ArrayList<ILineDataSet>()
    private val recyclerViewImages = ArrayList<String>()

    //趋势线起点X坐标
    private var i = 0
    private var isConnected = false
    private var clickTime = 0L
    private var previewHandle = -1
    private var selectChannel = -1
    private var returnUserId = -1
    private var aChannelNum = 0
    private var startAChannel = 0
    private var dChannelNum = 0
    private var startDChannel = 0
    private var isPreviewSuccess = false

    //手指是否已经从方向控制盘抬起
    private var isActionUp = true

    //焦距按钮是否已松开
    private var isScaleButtonUp = true

    private var timer: Timer? = null
    private var timerTask: TimerTask? = null
    private var seconds = 0L
    private var videoPath = ""
    private lateinit var weakReferenceHandler: WeakReferenceHandler
    private lateinit var serviceIntent: Intent
    private lateinit var dataSet: LineDataSet
    private lateinit var lineData: LineData
    private lateinit var imageAdapter: NormalRecyclerAdapter<String>

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

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == requestManageFilesAccess) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                FileManager.getRootDirectory()
            }
        }
    }

    override fun initOnCreate(savedInstanceState: Bundle?) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            if (!Environment.isExternalStorageManager()) {
                val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
                intent.data = Uri.parse("package:${packageName}")
                startActivityForResult(intent, requestManageFilesAccess)
            } else {
                FileManager.getRootDirectory()
            }
        }

        //显示数据
        weakReferenceHandler = WeakReferenceHandler(this)
        weakReferenceHandler.sendEmptyMessage(messageCode)

        serviceIntent = Intent(this, VideoTranscodeService::class.java)

        //初始化浓度趋势折线图和Marker
        binding.lineChart.init(this)
        val markerView = LineChartMarkerView(this)
        markerView.chartView = binding.lineChart
        markerView.setXAxisDate(xAxisLabels)
        binding.lineChart.marker = markerView
    }

    override fun onResume() {
        super.onResume()
        //启动视频转码服务
        startService(serviceIntent)
    }

    override fun initEvent() {
        binding.openAlbumButton.setOnClickListener {
            if (isPreviewSuccess) {
                "设备预览画面中,无法切换".show(this)
                return@setOnClickListener
            }
            navigatePageTo<AlbumActivity>()
        }

        binding.searchBleButton.setOnClickListener {
            if (!bleDeviceManager.isBluetoothEnabled()) {
                bleDeviceManager.openBluetooth()
            }

            if (bleDeviceManager.isDiscovering()) {
                bleDeviceManager.stopDiscoverDevice()
            }

            if (isConnected) {
                bleDeviceManager.disConnectDevice()
            } else {
                LoadingDialogHub.show(this, "设备搜索中...")
                bleDeviceManager.startScanDevice(object : OnDeviceDiscoveredListener {
                    override fun onDeviceFound(device: BluetoothDevice) {
                        if (bluetoothDevices.isEmpty()) {
                            bluetoothDevices.add(device)
                        } else {
                            //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备
                            var judge = 0
                            for (temp in bluetoothDevices) {
                                if (temp.address == device.address) {
                                    judge = 1
                                    break
                                }
                            }
                            if (judge == 0) {
                                bluetoothDevices.add(device)
                            }
                        }
                    }

                    override fun onDeviceDiscoveryEnd() {
                        LoadingDialogHub.dismiss()
                        //显示搜索到设备列表
                        showScanResult()
                    }

                }, 10 * 1000)
            }
        }

        binding.outputDataView.setOnClickListener {
            val valueBeans = DataBaseManager.get.loadAll()
            //导出到本地文件
            val rootDirectory = FileManager.getRootDirectory()
            val file = File("${rootDirectory}/GasDensityValue.json")
            if (file.exists()) {
                file.delete()
            }
            valueBeans.toJson().writeToFile(file)
            "数据导出成功,请在设备文件管理根目录【Endoscope】查看".show(this)
        }

        binding.upScaleButton.setOnTouchListener { _, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    if (isPreviewSuccess && isScaleButtonUp) {
                        hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.ZOOM_IN, 0)
                        isScaleButtonUp = false
                    }
                }

                MotionEvent.ACTION_UP -> {
                    isScaleButtonUp = true
                    hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.ZOOM_IN, 1)
                }
            }
            false
        }

        binding.downScaleButton.setOnTouchListener { _, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    if (isPreviewSuccess && isScaleButtonUp) {
                        hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.ZOOM_OUT, 0)
                        isScaleButtonUp = false
                    }
                }

                MotionEvent.ACTION_UP -> {
                    isScaleButtonUp = true
                    hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.ZOOM_OUT, 1)
                }
            }
            false
        }

        binding.topButton.setOnTouchListener { _, motionEvent ->
            when (motionEvent.action) {
                MotionEvent.ACTION_DOWN -> {
                    if (isPreviewSuccess && isActionUp) {
                        hkSDK.NET_DVR_PTZControlWithSpeed(
                            previewHandle, PTZCommand.TILT_UP, 0, selectedSpeed
                        )
                        isActionUp = false
                    }
                }

                MotionEvent.ACTION_UP -> {
                    hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.TILT_UP, 1)
                    isActionUp = true
                }
            }
            false
        }

        binding.centerButton.setOnClickListener {
            if (!isPreviewSuccess) {
                val deviceItem = SDKGuider.sdkGuider.devManageGuider.DeviceItem()
                deviceItem.szDevName = ""
                deviceItem.devNetInfo = SDKGuider.sdkGuider.devManageGuider.DevNetInfo(
                    ProjectConstant.HK_NET_IP,
                    ProjectConstant.HK_NET_PORT,
                    ProjectConstant.HK_NET_USERNAME,
                    ProjectConstant.HK_NET_PASSWORD
                )
                if (deviceItem.szDevName.isEmpty()) {
                    deviceItem.szDevName = deviceItem.devNetInfo.szIp
                }
                val loginV40Jna = SDKGuider.sdkGuider.devManageGuider.login_v40_jna(
                    deviceItem.szDevName, deviceItem.devNetInfo
                )
                if (loginV40Jna) {
                    //配置设备通道
                    try {
                        val deviceInfo = SDKGuider.sdkGuider.devManageGuider.devList[0]
                        returnUserId = deviceInfo.szUserId

                        aChannelNum =
                            deviceInfo.deviceInfoV40_jna.struDeviceV30.byChanNum.toInt()
                        startAChannel =
                            deviceInfo.deviceInfoV40_jna.struDeviceV30.byStartChan.toInt()

                        dChannelNum =
                            deviceInfo.deviceInfoV40_jna.struDeviceV30.byIPChanNum + deviceInfo.deviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256
                        startDChannel =
                            deviceInfo.deviceInfoV40_jna.struDeviceV30.byStartChan.toInt()

                        var iAnalogStartChan = startAChannel
                        var iDigitalStartChan = startDChannel

                        val channelList = ArrayList<String>()

                        for (i in 0 until aChannelNum) {
                            channelList.add("ACamera_$iAnalogStartChan")
                            iAnalogStartChan++
                        }

                        for (i in 0 until dChannelNum) {
                            channelList.add("DCamera_$iDigitalStartChan")
                            iDigitalStartChan++
                        }
                        selectChannel = Integer.valueOf(channelList[0].getChannel())

                        val streamList = ArrayList<String>()
                        streamList.add("main_stream")
                        streamList.add("sub_stream")
                        streamList.add("third_stream")

                        //开始预览
                        if (previewHandle != -1) {
                            SDKGuider.sdkGuider.devPreviewGuider.RealPlay_Stop_jni(previewHandle)
                        }
                        val strutPlayInfo = NET_DVR_PREVIEWINFO()
                        strutPlayInfo.lChannel = selectChannel
                        strutPlayInfo.dwStreamType = 1
                        strutPlayInfo.bBlocked = 1
                        strutPlayInfo.hHwnd = binding.surfaceView.holder
                        previewHandle = SDKGuider.sdkGuider.devPreviewGuider.RealPlay_V40_jni(
                            returnUserId, strutPlayInfo, null
                        )
                        if (previewHandle < 0) {
                            Log.d(kTag, "initEvent: Err:${MessageCodeHub.getErrorCode()}")
                            return@setOnClickListener
                        }
                        isPreviewSuccess = true
                        "预览开启成功".show(context)
                        //开始计时
                        timer = Timer()
                        timerTask = object : TimerTask() {
                            override fun run() {
                                seconds++
                                lifecycleScope.launch(Dispatchers.Main) {
                                    binding.runningTimeView.text = seconds.toTime()
                                }
                            }
                        }
                        timer?.schedule(timerTask, 0, 1000)
                    } catch (e: IndexOutOfBoundsException) {
                        e.printStackTrace()
                        "设备未正常连接,无法开启预览".show(context)
                    }
                } else {
                    "设备登陆失败".show(context)
                }
            } else {
                if (!SDKGuider.sdkGuider.devPreviewGuider.RealPlay_Stop_jni(previewHandle)) {
                    return@setOnClickListener
                }
                previewHandle = -1
                isPreviewSuccess = false
                "预览关闭成功".show(context)
                //停止计时
                timerTask?.cancel()
                timer?.cancel()
            }
        }

        binding.bottomButton.setOnTouchListener { _, motionEvent ->
            when (motionEvent.action) {
                MotionEvent.ACTION_DOWN -> {
                    if (isPreviewSuccess && isActionUp) {
                        hkSDK.NET_DVR_PTZControlWithSpeed(
                            previewHandle, PTZCommand.TILT_DOWN, 0, selectedSpeed
                        )
                        isActionUp = false
                    }
                }

                MotionEvent.ACTION_UP -> {
                    hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.TILT_DOWN, 1)
                    isActionUp = true
                }
            }
            false
        }

        //单张拍照
        binding.imageButton.setOnClickListener {
            if (isPreviewSuccess) {
                binding.imageButton.isEnabled = false

                val strJpeg = NET_DVR_JPEGPARA()
                strJpeg.wPicQuality = 1
                strJpeg.wPicSize = 2

                val imagePath = "/${FileManager.getImageFileDir()}/${timeFormat.format(Date())}.png"
                hkSDK.NET_DVR_CaptureJPEGPicture(
                    returnUserId, selectChannel, strJpeg, imagePath
                )

                if (MessageCodeHub.getErrorCode() == 0) {
                    "画面抓取成功".show(this)
                    binding.imageButton.isEnabled = true
                    //刷新右侧预览界面
                    recyclerViewImages.add(imagePath)
                    imageAdapter.notifyDataSetChanged()
                }
            } else {
                "摄像头预览未打开,无法拍照".show(this)
            }
        }

        //视频录制
        binding.videoButton.setOnClickListener {
            if (isPreviewSuccess) {
                videoPath = "/${FileManager.getVideoFileDir()}/${timeFormat.format(Date())}.mp4"
                hkSDK.NET_DVR_SaveRealData(returnUserId, videoPath)
                binding.videoStateView.visibility = View.VISIBLE
            } else {
                "摄像头预览未打开,无法录制视频".show(this)
            }
        }

        binding.videoButton.setOnLongClickListener {
            //停止视频抓取
            hkSDK.NET_DVR_StopSaveRealData(returnUserId)
            binding.videoStateView.visibility = View.INVISIBLE
            ProjectConstant.VIDEO_PATH_STACK.push(videoPath)
            "视频录制成功".show(this)
            true
        }

        binding.resetButton.setOnClickListener {
            //建立透明通道
            val serialStart = NET_DVR_SERIALSTART_V40()
            //串口类型:1- 232 串口,2- 485 串口
            serialStart.dwSerialPort = 2
            //串口编号
            serialStart.wPort = 0
            val serialHandle = hkSDK.NET_DVR_SerialStart_V40(returnUserId, serialStart)
            { _, _, _, _ -> }
            //链式执行
            lifecycleScope.launch(Dispatchers.IO) {
                flow {
                    //向透明通道发送数据,水平
                    val horizontalCommand = 60.createHorizontalCommand()
                    val serialSend = hkSDK.NET_DVR_SerialSend(
                        serialHandle, 1, horizontalCommand, horizontalCommand.size
                    )
                    delay(500)
                    emit(serialSend)
                }.onCompletion {
                    //关闭通道
                    hkSDK.NET_DVR_SerialStop(serialHandle)
                }.collect { value ->
                    if (value) {
                        //向透明通道发送数据,垂直
                        val verticalCommand = 0.createVerticalCommand()
                        val serialSend = hkSDK.NET_DVR_SerialSend(
                            serialHandle, 1, verticalCommand, verticalCommand.size
                        )
                        if (!serialSend) {
                            withContext(Dispatchers.Main) {
                                "垂直角度指令执行失败".show(context)
                            }
                        }
                        delay(500)
                    } else {
                        withContext(Dispatchers.Main) {
                            "水平角度指令执行失败".show(context)
                        }
                    }
                }
            }
        }
    }

    override fun handleMessage(msg: Message): Boolean {
        if (msg.what == messageCode) {
            //绑定数据
            imageAdapter = object : NormalRecyclerAdapter<String>(
                R.layout.item_image_rv_g, recyclerViewImages
            ) {
                override fun convertView(viewHolder: ViewHolder, position: Int, item: String) {
                    viewHolder.setImageResource(R.id.imageView, item)
                    viewHolder.setOnLongClickListener(R.id.imageView) {
                        AlertControlDialog.Builder()
                            .setContext(context)
                            .setTitle("温馨提示")
                            .setMessage("是否保存此图片?")
                            .setNegativeButton("取消")
                            .setPositiveButton("确定")
                            .setOnDialogButtonClickListener(object :
                                AlertControlDialog.OnDialogButtonClickListener {
                                override fun onConfirmClick() {

                                }

                                override fun onCancelClick() {
                                    //删除图片
                                    recyclerViewImages.remove(item)
                                    File(item).delete()
                                    imageAdapter.notifyDataSetChanged()
                                }
                            }).build().show()
                        true
                    }

                    viewHolder.setOnClickListener(R.id.imageView) {
                        //查看大图Dialog
                        PreviewImageDialog.Builder()
                            .setContext(context)
                            .setImagePath(item)
                            .build()
                            .show()
                    }
                }
            }
            binding.recyclerView.addItemDecoration(
                RecyclerViewItemOffsets(marginOffset, marginOffset, marginOffset, marginOffset)
            )
            binding.recyclerView.adapter = imageAdapter
        }
        return true
    }

    private fun showScanResult() {
        BluetoothDeviceDialog.Builder().setContext(this).setDeviceArray(bluetoothDevices)
            .setNegativeButton("取消").setOnDialogButtonClickListener(object :
                BluetoothDeviceDialog.OnDialogButtonClickListener {
                override fun onItemClick(position: Int) {
                    //连接点击的设备
                    startConnectDevice(bluetoothDevices[position])
                }

                override fun onCancelClick() {

                }
            }).build().show()
    }

    private fun startConnectDevice(device: BluetoothDevice) {
        // 当前蓝牙设备
        if (!isConnected) {
            LoadingDialogHub.show(this, "正在连接...")
            bleDeviceManager.connectBleDevice(
                device, ProjectConstant.SERVICE_UUID, 10 * 1000, bleConnectListener
            )
        } else {
            bleDeviceManager.disConnectDevice()
        }
    }

    override fun observeRequestState() {

    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        binding.surfaceView.holder.setFormat(PixelFormat.TRANSLUCENT)
        if (-1 == previewHandle) {
            return
        }
        val surface = holder.surface
        if (surface.isValid) {
            if (-1 == SDKGuider.sdkGuider.devPreviewGuider.RealPlaySurfaceChanged_jni(
                    previewHandle, 0, holder
                )
            ) {
                Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}")
            }
        }
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {

    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        if (-1 == previewHandle) {
            return
        }
        if (holder.surface.isValid) {
            if (-1 == SDKGuider.sdkGuider.devPreviewGuider.RealPlaySurfaceChanged_jni(
                    previewHandle, 0, null
                )
            ) {
                Log.d(kTag, "surfaceDestroyed: ${MessageCodeHub.getErrorCode()}")
            }
        }
    }

    override fun setupTopBarLayout() {
        ImmersionBar.with(this).statusBarDarkFont(false).init()
        //根据不同设备状态栏高度设置statusBarView高度
        val statusBarHeight = getStatusBarHeight()
        binding.rootView.setPadding(0, statusBarHeight, 0, 0)
        binding.rootView.requestLayout()
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (System.currentTimeMillis() - clickTime > 2000) {
                stopService(serviceIntent)
                "再按一次退出应用".show(this)
                clickTime = System.currentTimeMillis()
                return true
            } else {
                super.onKeyDown(keyCode, event)
            }
        }
        return super.onKeyDown(keyCode, event)
    }

    /***低功耗蓝牙********************************************/
    private val bleConnectListener = object : OnDeviceConnectListener {
        override fun onConnecting(bluetoothGatt: BluetoothGatt?) {
            Log.d(kTag, "onConnecting => ")
        }

        override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) {
            Log.d(kTag, "onConnectSuccess => $status")
        }

        override fun onConnectFailure(
            bluetoothGatt: BluetoothGatt?, exception: String, status: Int
        ) {
            Log.d(kTag, "onConnectFailure => $status")
            isConnected = false
        }

        override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) {
            Log.d(kTag, "onDisConnecting => ")
        }

        override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) {
            lifecycleScope.launch(Dispatchers.Main) {
                Log.d(kTag, "onDisConnectSuccess => $status")
                isConnected = false
                binding.searchBleButton.text = "搜索蓝牙"
                binding.stateView.text = "未连接"
                binding.stateView.setTextColor(Color.RED)
            }
        }

        override fun onServiceDiscoverySuccess(bluetoothGatt: BluetoothGatt?, status: Int) {
            lifecycleScope.launch(Dispatchers.Main) {
                Log.d(kTag, "onServiceDiscoverySuccess => $status")
                LoadingDialogHub.dismiss()
                "连接成功".show(context)
                isConnected = true
                binding.searchBleButton.text = "断开连接"
                binding.stateView.text = "已连接"
                binding.stateView.setTextColor(Color.GREEN)
            }
        }

        override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String) {
            Log.d(kTag, "onServiceDiscoveryFailed => ")
            isConnected = false
        }

        override fun onReceiveMessage(bluetoothGatt: BluetoothGatt?, value: ByteArray) {
            //[97, 97, 32, 48, 49, 32, 48, 48, 32, 48, 48, 32, 48, 48, 32, 48, 100, 32, 53, 53, 32]
            if (value.check()) {
                /***将值渲染到曲线图****************************************************************/
                lifecycleScope.launch(Dispatchers.Main) {
                    val currentTime = System.currentTimeMillis().timestampToTime()
                    //解析返回值
                    val density = value.convertValue()
                    withContext(Dispatchers.IO) {
                        //协程切换为子线程将数据保存在数据库
                        DataBaseManager.get.saveGasDensity(density, currentTime)
                    }
                    //时间作为X轴
                    xAxisLabels.add(currentTime)

                    //浓度作为Y轴
                    val entry = Entry(i++.toFloat(), density.toFloat(), "浓度")
                    densityEntries.add(entry)

                    //转化为控件需要的Data
                    dataSet = LineDataSet(densityEntries, "")
                    dataSet.setDrawCircles(false)
                    dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER
                    //线条颜色
                    dataSet.color = Color.RED
                    lineDataSets.add(dataSet)
                    lineData = LineData(lineDataSets)
                    lineData.setDrawValues(false)

                    //解决折线点太多导致卡顿问题
                    if (lineData.entryCount > 50) {
                        lineData.removeDataSet(0)
                        lineData.notifyDataChanged()
                    }
                    binding.lineChart.data = lineData
                    binding.lineChart.invalidate()
                }
            } else {
                Log.d(kTag, "onReceiveMessage: 数据校验失败")
            }
        }

        override fun onReceiveError(errorMsg: String) {
            Log.d(kTag, "onReceiveError => $errorMsg")
        }

        override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) {
            Log.d(kTag, "onWriteSuccess => ${msg.contentToString()}")
        }

        override fun onWriteFailed(bluetoothGatt: BluetoothGatt?, errorMsg: String) {
            Log.d(kTag, "onWriteFailed => $errorMsg")
        }

        override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) {
            Log.d(kTag, "onReadRssi => $rssi")
        }
    }
}