diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt index c5f18a6..b3dce3c 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt @@ -1,26 +1,19 @@ package com.casic.common.detector.gd.view import android.app.DatePickerDialog -import android.content.ComponentName -import android.content.Context import android.content.DialogInterface -import android.content.Intent -import android.content.ServiceConnection import android.hardware.Camera import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message -import android.util.Log import android.view.View import android.widget.AdapterView import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.common.detector.gd.R import com.casic.common.detector.gd.adapter.EditableImageAdapter +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.callback.OnImageCompressListener @@ -33,18 +26,15 @@ import com.casic.common.detector.gd.extensions.toColor import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toObjectType -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel 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.dateToTimestamp import com.pengxh.kt.lite.extensions.navigatePageTo @@ -55,13 +45,12 @@ import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import java.io.File import java.util.Calendar import java.util.Date -class InstallMarkerActivity : KotlinBaseActivity(), Handler.Callback { +class InstallMarkerActivity : SerialPortActivity() { private val kTag = "InstallMarkerActivity" private val context = this @@ -73,7 +62,6 @@ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build() private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build() private var soundResourceId = 0 - private var serialPortService: SerialPortService? = null private lateinit var imageAdapter: EditableImageAdapter override fun initEvent() { @@ -255,18 +243,18 @@ //读标识器 binding.readMarkerButton.setOnClickListener { + openSerialPort() LoadingDialog.show(this, "标识器读取中,请稍后...") countDownTimer.start() binding.readMarkerButton.isEnabled = false - RuntimeCache.currentSegment = CurrentSegment.InstallMarker - serialPortService?.writeCommand('2') + readMarkerId() } } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.INSTALL_MARKER) { - val buffer = msg.obj as ByteArray - if ((buffer.first().toInt() and 0xFF).toString(16).uppercase() == "30") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.InstallMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { val markerId = buffer.toMarkerId() LoadingDialog.dismiss() countDownTimer.cancel() @@ -274,7 +262,6 @@ binding.identifierInclude.identifierIdView.text = markerId } } - return true } /** @@ -363,37 +350,7 @@ }) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - //在连接正常关闭的情况下不会被调用, 只在Service被破坏了或者被杀死的时候调用 - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - locationKit.getCurrentLocation(true, object : OnGetLocationListener { override fun onSuccess(location: AMapLocation) { binding.identifierInclude.lngView.text = location.longitude.toString() @@ -552,8 +509,6 @@ override fun onDestroy() { super.onDestroy() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt index c5f18a6..b3dce3c 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt @@ -1,26 +1,19 @@ package com.casic.common.detector.gd.view import android.app.DatePickerDialog -import android.content.ComponentName -import android.content.Context import android.content.DialogInterface -import android.content.Intent -import android.content.ServiceConnection import android.hardware.Camera import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message -import android.util.Log import android.view.View import android.widget.AdapterView import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.common.detector.gd.R import com.casic.common.detector.gd.adapter.EditableImageAdapter +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.callback.OnImageCompressListener @@ -33,18 +26,15 @@ import com.casic.common.detector.gd.extensions.toColor import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toObjectType -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel 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.dateToTimestamp import com.pengxh.kt.lite.extensions.navigatePageTo @@ -55,13 +45,12 @@ import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import java.io.File import java.util.Calendar import java.util.Date -class InstallMarkerActivity : KotlinBaseActivity(), Handler.Callback { +class InstallMarkerActivity : SerialPortActivity() { private val kTag = "InstallMarkerActivity" private val context = this @@ -73,7 +62,6 @@ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build() private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build() private var soundResourceId = 0 - private var serialPortService: SerialPortService? = null private lateinit var imageAdapter: EditableImageAdapter override fun initEvent() { @@ -255,18 +243,18 @@ //读标识器 binding.readMarkerButton.setOnClickListener { + openSerialPort() LoadingDialog.show(this, "标识器读取中,请稍后...") countDownTimer.start() binding.readMarkerButton.isEnabled = false - RuntimeCache.currentSegment = CurrentSegment.InstallMarker - serialPortService?.writeCommand('2') + readMarkerId() } } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.INSTALL_MARKER) { - val buffer = msg.obj as ByteArray - if ((buffer.first().toInt() and 0xFF).toString(16).uppercase() == "30") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.InstallMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { val markerId = buffer.toMarkerId() LoadingDialog.dismiss() countDownTimer.cancel() @@ -274,7 +262,6 @@ binding.identifierInclude.identifierIdView.text = markerId } } - return true } /** @@ -363,37 +350,7 @@ }) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - //在连接正常关闭的情况下不会被调用, 只在Service被破坏了或者被杀死的时候调用 - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - locationKit.getCurrentLocation(true, object : OnGetLocationListener { override fun onSuccess(location: AMapLocation) { binding.identifierInclude.lngView.text = location.longitude.toString() @@ -552,8 +509,6 @@ override fun onDestroy() { super.onDestroy() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt index 8a798b6..2c48cc9 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt @@ -1,18 +1,12 @@ package com.casic.common.detector.gd.view import android.app.Activity -import android.content.ComponentName -import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.graphics.BitmapFactory import android.graphics.Color import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.View @@ -34,6 +28,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.casic.common.detector.gd.R import com.casic.common.detector.gd.base.BaseApplication +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.bean.TaskLocalBean import com.casic.common.detector.gd.callback.OnGetAddressListener @@ -47,32 +42,27 @@ import com.casic.common.detector.gd.model.TaskDetailLocalModel import com.casic.common.detector.gd.model.TaskModel import com.casic.common.detector.gd.service.NtripConnectService -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.ExcelKit -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType -import com.casic.common.detector.gd.utils.GpioManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit import com.casic.common.detector.gd.utils.RouteOnMap -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.MarkerDetailDialog import com.casic.common.detector.gd.widgets.QueryMarkerDialog import com.casic.common.detector.gd.widgets.SamplePopupWindow -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.dp2px 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.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog @@ -82,45 +72,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.concurrent.atomic.AtomicInteger -class MainActivity : KotlinBaseActivity(), AMap.OnMarkerClickListener, - OnMapLongClickListener, Handler.Callback { +class MainActivity : SerialPortActivity(), AMap.OnMarkerClickListener, + OnMapLongClickListener { private val kTag = "MainActivity" private val context = this - - /***************************************************************************************/ - private val gpioManager by lazy { GpioManager() } - - /** - * 读取数据 1 - * 暂停读取 0 - * */ - private val gpioState = AtomicInteger(0) - private fun openSerialPort() { - if (gpioState.get() != 1) { - gpioManager.setGpioHigh("18") - gpioState.set(1) - Log.d(kTag, "openSerialPort: 调高串口电位") - } else { - Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") - } - } - - private fun closeSerialPort() { - if (gpioState.get() == 0) { - Log.d(kTag, "closeSerialPort: 已经是低电位,直接返回") - return - } - // 降低串口电位 - gpioManager.setGpioLow("18") - gpioState.set(0) - Log.d(kTag, "closeSerialPort: 降低串口电位") - } - - /***************************************************************************************/ private val samplePopupWindow by lazy { SamplePopupWindow(this) } private val detailDialog by lazy { MarkerDetailDialog(this) } private val locationKit by lazy { LocationKit(this) } @@ -133,7 +91,6 @@ private var freeTaskTitle = "" private var ids = HashSet() private var freeTaskId: String? = null - private var serialPortService: SerialPortService? = null private var soundResourceId = 0 private var isExecuteTask = false private lateinit var aMap: AMap @@ -146,43 +103,13 @@ binding.rootView.initImmersionBar(this, false, R.color.themeColor) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - openSerialPort() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - //地图初始化 initMapConfig(savedInstanceState) - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - Intent(this, NtripConnectService::class.java).also { - startService(it) - } +// Intent(this, NtripConnectService::class.java).also { +// startService(it) +// } soundResourceId = soundPool.load(this, R.raw.ring3, 1) //右上角菜单 @@ -211,7 +138,7 @@ showMarkersOnMap() } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() } @@ -446,7 +373,7 @@ override fun onConfirmClick() { isFreeTask = false soundPool.autoPause() - RuntimeCache.currentSegment = null + closeSerialPort() binding.stopFreeTaskButton.visibility = View.GONE if (freeTaskId.isNullOrBlank()) { @@ -499,11 +426,7 @@ } else { "此工单下无标识器,将执行自由巡检任务".show(context) freeTaskId = task.taskId - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } } @@ -524,21 +447,22 @@ freeTaskTitle = value val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(context, userId, value) - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } override fun onCancelClick() {} }).build().show() } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.FREE_INSPECTION) { - val buffer = msg.obj as ByteArray - //只响一次,因为探测频率高,所以依旧是连续的报警声 + private fun detectMarker() { + binding.stopFreeTaskButton.visibility = View.VISIBLE + isFreeTask = true + openSerialPort() + searchMarker(CurrentSegment.FreeInspection) + } + + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.FreeInspection) { soundPool.play(soundResourceId, 1f, 1f, 0, 0, 1f) when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { "4E" -> { @@ -552,27 +476,30 @@ val markerId = buffer.toMarkerId() //添加地图Marker if (!ids.contains(markerId)) { - //根据markerId查询标识器经纬度 - val labels = DataBaseManager.get.queryMarkerById(markerId) - if (labels.isNotEmpty()) { - val bean = labels.first() - aMap.addMarker( - MarkerOptions() - .position(LatLng(bean.lat.toDouble(), bean.lng.toDouble())) - .icon(BitmapDescriptorFactory.fromResource(R.mipmap.label_blue1)) + val markers = DataBaseManager.get.queryMarkerById(markerId) + if (markers.isNotEmpty()) { + val marker = markers.first() + val latLng = LatLng( + marker.lat.toDouble(), marker.lng.toDouble() ) + val resource = BitmapDescriptorFactory.fromResource( + R.mipmap.label_blue1 + ) + val options = MarkerOptions().position(latLng).icon(resource) + aMap.addMarker(options) } } ids.add(markerId) //显示标识器详细信息 if (!detailDialog.isShowing) { - val markerBean = DataBaseManager.get.queryMarkerById(markerId).firstOrNull() - if (markerBean == null) { - "无法查询到此ID【${markerId}】的信息".show(context) - } else { - detailDialog.setMarker(markerBean) - detailDialog.show() + DataBaseManager.get.queryMarkerById(markerId).firstOrNull().apply { + if (this == null) { + "无法查询到此ID【${markerId}】的信息".show(context) + } else { + detailDialog.setMarker(this) + detailDialog.show() + } } } } @@ -582,7 +509,6 @@ } } } - return true } override fun observeRequestState() { @@ -865,9 +791,6 @@ override fun onResume() { super.onResume() binding.mapView.onResume() - if (serialPortService != null) { - openSerialPort() - } } override fun onPause() { @@ -885,8 +808,6 @@ binding.mapView.onDestroy() soundPool.autoPause() locationKit.stopLocation() - unbindService(serviceConnection) - closeSerialPort() BaseApplication.get().closeSerialPort() } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt index c5f18a6..b3dce3c 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt @@ -1,26 +1,19 @@ package com.casic.common.detector.gd.view import android.app.DatePickerDialog -import android.content.ComponentName -import android.content.Context import android.content.DialogInterface -import android.content.Intent -import android.content.ServiceConnection import android.hardware.Camera import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message -import android.util.Log import android.view.View import android.widget.AdapterView import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.common.detector.gd.R import com.casic.common.detector.gd.adapter.EditableImageAdapter +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.callback.OnImageCompressListener @@ -33,18 +26,15 @@ import com.casic.common.detector.gd.extensions.toColor import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toObjectType -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel 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.dateToTimestamp import com.pengxh.kt.lite.extensions.navigatePageTo @@ -55,13 +45,12 @@ import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import java.io.File import java.util.Calendar import java.util.Date -class InstallMarkerActivity : KotlinBaseActivity(), Handler.Callback { +class InstallMarkerActivity : SerialPortActivity() { private val kTag = "InstallMarkerActivity" private val context = this @@ -73,7 +62,6 @@ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build() private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build() private var soundResourceId = 0 - private var serialPortService: SerialPortService? = null private lateinit var imageAdapter: EditableImageAdapter override fun initEvent() { @@ -255,18 +243,18 @@ //读标识器 binding.readMarkerButton.setOnClickListener { + openSerialPort() LoadingDialog.show(this, "标识器读取中,请稍后...") countDownTimer.start() binding.readMarkerButton.isEnabled = false - RuntimeCache.currentSegment = CurrentSegment.InstallMarker - serialPortService?.writeCommand('2') + readMarkerId() } } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.INSTALL_MARKER) { - val buffer = msg.obj as ByteArray - if ((buffer.first().toInt() and 0xFF).toString(16).uppercase() == "30") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.InstallMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { val markerId = buffer.toMarkerId() LoadingDialog.dismiss() countDownTimer.cancel() @@ -274,7 +262,6 @@ binding.identifierInclude.identifierIdView.text = markerId } } - return true } /** @@ -363,37 +350,7 @@ }) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - //在连接正常关闭的情况下不会被调用, 只在Service被破坏了或者被杀死的时候调用 - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - locationKit.getCurrentLocation(true, object : OnGetLocationListener { override fun onSuccess(location: AMapLocation) { binding.identifierInclude.lngView.text = location.longitude.toString() @@ -552,8 +509,6 @@ override fun onDestroy() { super.onDestroy() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt index 8a798b6..2c48cc9 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt @@ -1,18 +1,12 @@ package com.casic.common.detector.gd.view import android.app.Activity -import android.content.ComponentName -import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.graphics.BitmapFactory import android.graphics.Color import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.View @@ -34,6 +28,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.casic.common.detector.gd.R import com.casic.common.detector.gd.base.BaseApplication +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.bean.TaskLocalBean import com.casic.common.detector.gd.callback.OnGetAddressListener @@ -47,32 +42,27 @@ import com.casic.common.detector.gd.model.TaskDetailLocalModel import com.casic.common.detector.gd.model.TaskModel import com.casic.common.detector.gd.service.NtripConnectService -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.ExcelKit -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType -import com.casic.common.detector.gd.utils.GpioManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit import com.casic.common.detector.gd.utils.RouteOnMap -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.MarkerDetailDialog import com.casic.common.detector.gd.widgets.QueryMarkerDialog import com.casic.common.detector.gd.widgets.SamplePopupWindow -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.dp2px 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.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog @@ -82,45 +72,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.concurrent.atomic.AtomicInteger -class MainActivity : KotlinBaseActivity(), AMap.OnMarkerClickListener, - OnMapLongClickListener, Handler.Callback { +class MainActivity : SerialPortActivity(), AMap.OnMarkerClickListener, + OnMapLongClickListener { private val kTag = "MainActivity" private val context = this - - /***************************************************************************************/ - private val gpioManager by lazy { GpioManager() } - - /** - * 读取数据 1 - * 暂停读取 0 - * */ - private val gpioState = AtomicInteger(0) - private fun openSerialPort() { - if (gpioState.get() != 1) { - gpioManager.setGpioHigh("18") - gpioState.set(1) - Log.d(kTag, "openSerialPort: 调高串口电位") - } else { - Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") - } - } - - private fun closeSerialPort() { - if (gpioState.get() == 0) { - Log.d(kTag, "closeSerialPort: 已经是低电位,直接返回") - return - } - // 降低串口电位 - gpioManager.setGpioLow("18") - gpioState.set(0) - Log.d(kTag, "closeSerialPort: 降低串口电位") - } - - /***************************************************************************************/ private val samplePopupWindow by lazy { SamplePopupWindow(this) } private val detailDialog by lazy { MarkerDetailDialog(this) } private val locationKit by lazy { LocationKit(this) } @@ -133,7 +91,6 @@ private var freeTaskTitle = "" private var ids = HashSet() private var freeTaskId: String? = null - private var serialPortService: SerialPortService? = null private var soundResourceId = 0 private var isExecuteTask = false private lateinit var aMap: AMap @@ -146,43 +103,13 @@ binding.rootView.initImmersionBar(this, false, R.color.themeColor) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - openSerialPort() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - //地图初始化 initMapConfig(savedInstanceState) - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - Intent(this, NtripConnectService::class.java).also { - startService(it) - } +// Intent(this, NtripConnectService::class.java).also { +// startService(it) +// } soundResourceId = soundPool.load(this, R.raw.ring3, 1) //右上角菜单 @@ -211,7 +138,7 @@ showMarkersOnMap() } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() } @@ -446,7 +373,7 @@ override fun onConfirmClick() { isFreeTask = false soundPool.autoPause() - RuntimeCache.currentSegment = null + closeSerialPort() binding.stopFreeTaskButton.visibility = View.GONE if (freeTaskId.isNullOrBlank()) { @@ -499,11 +426,7 @@ } else { "此工单下无标识器,将执行自由巡检任务".show(context) freeTaskId = task.taskId - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } } @@ -524,21 +447,22 @@ freeTaskTitle = value val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(context, userId, value) - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } override fun onCancelClick() {} }).build().show() } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.FREE_INSPECTION) { - val buffer = msg.obj as ByteArray - //只响一次,因为探测频率高,所以依旧是连续的报警声 + private fun detectMarker() { + binding.stopFreeTaskButton.visibility = View.VISIBLE + isFreeTask = true + openSerialPort() + searchMarker(CurrentSegment.FreeInspection) + } + + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.FreeInspection) { soundPool.play(soundResourceId, 1f, 1f, 0, 0, 1f) when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { "4E" -> { @@ -552,27 +476,30 @@ val markerId = buffer.toMarkerId() //添加地图Marker if (!ids.contains(markerId)) { - //根据markerId查询标识器经纬度 - val labels = DataBaseManager.get.queryMarkerById(markerId) - if (labels.isNotEmpty()) { - val bean = labels.first() - aMap.addMarker( - MarkerOptions() - .position(LatLng(bean.lat.toDouble(), bean.lng.toDouble())) - .icon(BitmapDescriptorFactory.fromResource(R.mipmap.label_blue1)) + val markers = DataBaseManager.get.queryMarkerById(markerId) + if (markers.isNotEmpty()) { + val marker = markers.first() + val latLng = LatLng( + marker.lat.toDouble(), marker.lng.toDouble() ) + val resource = BitmapDescriptorFactory.fromResource( + R.mipmap.label_blue1 + ) + val options = MarkerOptions().position(latLng).icon(resource) + aMap.addMarker(options) } } ids.add(markerId) //显示标识器详细信息 if (!detailDialog.isShowing) { - val markerBean = DataBaseManager.get.queryMarkerById(markerId).firstOrNull() - if (markerBean == null) { - "无法查询到此ID【${markerId}】的信息".show(context) - } else { - detailDialog.setMarker(markerBean) - detailDialog.show() + DataBaseManager.get.queryMarkerById(markerId).firstOrNull().apply { + if (this == null) { + "无法查询到此ID【${markerId}】的信息".show(context) + } else { + detailDialog.setMarker(this) + detailDialog.show() + } } } } @@ -582,7 +509,6 @@ } } } - return true } override fun observeRequestState() { @@ -865,9 +791,6 @@ override fun onResume() { super.onResume() binding.mapView.onResume() - if (serialPortService != null) { - openSerialPort() - } } override fun onPause() { @@ -885,8 +808,6 @@ binding.mapView.onDestroy() soundPool.autoPause() locationKit.stopLocation() - unbindService(serviceConnection) - closeSerialPort() BaseApplication.get().closeSerialPort() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt index 9f2d5cd..0f1611d 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt @@ -74,7 +74,7 @@ locationManager.registerGnssStatusCallback(gnssStatusListener, null) satelliteAdapter = SatelliteRecyclerAdapter(this, satelliteCollection) binding.recyclerView.adapter = satelliteAdapter - binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(1, Color.WHITE)) + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.WHITE)) //高德定位 locationKit.getCurrentLocation(false, object : OnGetLocationListener { diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt index c5f18a6..b3dce3c 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt @@ -1,26 +1,19 @@ package com.casic.common.detector.gd.view import android.app.DatePickerDialog -import android.content.ComponentName -import android.content.Context import android.content.DialogInterface -import android.content.Intent -import android.content.ServiceConnection import android.hardware.Camera import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message -import android.util.Log import android.view.View import android.widget.AdapterView import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.common.detector.gd.R import com.casic.common.detector.gd.adapter.EditableImageAdapter +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.callback.OnImageCompressListener @@ -33,18 +26,15 @@ import com.casic.common.detector.gd.extensions.toColor import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toObjectType -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel 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.dateToTimestamp import com.pengxh.kt.lite.extensions.navigatePageTo @@ -55,13 +45,12 @@ import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import java.io.File import java.util.Calendar import java.util.Date -class InstallMarkerActivity : KotlinBaseActivity(), Handler.Callback { +class InstallMarkerActivity : SerialPortActivity() { private val kTag = "InstallMarkerActivity" private val context = this @@ -73,7 +62,6 @@ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build() private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build() private var soundResourceId = 0 - private var serialPortService: SerialPortService? = null private lateinit var imageAdapter: EditableImageAdapter override fun initEvent() { @@ -255,18 +243,18 @@ //读标识器 binding.readMarkerButton.setOnClickListener { + openSerialPort() LoadingDialog.show(this, "标识器读取中,请稍后...") countDownTimer.start() binding.readMarkerButton.isEnabled = false - RuntimeCache.currentSegment = CurrentSegment.InstallMarker - serialPortService?.writeCommand('2') + readMarkerId() } } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.INSTALL_MARKER) { - val buffer = msg.obj as ByteArray - if ((buffer.first().toInt() and 0xFF).toString(16).uppercase() == "30") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.InstallMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { val markerId = buffer.toMarkerId() LoadingDialog.dismiss() countDownTimer.cancel() @@ -274,7 +262,6 @@ binding.identifierInclude.identifierIdView.text = markerId } } - return true } /** @@ -363,37 +350,7 @@ }) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - //在连接正常关闭的情况下不会被调用, 只在Service被破坏了或者被杀死的时候调用 - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - locationKit.getCurrentLocation(true, object : OnGetLocationListener { override fun onSuccess(location: AMapLocation) { binding.identifierInclude.lngView.text = location.longitude.toString() @@ -552,8 +509,6 @@ override fun onDestroy() { super.onDestroy() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt index 8a798b6..2c48cc9 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt @@ -1,18 +1,12 @@ package com.casic.common.detector.gd.view import android.app.Activity -import android.content.ComponentName -import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.graphics.BitmapFactory import android.graphics.Color import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.View @@ -34,6 +28,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.casic.common.detector.gd.R import com.casic.common.detector.gd.base.BaseApplication +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.bean.TaskLocalBean import com.casic.common.detector.gd.callback.OnGetAddressListener @@ -47,32 +42,27 @@ import com.casic.common.detector.gd.model.TaskDetailLocalModel import com.casic.common.detector.gd.model.TaskModel import com.casic.common.detector.gd.service.NtripConnectService -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.ExcelKit -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType -import com.casic.common.detector.gd.utils.GpioManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit import com.casic.common.detector.gd.utils.RouteOnMap -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.MarkerDetailDialog import com.casic.common.detector.gd.widgets.QueryMarkerDialog import com.casic.common.detector.gd.widgets.SamplePopupWindow -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.dp2px 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.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog @@ -82,45 +72,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.concurrent.atomic.AtomicInteger -class MainActivity : KotlinBaseActivity(), AMap.OnMarkerClickListener, - OnMapLongClickListener, Handler.Callback { +class MainActivity : SerialPortActivity(), AMap.OnMarkerClickListener, + OnMapLongClickListener { private val kTag = "MainActivity" private val context = this - - /***************************************************************************************/ - private val gpioManager by lazy { GpioManager() } - - /** - * 读取数据 1 - * 暂停读取 0 - * */ - private val gpioState = AtomicInteger(0) - private fun openSerialPort() { - if (gpioState.get() != 1) { - gpioManager.setGpioHigh("18") - gpioState.set(1) - Log.d(kTag, "openSerialPort: 调高串口电位") - } else { - Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") - } - } - - private fun closeSerialPort() { - if (gpioState.get() == 0) { - Log.d(kTag, "closeSerialPort: 已经是低电位,直接返回") - return - } - // 降低串口电位 - gpioManager.setGpioLow("18") - gpioState.set(0) - Log.d(kTag, "closeSerialPort: 降低串口电位") - } - - /***************************************************************************************/ private val samplePopupWindow by lazy { SamplePopupWindow(this) } private val detailDialog by lazy { MarkerDetailDialog(this) } private val locationKit by lazy { LocationKit(this) } @@ -133,7 +91,6 @@ private var freeTaskTitle = "" private var ids = HashSet() private var freeTaskId: String? = null - private var serialPortService: SerialPortService? = null private var soundResourceId = 0 private var isExecuteTask = false private lateinit var aMap: AMap @@ -146,43 +103,13 @@ binding.rootView.initImmersionBar(this, false, R.color.themeColor) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - openSerialPort() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - //地图初始化 initMapConfig(savedInstanceState) - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - Intent(this, NtripConnectService::class.java).also { - startService(it) - } +// Intent(this, NtripConnectService::class.java).also { +// startService(it) +// } soundResourceId = soundPool.load(this, R.raw.ring3, 1) //右上角菜单 @@ -211,7 +138,7 @@ showMarkersOnMap() } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() } @@ -446,7 +373,7 @@ override fun onConfirmClick() { isFreeTask = false soundPool.autoPause() - RuntimeCache.currentSegment = null + closeSerialPort() binding.stopFreeTaskButton.visibility = View.GONE if (freeTaskId.isNullOrBlank()) { @@ -499,11 +426,7 @@ } else { "此工单下无标识器,将执行自由巡检任务".show(context) freeTaskId = task.taskId - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } } @@ -524,21 +447,22 @@ freeTaskTitle = value val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(context, userId, value) - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } override fun onCancelClick() {} }).build().show() } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.FREE_INSPECTION) { - val buffer = msg.obj as ByteArray - //只响一次,因为探测频率高,所以依旧是连续的报警声 + private fun detectMarker() { + binding.stopFreeTaskButton.visibility = View.VISIBLE + isFreeTask = true + openSerialPort() + searchMarker(CurrentSegment.FreeInspection) + } + + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.FreeInspection) { soundPool.play(soundResourceId, 1f, 1f, 0, 0, 1f) when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { "4E" -> { @@ -552,27 +476,30 @@ val markerId = buffer.toMarkerId() //添加地图Marker if (!ids.contains(markerId)) { - //根据markerId查询标识器经纬度 - val labels = DataBaseManager.get.queryMarkerById(markerId) - if (labels.isNotEmpty()) { - val bean = labels.first() - aMap.addMarker( - MarkerOptions() - .position(LatLng(bean.lat.toDouble(), bean.lng.toDouble())) - .icon(BitmapDescriptorFactory.fromResource(R.mipmap.label_blue1)) + val markers = DataBaseManager.get.queryMarkerById(markerId) + if (markers.isNotEmpty()) { + val marker = markers.first() + val latLng = LatLng( + marker.lat.toDouble(), marker.lng.toDouble() ) + val resource = BitmapDescriptorFactory.fromResource( + R.mipmap.label_blue1 + ) + val options = MarkerOptions().position(latLng).icon(resource) + aMap.addMarker(options) } } ids.add(markerId) //显示标识器详细信息 if (!detailDialog.isShowing) { - val markerBean = DataBaseManager.get.queryMarkerById(markerId).firstOrNull() - if (markerBean == null) { - "无法查询到此ID【${markerId}】的信息".show(context) - } else { - detailDialog.setMarker(markerBean) - detailDialog.show() + DataBaseManager.get.queryMarkerById(markerId).firstOrNull().apply { + if (this == null) { + "无法查询到此ID【${markerId}】的信息".show(context) + } else { + detailDialog.setMarker(this) + detailDialog.show() + } } } } @@ -582,7 +509,6 @@ } } } - return true } override fun observeRequestState() { @@ -865,9 +791,6 @@ override fun onResume() { super.onResume() binding.mapView.onResume() - if (serialPortService != null) { - openSerialPort() - } } override fun onPause() { @@ -885,8 +808,6 @@ binding.mapView.onDestroy() soundPool.autoPause() locationKit.stopLocation() - unbindService(serviceConnection) - closeSerialPort() BaseApplication.get().closeSerialPort() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt index 9f2d5cd..0f1611d 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt @@ -74,7 +74,7 @@ locationManager.registerGnssStatusCallback(gnssStatusListener, null) satelliteAdapter = SatelliteRecyclerAdapter(this, satelliteCollection) binding.recyclerView.adapter = satelliteAdapter - binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(1, Color.WHITE)) + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.WHITE)) //高德定位 locationKit.getCurrentLocation(false, object : OnGetLocationListener { diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt index c7c8bb9..5417e1f 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt @@ -1,9 +1,5 @@ package com.casic.common.detector.gd.view -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection import android.graphics.Color import android.hardware.Sensor import android.hardware.SensorEvent @@ -13,9 +9,6 @@ import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.View import android.view.WindowManager @@ -25,28 +18,25 @@ import com.amap.api.maps.AMapUtils import com.amap.api.maps.model.LatLng import com.casic.common.detector.gd.R +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.databinding.ActivitySearchMarkerBinding import com.casic.common.detector.gd.extensions.toBuryDepth import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toSignalStrength import com.casic.common.detector.gd.model.MarkerDistanceData -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.RadarScanView -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.getSystemService import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.LiteKitConstant import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.dialog.AlertMessageDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -55,8 +45,8 @@ import java.util.TimerTask import kotlin.math.atan2 -class SearchMarkerActivity : KotlinBaseActivity(), - SensorEventListener, Handler.Callback { +class SearchMarkerActivity : SerialPortActivity(), + SensorEventListener { private val kTag = "SearchMarkerActivity" private val context = this @@ -76,38 +66,10 @@ private var signalEnergy = 0 //标识器信号强度 private var gravity: FloatArray? = null private var geomagnetic: FloatArray? = null - private var serialPortService: SerialPortService? = null - private lateinit var searchSignalEnergyTimer: Timer - - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定,开始搜索标识器") - searchMarker() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - val flag = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String - isExecuteTask = flag != "0" + val taskState = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String + isExecuteTask = taskState != "0" slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1) fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1) @@ -131,14 +93,11 @@ binding.taskStateView.isSelected = true } - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } + openSerialPort() + searchMarker(CurrentSegment.SearchMarker) //在标识器信号强度很强的时候,从数据库中计算出距离最近的点,以防出现探测不到ID的情况 - searchSignalEnergyTimer = Timer() - searchSignalEnergyTimer.schedule(object : TimerTask() { + Timer().schedule(object : TimerTask() { override fun run() { if (signalEnergy <= 700) {//18° runOnUiThread { @@ -183,21 +142,8 @@ }, 0, 2000) } - private val countDownTimer = object : CountDownTimer(10 * 1000, 1000) { - override fun onTick(millisUntilFinished: Long) { - - } - - override fun onFinish() { - LoadingDialog.dismiss() - "标识器深度探测超时,请移动位置重试".show(context) - searchMarker() - } - } - override fun initEvent() { binding.depthButton.setOnClickListener { - soundPool.autoPause() val result = DataBaseManager.get.queryMarkerById(markerId) if (result.isNotEmpty()) { val tag = when (result.first().markerType) { @@ -208,18 +154,25 @@ } if (tag == '1') { "此标识器无法读取埋深!".show(this) - searchMarker() } else { + soundPool.autoPause() LoadingDialog.show(this, "正在探测标识器埋深,请稍后...") - countDownTimer.start() // 发送读取标识器埋设深度指令 - RuntimeCache.currentSegment = CurrentSegment.DetectMarkerDepth - serialPortService?.writeCommand(tag) + detectDepth(tag) + object : CountDownTimer(3 * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + LoadingDialog.dismiss() + updateSegment(CurrentSegment.SearchMarker) + } + }.start() } } else { LoadingDialog.dismiss() "标识器未安装,安装成功后才可读取埋深!".show(this) - searchMarker() } } @@ -236,13 +189,11 @@ } } - override fun handleMessage(msg: Message): Boolean { - val buffer = msg.obj as ByteArray - if (msg.what == LocaleConstant.DETECT_MARKER_DEPTH) { - val tag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() - if (tag == "53") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.DetectDepth) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "53" && buffer.size == 5) { LoadingDialog.dismiss() - countDownTimer.cancel() try { AlertMessageDialog.Builder().setContext(context) .setTitle("温馨提示") @@ -251,54 +202,32 @@ .setOnDialogButtonClickListener(object : AlertMessageDialog.OnDialogButtonClickListener { override fun onConfirmClick() { - searchMarker() + updateSegment(CurrentSegment.SearchMarker) } }).build().show() } catch (e: WindowManager.BadTokenException) { e.printStackTrace() - } catch (e: NumberFormatException) { - e.printStackTrace() } } - } else if (msg.what == LocaleConstant.SEARCH_MARKER) { - try { - // 读取到信号强度 - when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { - "4E" -> { - val signalEnergy = buffer.toSignalStrength() + } else if (segment == CurrentSegment.SearchMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { + handleMarker(buffer.toMarkerId()) + } else if (flag == "4E" && buffer.size == 5) { + val signalEnergy = buffer.toSignalStrength() - //声音 - updateSound(signalEnergy) + //声音 + updateSound(signalEnergy) - //信号强度显示 - updateProgressBar(signalEnergy) + //信号强度显示 + updateProgressBar(signalEnergy) - //搜索信号文字显示 - updateTextResult(signalEnergy) - } - - "30" -> { - handleMarker(buffer.toMarkerId()) - } - - else -> { - Log.d(kTag, "乱码数据: ${buffer.contentToString()}") - } - } - } catch (e: Exception) { - e.printStackTrace() - Log.e(kTag, "Error processing buffer data", e) + //搜索信号文字显示 + updateTextResult(signalEnergy) + } else { + Log.d(kTag, "乱码数据: ${buffer.contentToString()}") } } - return true - } - - /** - * 搜索标识器 - * */ - private fun searchMarker() { - RuntimeCache.currentSegment = CurrentSegment.SearchMarker - serialPortService?.writeCommand('2', '6') } private fun updateSound(signalEnergy: Int) { @@ -492,9 +421,8 @@ override fun onDestroy() { super.onDestroy() + closeSerialPort() soundPool.autoPause() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt index c5f18a6..b3dce3c 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt @@ -1,26 +1,19 @@ package com.casic.common.detector.gd.view import android.app.DatePickerDialog -import android.content.ComponentName -import android.content.Context import android.content.DialogInterface -import android.content.Intent -import android.content.ServiceConnection import android.hardware.Camera import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message -import android.util.Log import android.view.View import android.widget.AdapterView import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.common.detector.gd.R import com.casic.common.detector.gd.adapter.EditableImageAdapter +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.callback.OnImageCompressListener @@ -33,18 +26,15 @@ import com.casic.common.detector.gd.extensions.toColor import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toObjectType -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel 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.dateToTimestamp import com.pengxh.kt.lite.extensions.navigatePageTo @@ -55,13 +45,12 @@ import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import java.io.File import java.util.Calendar import java.util.Date -class InstallMarkerActivity : KotlinBaseActivity(), Handler.Callback { +class InstallMarkerActivity : SerialPortActivity() { private val kTag = "InstallMarkerActivity" private val context = this @@ -73,7 +62,6 @@ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build() private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build() private var soundResourceId = 0 - private var serialPortService: SerialPortService? = null private lateinit var imageAdapter: EditableImageAdapter override fun initEvent() { @@ -255,18 +243,18 @@ //读标识器 binding.readMarkerButton.setOnClickListener { + openSerialPort() LoadingDialog.show(this, "标识器读取中,请稍后...") countDownTimer.start() binding.readMarkerButton.isEnabled = false - RuntimeCache.currentSegment = CurrentSegment.InstallMarker - serialPortService?.writeCommand('2') + readMarkerId() } } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.INSTALL_MARKER) { - val buffer = msg.obj as ByteArray - if ((buffer.first().toInt() and 0xFF).toString(16).uppercase() == "30") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.InstallMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { val markerId = buffer.toMarkerId() LoadingDialog.dismiss() countDownTimer.cancel() @@ -274,7 +262,6 @@ binding.identifierInclude.identifierIdView.text = markerId } } - return true } /** @@ -363,37 +350,7 @@ }) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - //在连接正常关闭的情况下不会被调用, 只在Service被破坏了或者被杀死的时候调用 - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - locationKit.getCurrentLocation(true, object : OnGetLocationListener { override fun onSuccess(location: AMapLocation) { binding.identifierInclude.lngView.text = location.longitude.toString() @@ -552,8 +509,6 @@ override fun onDestroy() { super.onDestroy() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt index 8a798b6..2c48cc9 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt @@ -1,18 +1,12 @@ package com.casic.common.detector.gd.view import android.app.Activity -import android.content.ComponentName -import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.graphics.BitmapFactory import android.graphics.Color import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.View @@ -34,6 +28,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.casic.common.detector.gd.R import com.casic.common.detector.gd.base.BaseApplication +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.bean.TaskLocalBean import com.casic.common.detector.gd.callback.OnGetAddressListener @@ -47,32 +42,27 @@ import com.casic.common.detector.gd.model.TaskDetailLocalModel import com.casic.common.detector.gd.model.TaskModel import com.casic.common.detector.gd.service.NtripConnectService -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.ExcelKit -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType -import com.casic.common.detector.gd.utils.GpioManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit import com.casic.common.detector.gd.utils.RouteOnMap -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.MarkerDetailDialog import com.casic.common.detector.gd.widgets.QueryMarkerDialog import com.casic.common.detector.gd.widgets.SamplePopupWindow -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.dp2px 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.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog @@ -82,45 +72,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.concurrent.atomic.AtomicInteger -class MainActivity : KotlinBaseActivity(), AMap.OnMarkerClickListener, - OnMapLongClickListener, Handler.Callback { +class MainActivity : SerialPortActivity(), AMap.OnMarkerClickListener, + OnMapLongClickListener { private val kTag = "MainActivity" private val context = this - - /***************************************************************************************/ - private val gpioManager by lazy { GpioManager() } - - /** - * 读取数据 1 - * 暂停读取 0 - * */ - private val gpioState = AtomicInteger(0) - private fun openSerialPort() { - if (gpioState.get() != 1) { - gpioManager.setGpioHigh("18") - gpioState.set(1) - Log.d(kTag, "openSerialPort: 调高串口电位") - } else { - Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") - } - } - - private fun closeSerialPort() { - if (gpioState.get() == 0) { - Log.d(kTag, "closeSerialPort: 已经是低电位,直接返回") - return - } - // 降低串口电位 - gpioManager.setGpioLow("18") - gpioState.set(0) - Log.d(kTag, "closeSerialPort: 降低串口电位") - } - - /***************************************************************************************/ private val samplePopupWindow by lazy { SamplePopupWindow(this) } private val detailDialog by lazy { MarkerDetailDialog(this) } private val locationKit by lazy { LocationKit(this) } @@ -133,7 +91,6 @@ private var freeTaskTitle = "" private var ids = HashSet() private var freeTaskId: String? = null - private var serialPortService: SerialPortService? = null private var soundResourceId = 0 private var isExecuteTask = false private lateinit var aMap: AMap @@ -146,43 +103,13 @@ binding.rootView.initImmersionBar(this, false, R.color.themeColor) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - openSerialPort() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - //地图初始化 initMapConfig(savedInstanceState) - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - Intent(this, NtripConnectService::class.java).also { - startService(it) - } +// Intent(this, NtripConnectService::class.java).also { +// startService(it) +// } soundResourceId = soundPool.load(this, R.raw.ring3, 1) //右上角菜单 @@ -211,7 +138,7 @@ showMarkersOnMap() } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() } @@ -446,7 +373,7 @@ override fun onConfirmClick() { isFreeTask = false soundPool.autoPause() - RuntimeCache.currentSegment = null + closeSerialPort() binding.stopFreeTaskButton.visibility = View.GONE if (freeTaskId.isNullOrBlank()) { @@ -499,11 +426,7 @@ } else { "此工单下无标识器,将执行自由巡检任务".show(context) freeTaskId = task.taskId - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } } @@ -524,21 +447,22 @@ freeTaskTitle = value val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(context, userId, value) - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } override fun onCancelClick() {} }).build().show() } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.FREE_INSPECTION) { - val buffer = msg.obj as ByteArray - //只响一次,因为探测频率高,所以依旧是连续的报警声 + private fun detectMarker() { + binding.stopFreeTaskButton.visibility = View.VISIBLE + isFreeTask = true + openSerialPort() + searchMarker(CurrentSegment.FreeInspection) + } + + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.FreeInspection) { soundPool.play(soundResourceId, 1f, 1f, 0, 0, 1f) when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { "4E" -> { @@ -552,27 +476,30 @@ val markerId = buffer.toMarkerId() //添加地图Marker if (!ids.contains(markerId)) { - //根据markerId查询标识器经纬度 - val labels = DataBaseManager.get.queryMarkerById(markerId) - if (labels.isNotEmpty()) { - val bean = labels.first() - aMap.addMarker( - MarkerOptions() - .position(LatLng(bean.lat.toDouble(), bean.lng.toDouble())) - .icon(BitmapDescriptorFactory.fromResource(R.mipmap.label_blue1)) + val markers = DataBaseManager.get.queryMarkerById(markerId) + if (markers.isNotEmpty()) { + val marker = markers.first() + val latLng = LatLng( + marker.lat.toDouble(), marker.lng.toDouble() ) + val resource = BitmapDescriptorFactory.fromResource( + R.mipmap.label_blue1 + ) + val options = MarkerOptions().position(latLng).icon(resource) + aMap.addMarker(options) } } ids.add(markerId) //显示标识器详细信息 if (!detailDialog.isShowing) { - val markerBean = DataBaseManager.get.queryMarkerById(markerId).firstOrNull() - if (markerBean == null) { - "无法查询到此ID【${markerId}】的信息".show(context) - } else { - detailDialog.setMarker(markerBean) - detailDialog.show() + DataBaseManager.get.queryMarkerById(markerId).firstOrNull().apply { + if (this == null) { + "无法查询到此ID【${markerId}】的信息".show(context) + } else { + detailDialog.setMarker(this) + detailDialog.show() + } } } } @@ -582,7 +509,6 @@ } } } - return true } override fun observeRequestState() { @@ -865,9 +791,6 @@ override fun onResume() { super.onResume() binding.mapView.onResume() - if (serialPortService != null) { - openSerialPort() - } } override fun onPause() { @@ -885,8 +808,6 @@ binding.mapView.onDestroy() soundPool.autoPause() locationKit.stopLocation() - unbindService(serviceConnection) - closeSerialPort() BaseApplication.get().closeSerialPort() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt index 9f2d5cd..0f1611d 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt @@ -74,7 +74,7 @@ locationManager.registerGnssStatusCallback(gnssStatusListener, null) satelliteAdapter = SatelliteRecyclerAdapter(this, satelliteCollection) binding.recyclerView.adapter = satelliteAdapter - binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(1, Color.WHITE)) + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.WHITE)) //高德定位 locationKit.getCurrentLocation(false, object : OnGetLocationListener { diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt index c7c8bb9..5417e1f 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt @@ -1,9 +1,5 @@ package com.casic.common.detector.gd.view -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection import android.graphics.Color import android.hardware.Sensor import android.hardware.SensorEvent @@ -13,9 +9,6 @@ import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.View import android.view.WindowManager @@ -25,28 +18,25 @@ import com.amap.api.maps.AMapUtils import com.amap.api.maps.model.LatLng import com.casic.common.detector.gd.R +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.databinding.ActivitySearchMarkerBinding import com.casic.common.detector.gd.extensions.toBuryDepth import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toSignalStrength import com.casic.common.detector.gd.model.MarkerDistanceData -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.RadarScanView -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.getSystemService import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.LiteKitConstant import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.dialog.AlertMessageDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -55,8 +45,8 @@ import java.util.TimerTask import kotlin.math.atan2 -class SearchMarkerActivity : KotlinBaseActivity(), - SensorEventListener, Handler.Callback { +class SearchMarkerActivity : SerialPortActivity(), + SensorEventListener { private val kTag = "SearchMarkerActivity" private val context = this @@ -76,38 +66,10 @@ private var signalEnergy = 0 //标识器信号强度 private var gravity: FloatArray? = null private var geomagnetic: FloatArray? = null - private var serialPortService: SerialPortService? = null - private lateinit var searchSignalEnergyTimer: Timer - - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定,开始搜索标识器") - searchMarker() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - val flag = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String - isExecuteTask = flag != "0" + val taskState = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String + isExecuteTask = taskState != "0" slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1) fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1) @@ -131,14 +93,11 @@ binding.taskStateView.isSelected = true } - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } + openSerialPort() + searchMarker(CurrentSegment.SearchMarker) //在标识器信号强度很强的时候,从数据库中计算出距离最近的点,以防出现探测不到ID的情况 - searchSignalEnergyTimer = Timer() - searchSignalEnergyTimer.schedule(object : TimerTask() { + Timer().schedule(object : TimerTask() { override fun run() { if (signalEnergy <= 700) {//18° runOnUiThread { @@ -183,21 +142,8 @@ }, 0, 2000) } - private val countDownTimer = object : CountDownTimer(10 * 1000, 1000) { - override fun onTick(millisUntilFinished: Long) { - - } - - override fun onFinish() { - LoadingDialog.dismiss() - "标识器深度探测超时,请移动位置重试".show(context) - searchMarker() - } - } - override fun initEvent() { binding.depthButton.setOnClickListener { - soundPool.autoPause() val result = DataBaseManager.get.queryMarkerById(markerId) if (result.isNotEmpty()) { val tag = when (result.first().markerType) { @@ -208,18 +154,25 @@ } if (tag == '1') { "此标识器无法读取埋深!".show(this) - searchMarker() } else { + soundPool.autoPause() LoadingDialog.show(this, "正在探测标识器埋深,请稍后...") - countDownTimer.start() // 发送读取标识器埋设深度指令 - RuntimeCache.currentSegment = CurrentSegment.DetectMarkerDepth - serialPortService?.writeCommand(tag) + detectDepth(tag) + object : CountDownTimer(3 * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + LoadingDialog.dismiss() + updateSegment(CurrentSegment.SearchMarker) + } + }.start() } } else { LoadingDialog.dismiss() "标识器未安装,安装成功后才可读取埋深!".show(this) - searchMarker() } } @@ -236,13 +189,11 @@ } } - override fun handleMessage(msg: Message): Boolean { - val buffer = msg.obj as ByteArray - if (msg.what == LocaleConstant.DETECT_MARKER_DEPTH) { - val tag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() - if (tag == "53") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.DetectDepth) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "53" && buffer.size == 5) { LoadingDialog.dismiss() - countDownTimer.cancel() try { AlertMessageDialog.Builder().setContext(context) .setTitle("温馨提示") @@ -251,54 +202,32 @@ .setOnDialogButtonClickListener(object : AlertMessageDialog.OnDialogButtonClickListener { override fun onConfirmClick() { - searchMarker() + updateSegment(CurrentSegment.SearchMarker) } }).build().show() } catch (e: WindowManager.BadTokenException) { e.printStackTrace() - } catch (e: NumberFormatException) { - e.printStackTrace() } } - } else if (msg.what == LocaleConstant.SEARCH_MARKER) { - try { - // 读取到信号强度 - when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { - "4E" -> { - val signalEnergy = buffer.toSignalStrength() + } else if (segment == CurrentSegment.SearchMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { + handleMarker(buffer.toMarkerId()) + } else if (flag == "4E" && buffer.size == 5) { + val signalEnergy = buffer.toSignalStrength() - //声音 - updateSound(signalEnergy) + //声音 + updateSound(signalEnergy) - //信号强度显示 - updateProgressBar(signalEnergy) + //信号强度显示 + updateProgressBar(signalEnergy) - //搜索信号文字显示 - updateTextResult(signalEnergy) - } - - "30" -> { - handleMarker(buffer.toMarkerId()) - } - - else -> { - Log.d(kTag, "乱码数据: ${buffer.contentToString()}") - } - } - } catch (e: Exception) { - e.printStackTrace() - Log.e(kTag, "Error processing buffer data", e) + //搜索信号文字显示 + updateTextResult(signalEnergy) + } else { + Log.d(kTag, "乱码数据: ${buffer.contentToString()}") } } - return true - } - - /** - * 搜索标识器 - * */ - private fun searchMarker() { - RuntimeCache.currentSegment = CurrentSegment.SearchMarker - serialPortService?.writeCommand('2', '6') } private fun updateSound(signalEnergy: Int) { @@ -492,9 +421,8 @@ override fun onDestroy() { super.onDestroy() + closeSerialPort() soundPool.autoPause() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt index ddc5a32..8641c79 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt @@ -13,17 +13,17 @@ import com.casic.common.detector.gd.databinding.ActivityVersionControlBinding import com.casic.common.detector.gd.extensions.appendDownloadUrl import com.casic.common.detector.gd.extensions.initImmersionBar -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType import com.casic.common.detector.gd.vm.VersionViewModel -import com.casic.common.detector.gd.widgets.ProgressDialog import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.ProgressDialog import java.io.File class VersionControlActivity : KotlinBaseActivity() { @@ -118,7 +118,7 @@ installApk(file) } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() progressDialog.dismiss() } diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt index c5f18a6..b3dce3c 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt @@ -1,26 +1,19 @@ package com.casic.common.detector.gd.view import android.app.DatePickerDialog -import android.content.ComponentName -import android.content.Context import android.content.DialogInterface -import android.content.Intent -import android.content.ServiceConnection import android.hardware.Camera import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message -import android.util.Log import android.view.View import android.widget.AdapterView import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.common.detector.gd.R import com.casic.common.detector.gd.adapter.EditableImageAdapter +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.callback.OnImageCompressListener @@ -33,18 +26,15 @@ import com.casic.common.detector.gd.extensions.toColor import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toObjectType -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel 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.dateToTimestamp import com.pengxh.kt.lite.extensions.navigatePageTo @@ -55,13 +45,12 @@ import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import java.io.File import java.util.Calendar import java.util.Date -class InstallMarkerActivity : KotlinBaseActivity(), Handler.Callback { +class InstallMarkerActivity : SerialPortActivity() { private val kTag = "InstallMarkerActivity" private val context = this @@ -73,7 +62,6 @@ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build() private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build() private var soundResourceId = 0 - private var serialPortService: SerialPortService? = null private lateinit var imageAdapter: EditableImageAdapter override fun initEvent() { @@ -255,18 +243,18 @@ //读标识器 binding.readMarkerButton.setOnClickListener { + openSerialPort() LoadingDialog.show(this, "标识器读取中,请稍后...") countDownTimer.start() binding.readMarkerButton.isEnabled = false - RuntimeCache.currentSegment = CurrentSegment.InstallMarker - serialPortService?.writeCommand('2') + readMarkerId() } } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.INSTALL_MARKER) { - val buffer = msg.obj as ByteArray - if ((buffer.first().toInt() and 0xFF).toString(16).uppercase() == "30") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.InstallMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { val markerId = buffer.toMarkerId() LoadingDialog.dismiss() countDownTimer.cancel() @@ -274,7 +262,6 @@ binding.identifierInclude.identifierIdView.text = markerId } } - return true } /** @@ -363,37 +350,7 @@ }) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - //在连接正常关闭的情况下不会被调用, 只在Service被破坏了或者被杀死的时候调用 - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - locationKit.getCurrentLocation(true, object : OnGetLocationListener { override fun onSuccess(location: AMapLocation) { binding.identifierInclude.lngView.text = location.longitude.toString() @@ -552,8 +509,6 @@ override fun onDestroy() { super.onDestroy() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt index 8a798b6..2c48cc9 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt @@ -1,18 +1,12 @@ package com.casic.common.detector.gd.view import android.app.Activity -import android.content.ComponentName -import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.graphics.BitmapFactory import android.graphics.Color import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.View @@ -34,6 +28,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.casic.common.detector.gd.R import com.casic.common.detector.gd.base.BaseApplication +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.bean.TaskLocalBean import com.casic.common.detector.gd.callback.OnGetAddressListener @@ -47,32 +42,27 @@ import com.casic.common.detector.gd.model.TaskDetailLocalModel import com.casic.common.detector.gd.model.TaskModel import com.casic.common.detector.gd.service.NtripConnectService -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.ExcelKit -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType -import com.casic.common.detector.gd.utils.GpioManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit import com.casic.common.detector.gd.utils.RouteOnMap -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.MarkerDetailDialog import com.casic.common.detector.gd.widgets.QueryMarkerDialog import com.casic.common.detector.gd.widgets.SamplePopupWindow -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.dp2px 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.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog @@ -82,45 +72,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.concurrent.atomic.AtomicInteger -class MainActivity : KotlinBaseActivity(), AMap.OnMarkerClickListener, - OnMapLongClickListener, Handler.Callback { +class MainActivity : SerialPortActivity(), AMap.OnMarkerClickListener, + OnMapLongClickListener { private val kTag = "MainActivity" private val context = this - - /***************************************************************************************/ - private val gpioManager by lazy { GpioManager() } - - /** - * 读取数据 1 - * 暂停读取 0 - * */ - private val gpioState = AtomicInteger(0) - private fun openSerialPort() { - if (gpioState.get() != 1) { - gpioManager.setGpioHigh("18") - gpioState.set(1) - Log.d(kTag, "openSerialPort: 调高串口电位") - } else { - Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") - } - } - - private fun closeSerialPort() { - if (gpioState.get() == 0) { - Log.d(kTag, "closeSerialPort: 已经是低电位,直接返回") - return - } - // 降低串口电位 - gpioManager.setGpioLow("18") - gpioState.set(0) - Log.d(kTag, "closeSerialPort: 降低串口电位") - } - - /***************************************************************************************/ private val samplePopupWindow by lazy { SamplePopupWindow(this) } private val detailDialog by lazy { MarkerDetailDialog(this) } private val locationKit by lazy { LocationKit(this) } @@ -133,7 +91,6 @@ private var freeTaskTitle = "" private var ids = HashSet() private var freeTaskId: String? = null - private var serialPortService: SerialPortService? = null private var soundResourceId = 0 private var isExecuteTask = false private lateinit var aMap: AMap @@ -146,43 +103,13 @@ binding.rootView.initImmersionBar(this, false, R.color.themeColor) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - openSerialPort() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - //地图初始化 initMapConfig(savedInstanceState) - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - Intent(this, NtripConnectService::class.java).also { - startService(it) - } +// Intent(this, NtripConnectService::class.java).also { +// startService(it) +// } soundResourceId = soundPool.load(this, R.raw.ring3, 1) //右上角菜单 @@ -211,7 +138,7 @@ showMarkersOnMap() } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() } @@ -446,7 +373,7 @@ override fun onConfirmClick() { isFreeTask = false soundPool.autoPause() - RuntimeCache.currentSegment = null + closeSerialPort() binding.stopFreeTaskButton.visibility = View.GONE if (freeTaskId.isNullOrBlank()) { @@ -499,11 +426,7 @@ } else { "此工单下无标识器,将执行自由巡检任务".show(context) freeTaskId = task.taskId - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } } @@ -524,21 +447,22 @@ freeTaskTitle = value val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(context, userId, value) - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } override fun onCancelClick() {} }).build().show() } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.FREE_INSPECTION) { - val buffer = msg.obj as ByteArray - //只响一次,因为探测频率高,所以依旧是连续的报警声 + private fun detectMarker() { + binding.stopFreeTaskButton.visibility = View.VISIBLE + isFreeTask = true + openSerialPort() + searchMarker(CurrentSegment.FreeInspection) + } + + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.FreeInspection) { soundPool.play(soundResourceId, 1f, 1f, 0, 0, 1f) when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { "4E" -> { @@ -552,27 +476,30 @@ val markerId = buffer.toMarkerId() //添加地图Marker if (!ids.contains(markerId)) { - //根据markerId查询标识器经纬度 - val labels = DataBaseManager.get.queryMarkerById(markerId) - if (labels.isNotEmpty()) { - val bean = labels.first() - aMap.addMarker( - MarkerOptions() - .position(LatLng(bean.lat.toDouble(), bean.lng.toDouble())) - .icon(BitmapDescriptorFactory.fromResource(R.mipmap.label_blue1)) + val markers = DataBaseManager.get.queryMarkerById(markerId) + if (markers.isNotEmpty()) { + val marker = markers.first() + val latLng = LatLng( + marker.lat.toDouble(), marker.lng.toDouble() ) + val resource = BitmapDescriptorFactory.fromResource( + R.mipmap.label_blue1 + ) + val options = MarkerOptions().position(latLng).icon(resource) + aMap.addMarker(options) } } ids.add(markerId) //显示标识器详细信息 if (!detailDialog.isShowing) { - val markerBean = DataBaseManager.get.queryMarkerById(markerId).firstOrNull() - if (markerBean == null) { - "无法查询到此ID【${markerId}】的信息".show(context) - } else { - detailDialog.setMarker(markerBean) - detailDialog.show() + DataBaseManager.get.queryMarkerById(markerId).firstOrNull().apply { + if (this == null) { + "无法查询到此ID【${markerId}】的信息".show(context) + } else { + detailDialog.setMarker(this) + detailDialog.show() + } } } } @@ -582,7 +509,6 @@ } } } - return true } override fun observeRequestState() { @@ -865,9 +791,6 @@ override fun onResume() { super.onResume() binding.mapView.onResume() - if (serialPortService != null) { - openSerialPort() - } } override fun onPause() { @@ -885,8 +808,6 @@ binding.mapView.onDestroy() soundPool.autoPause() locationKit.stopLocation() - unbindService(serviceConnection) - closeSerialPort() BaseApplication.get().closeSerialPort() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt index 9f2d5cd..0f1611d 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt @@ -74,7 +74,7 @@ locationManager.registerGnssStatusCallback(gnssStatusListener, null) satelliteAdapter = SatelliteRecyclerAdapter(this, satelliteCollection) binding.recyclerView.adapter = satelliteAdapter - binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(1, Color.WHITE)) + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.WHITE)) //高德定位 locationKit.getCurrentLocation(false, object : OnGetLocationListener { diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt index c7c8bb9..5417e1f 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt @@ -1,9 +1,5 @@ package com.casic.common.detector.gd.view -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection import android.graphics.Color import android.hardware.Sensor import android.hardware.SensorEvent @@ -13,9 +9,6 @@ import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.View import android.view.WindowManager @@ -25,28 +18,25 @@ import com.amap.api.maps.AMapUtils import com.amap.api.maps.model.LatLng import com.casic.common.detector.gd.R +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.databinding.ActivitySearchMarkerBinding import com.casic.common.detector.gd.extensions.toBuryDepth import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toSignalStrength import com.casic.common.detector.gd.model.MarkerDistanceData -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.RadarScanView -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.getSystemService import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.LiteKitConstant import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.dialog.AlertMessageDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -55,8 +45,8 @@ import java.util.TimerTask import kotlin.math.atan2 -class SearchMarkerActivity : KotlinBaseActivity(), - SensorEventListener, Handler.Callback { +class SearchMarkerActivity : SerialPortActivity(), + SensorEventListener { private val kTag = "SearchMarkerActivity" private val context = this @@ -76,38 +66,10 @@ private var signalEnergy = 0 //标识器信号强度 private var gravity: FloatArray? = null private var geomagnetic: FloatArray? = null - private var serialPortService: SerialPortService? = null - private lateinit var searchSignalEnergyTimer: Timer - - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定,开始搜索标识器") - searchMarker() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - val flag = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String - isExecuteTask = flag != "0" + val taskState = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String + isExecuteTask = taskState != "0" slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1) fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1) @@ -131,14 +93,11 @@ binding.taskStateView.isSelected = true } - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } + openSerialPort() + searchMarker(CurrentSegment.SearchMarker) //在标识器信号强度很强的时候,从数据库中计算出距离最近的点,以防出现探测不到ID的情况 - searchSignalEnergyTimer = Timer() - searchSignalEnergyTimer.schedule(object : TimerTask() { + Timer().schedule(object : TimerTask() { override fun run() { if (signalEnergy <= 700) {//18° runOnUiThread { @@ -183,21 +142,8 @@ }, 0, 2000) } - private val countDownTimer = object : CountDownTimer(10 * 1000, 1000) { - override fun onTick(millisUntilFinished: Long) { - - } - - override fun onFinish() { - LoadingDialog.dismiss() - "标识器深度探测超时,请移动位置重试".show(context) - searchMarker() - } - } - override fun initEvent() { binding.depthButton.setOnClickListener { - soundPool.autoPause() val result = DataBaseManager.get.queryMarkerById(markerId) if (result.isNotEmpty()) { val tag = when (result.first().markerType) { @@ -208,18 +154,25 @@ } if (tag == '1') { "此标识器无法读取埋深!".show(this) - searchMarker() } else { + soundPool.autoPause() LoadingDialog.show(this, "正在探测标识器埋深,请稍后...") - countDownTimer.start() // 发送读取标识器埋设深度指令 - RuntimeCache.currentSegment = CurrentSegment.DetectMarkerDepth - serialPortService?.writeCommand(tag) + detectDepth(tag) + object : CountDownTimer(3 * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + LoadingDialog.dismiss() + updateSegment(CurrentSegment.SearchMarker) + } + }.start() } } else { LoadingDialog.dismiss() "标识器未安装,安装成功后才可读取埋深!".show(this) - searchMarker() } } @@ -236,13 +189,11 @@ } } - override fun handleMessage(msg: Message): Boolean { - val buffer = msg.obj as ByteArray - if (msg.what == LocaleConstant.DETECT_MARKER_DEPTH) { - val tag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() - if (tag == "53") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.DetectDepth) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "53" && buffer.size == 5) { LoadingDialog.dismiss() - countDownTimer.cancel() try { AlertMessageDialog.Builder().setContext(context) .setTitle("温馨提示") @@ -251,54 +202,32 @@ .setOnDialogButtonClickListener(object : AlertMessageDialog.OnDialogButtonClickListener { override fun onConfirmClick() { - searchMarker() + updateSegment(CurrentSegment.SearchMarker) } }).build().show() } catch (e: WindowManager.BadTokenException) { e.printStackTrace() - } catch (e: NumberFormatException) { - e.printStackTrace() } } - } else if (msg.what == LocaleConstant.SEARCH_MARKER) { - try { - // 读取到信号强度 - when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { - "4E" -> { - val signalEnergy = buffer.toSignalStrength() + } else if (segment == CurrentSegment.SearchMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { + handleMarker(buffer.toMarkerId()) + } else if (flag == "4E" && buffer.size == 5) { + val signalEnergy = buffer.toSignalStrength() - //声音 - updateSound(signalEnergy) + //声音 + updateSound(signalEnergy) - //信号强度显示 - updateProgressBar(signalEnergy) + //信号强度显示 + updateProgressBar(signalEnergy) - //搜索信号文字显示 - updateTextResult(signalEnergy) - } - - "30" -> { - handleMarker(buffer.toMarkerId()) - } - - else -> { - Log.d(kTag, "乱码数据: ${buffer.contentToString()}") - } - } - } catch (e: Exception) { - e.printStackTrace() - Log.e(kTag, "Error processing buffer data", e) + //搜索信号文字显示 + updateTextResult(signalEnergy) + } else { + Log.d(kTag, "乱码数据: ${buffer.contentToString()}") } } - return true - } - - /** - * 搜索标识器 - * */ - private fun searchMarker() { - RuntimeCache.currentSegment = CurrentSegment.SearchMarker - serialPortService?.writeCommand('2', '6') } private fun updateSound(signalEnergy: Int) { @@ -492,9 +421,8 @@ override fun onDestroy() { super.onDestroy() + closeSerialPort() soundPool.autoPause() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt index ddc5a32..8641c79 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt @@ -13,17 +13,17 @@ import com.casic.common.detector.gd.databinding.ActivityVersionControlBinding import com.casic.common.detector.gd.extensions.appendDownloadUrl import com.casic.common.detector.gd.extensions.initImmersionBar -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType import com.casic.common.detector.gd.vm.VersionViewModel -import com.casic.common.detector.gd.widgets.ProgressDialog import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.ProgressDialog import java.io.File class VersionControlActivity : KotlinBaseActivity() { @@ -118,7 +118,7 @@ installApk(file) } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() progressDialog.dismiss() } diff --git a/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt b/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt deleted file mode 100644 index af480b0..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.casic.common.detector.gd.widgets - -import android.app.Dialog -import android.content.Context -import android.os.Bundle -import com.casic.common.detector.gd.databinding.DialogProgressBinding -import com.pengxh.kt.lite.R -import com.pengxh.kt.lite.extensions.binding -import com.pengxh.kt.lite.extensions.initDialogLayoutParams - -class ProgressDialog(context: Context) : Dialog(context, R.style.UserDefinedDialogStyle) { - - private val binding: DialogProgressBinding by binding() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - this.initDialogLayoutParams(0.5f) - setCanceledOnTouchOutside(false) - setCancelable(false) - binding.progressBar.progress = 0 - binding.progressText.text = "0 %" - } - - fun setMaxProgress(maxProgress: Long) { - binding.progressBar.max = maxProgress.toInt() - } - - private fun getMaxProgress() = binding.progressBar.max - - fun updateProgress(progress: Long) { - binding.progressBar.progress = progress.toInt() - - val percent = (progress.toFloat() / getMaxProgress()) * 100 - binding.progressText.text = String.format("%.2f %%", percent) - } -} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c9b11ec..1a81160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { //基础依赖库 - implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.1' + implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.1.3' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' //Google官方授权框架 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f152d8..5c645f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,6 @@ android:resource="@xml/file_paths" /> - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt index c5f18a6..b3dce3c 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt @@ -1,26 +1,19 @@ package com.casic.common.detector.gd.view import android.app.DatePickerDialog -import android.content.ComponentName -import android.content.Context import android.content.DialogInterface -import android.content.Intent -import android.content.ServiceConnection import android.hardware.Camera import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message -import android.util.Log import android.view.View import android.widget.AdapterView import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.common.detector.gd.R import com.casic.common.detector.gd.adapter.EditableImageAdapter +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.callback.OnImageCompressListener @@ -33,18 +26,15 @@ import com.casic.common.detector.gd.extensions.toColor import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toObjectType -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel 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.dateToTimestamp import com.pengxh.kt.lite.extensions.navigatePageTo @@ -55,13 +45,12 @@ import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import java.io.File import java.util.Calendar import java.util.Date -class InstallMarkerActivity : KotlinBaseActivity(), Handler.Callback { +class InstallMarkerActivity : SerialPortActivity() { private val kTag = "InstallMarkerActivity" private val context = this @@ -73,7 +62,6 @@ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build() private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build() private var soundResourceId = 0 - private var serialPortService: SerialPortService? = null private lateinit var imageAdapter: EditableImageAdapter override fun initEvent() { @@ -255,18 +243,18 @@ //读标识器 binding.readMarkerButton.setOnClickListener { + openSerialPort() LoadingDialog.show(this, "标识器读取中,请稍后...") countDownTimer.start() binding.readMarkerButton.isEnabled = false - RuntimeCache.currentSegment = CurrentSegment.InstallMarker - serialPortService?.writeCommand('2') + readMarkerId() } } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.INSTALL_MARKER) { - val buffer = msg.obj as ByteArray - if ((buffer.first().toInt() and 0xFF).toString(16).uppercase() == "30") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.InstallMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { val markerId = buffer.toMarkerId() LoadingDialog.dismiss() countDownTimer.cancel() @@ -274,7 +262,6 @@ binding.identifierInclude.identifierIdView.text = markerId } } - return true } /** @@ -363,37 +350,7 @@ }) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - //在连接正常关闭的情况下不会被调用, 只在Service被破坏了或者被杀死的时候调用 - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - locationKit.getCurrentLocation(true, object : OnGetLocationListener { override fun onSuccess(location: AMapLocation) { binding.identifierInclude.lngView.text = location.longitude.toString() @@ -552,8 +509,6 @@ override fun onDestroy() { super.onDestroy() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt index 8a798b6..2c48cc9 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt @@ -1,18 +1,12 @@ package com.casic.common.detector.gd.view import android.app.Activity -import android.content.ComponentName -import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.graphics.BitmapFactory import android.graphics.Color import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.View @@ -34,6 +28,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.casic.common.detector.gd.R import com.casic.common.detector.gd.base.BaseApplication +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.bean.TaskLocalBean import com.casic.common.detector.gd.callback.OnGetAddressListener @@ -47,32 +42,27 @@ import com.casic.common.detector.gd.model.TaskDetailLocalModel import com.casic.common.detector.gd.model.TaskModel import com.casic.common.detector.gd.service.NtripConnectService -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.ExcelKit -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType -import com.casic.common.detector.gd.utils.GpioManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit import com.casic.common.detector.gd.utils.RouteOnMap -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.MarkerDetailDialog import com.casic.common.detector.gd.widgets.QueryMarkerDialog import com.casic.common.detector.gd.widgets.SamplePopupWindow -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.dp2px 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.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog @@ -82,45 +72,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.concurrent.atomic.AtomicInteger -class MainActivity : KotlinBaseActivity(), AMap.OnMarkerClickListener, - OnMapLongClickListener, Handler.Callback { +class MainActivity : SerialPortActivity(), AMap.OnMarkerClickListener, + OnMapLongClickListener { private val kTag = "MainActivity" private val context = this - - /***************************************************************************************/ - private val gpioManager by lazy { GpioManager() } - - /** - * 读取数据 1 - * 暂停读取 0 - * */ - private val gpioState = AtomicInteger(0) - private fun openSerialPort() { - if (gpioState.get() != 1) { - gpioManager.setGpioHigh("18") - gpioState.set(1) - Log.d(kTag, "openSerialPort: 调高串口电位") - } else { - Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") - } - } - - private fun closeSerialPort() { - if (gpioState.get() == 0) { - Log.d(kTag, "closeSerialPort: 已经是低电位,直接返回") - return - } - // 降低串口电位 - gpioManager.setGpioLow("18") - gpioState.set(0) - Log.d(kTag, "closeSerialPort: 降低串口电位") - } - - /***************************************************************************************/ private val samplePopupWindow by lazy { SamplePopupWindow(this) } private val detailDialog by lazy { MarkerDetailDialog(this) } private val locationKit by lazy { LocationKit(this) } @@ -133,7 +91,6 @@ private var freeTaskTitle = "" private var ids = HashSet() private var freeTaskId: String? = null - private var serialPortService: SerialPortService? = null private var soundResourceId = 0 private var isExecuteTask = false private lateinit var aMap: AMap @@ -146,43 +103,13 @@ binding.rootView.initImmersionBar(this, false, R.color.themeColor) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - openSerialPort() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - //地图初始化 initMapConfig(savedInstanceState) - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - Intent(this, NtripConnectService::class.java).also { - startService(it) - } +// Intent(this, NtripConnectService::class.java).also { +// startService(it) +// } soundResourceId = soundPool.load(this, R.raw.ring3, 1) //右上角菜单 @@ -211,7 +138,7 @@ showMarkersOnMap() } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() } @@ -446,7 +373,7 @@ override fun onConfirmClick() { isFreeTask = false soundPool.autoPause() - RuntimeCache.currentSegment = null + closeSerialPort() binding.stopFreeTaskButton.visibility = View.GONE if (freeTaskId.isNullOrBlank()) { @@ -499,11 +426,7 @@ } else { "此工单下无标识器,将执行自由巡检任务".show(context) freeTaskId = task.taskId - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } } @@ -524,21 +447,22 @@ freeTaskTitle = value val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(context, userId, value) - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } override fun onCancelClick() {} }).build().show() } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.FREE_INSPECTION) { - val buffer = msg.obj as ByteArray - //只响一次,因为探测频率高,所以依旧是连续的报警声 + private fun detectMarker() { + binding.stopFreeTaskButton.visibility = View.VISIBLE + isFreeTask = true + openSerialPort() + searchMarker(CurrentSegment.FreeInspection) + } + + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.FreeInspection) { soundPool.play(soundResourceId, 1f, 1f, 0, 0, 1f) when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { "4E" -> { @@ -552,27 +476,30 @@ val markerId = buffer.toMarkerId() //添加地图Marker if (!ids.contains(markerId)) { - //根据markerId查询标识器经纬度 - val labels = DataBaseManager.get.queryMarkerById(markerId) - if (labels.isNotEmpty()) { - val bean = labels.first() - aMap.addMarker( - MarkerOptions() - .position(LatLng(bean.lat.toDouble(), bean.lng.toDouble())) - .icon(BitmapDescriptorFactory.fromResource(R.mipmap.label_blue1)) + val markers = DataBaseManager.get.queryMarkerById(markerId) + if (markers.isNotEmpty()) { + val marker = markers.first() + val latLng = LatLng( + marker.lat.toDouble(), marker.lng.toDouble() ) + val resource = BitmapDescriptorFactory.fromResource( + R.mipmap.label_blue1 + ) + val options = MarkerOptions().position(latLng).icon(resource) + aMap.addMarker(options) } } ids.add(markerId) //显示标识器详细信息 if (!detailDialog.isShowing) { - val markerBean = DataBaseManager.get.queryMarkerById(markerId).firstOrNull() - if (markerBean == null) { - "无法查询到此ID【${markerId}】的信息".show(context) - } else { - detailDialog.setMarker(markerBean) - detailDialog.show() + DataBaseManager.get.queryMarkerById(markerId).firstOrNull().apply { + if (this == null) { + "无法查询到此ID【${markerId}】的信息".show(context) + } else { + detailDialog.setMarker(this) + detailDialog.show() + } } } } @@ -582,7 +509,6 @@ } } } - return true } override fun observeRequestState() { @@ -865,9 +791,6 @@ override fun onResume() { super.onResume() binding.mapView.onResume() - if (serialPortService != null) { - openSerialPort() - } } override fun onPause() { @@ -885,8 +808,6 @@ binding.mapView.onDestroy() soundPool.autoPause() locationKit.stopLocation() - unbindService(serviceConnection) - closeSerialPort() BaseApplication.get().closeSerialPort() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt index 9f2d5cd..0f1611d 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt @@ -74,7 +74,7 @@ locationManager.registerGnssStatusCallback(gnssStatusListener, null) satelliteAdapter = SatelliteRecyclerAdapter(this, satelliteCollection) binding.recyclerView.adapter = satelliteAdapter - binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(1, Color.WHITE)) + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.WHITE)) //高德定位 locationKit.getCurrentLocation(false, object : OnGetLocationListener { diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt index c7c8bb9..5417e1f 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt @@ -1,9 +1,5 @@ package com.casic.common.detector.gd.view -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection import android.graphics.Color import android.hardware.Sensor import android.hardware.SensorEvent @@ -13,9 +9,6 @@ import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.View import android.view.WindowManager @@ -25,28 +18,25 @@ import com.amap.api.maps.AMapUtils import com.amap.api.maps.model.LatLng import com.casic.common.detector.gd.R +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.databinding.ActivitySearchMarkerBinding import com.casic.common.detector.gd.extensions.toBuryDepth import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toSignalStrength import com.casic.common.detector.gd.model.MarkerDistanceData -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.RadarScanView -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.getSystemService import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.LiteKitConstant import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.dialog.AlertMessageDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -55,8 +45,8 @@ import java.util.TimerTask import kotlin.math.atan2 -class SearchMarkerActivity : KotlinBaseActivity(), - SensorEventListener, Handler.Callback { +class SearchMarkerActivity : SerialPortActivity(), + SensorEventListener { private val kTag = "SearchMarkerActivity" private val context = this @@ -76,38 +66,10 @@ private var signalEnergy = 0 //标识器信号强度 private var gravity: FloatArray? = null private var geomagnetic: FloatArray? = null - private var serialPortService: SerialPortService? = null - private lateinit var searchSignalEnergyTimer: Timer - - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定,开始搜索标识器") - searchMarker() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - val flag = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String - isExecuteTask = flag != "0" + val taskState = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String + isExecuteTask = taskState != "0" slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1) fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1) @@ -131,14 +93,11 @@ binding.taskStateView.isSelected = true } - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } + openSerialPort() + searchMarker(CurrentSegment.SearchMarker) //在标识器信号强度很强的时候,从数据库中计算出距离最近的点,以防出现探测不到ID的情况 - searchSignalEnergyTimer = Timer() - searchSignalEnergyTimer.schedule(object : TimerTask() { + Timer().schedule(object : TimerTask() { override fun run() { if (signalEnergy <= 700) {//18° runOnUiThread { @@ -183,21 +142,8 @@ }, 0, 2000) } - private val countDownTimer = object : CountDownTimer(10 * 1000, 1000) { - override fun onTick(millisUntilFinished: Long) { - - } - - override fun onFinish() { - LoadingDialog.dismiss() - "标识器深度探测超时,请移动位置重试".show(context) - searchMarker() - } - } - override fun initEvent() { binding.depthButton.setOnClickListener { - soundPool.autoPause() val result = DataBaseManager.get.queryMarkerById(markerId) if (result.isNotEmpty()) { val tag = when (result.first().markerType) { @@ -208,18 +154,25 @@ } if (tag == '1') { "此标识器无法读取埋深!".show(this) - searchMarker() } else { + soundPool.autoPause() LoadingDialog.show(this, "正在探测标识器埋深,请稍后...") - countDownTimer.start() // 发送读取标识器埋设深度指令 - RuntimeCache.currentSegment = CurrentSegment.DetectMarkerDepth - serialPortService?.writeCommand(tag) + detectDepth(tag) + object : CountDownTimer(3 * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + LoadingDialog.dismiss() + updateSegment(CurrentSegment.SearchMarker) + } + }.start() } } else { LoadingDialog.dismiss() "标识器未安装,安装成功后才可读取埋深!".show(this) - searchMarker() } } @@ -236,13 +189,11 @@ } } - override fun handleMessage(msg: Message): Boolean { - val buffer = msg.obj as ByteArray - if (msg.what == LocaleConstant.DETECT_MARKER_DEPTH) { - val tag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() - if (tag == "53") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.DetectDepth) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "53" && buffer.size == 5) { LoadingDialog.dismiss() - countDownTimer.cancel() try { AlertMessageDialog.Builder().setContext(context) .setTitle("温馨提示") @@ -251,54 +202,32 @@ .setOnDialogButtonClickListener(object : AlertMessageDialog.OnDialogButtonClickListener { override fun onConfirmClick() { - searchMarker() + updateSegment(CurrentSegment.SearchMarker) } }).build().show() } catch (e: WindowManager.BadTokenException) { e.printStackTrace() - } catch (e: NumberFormatException) { - e.printStackTrace() } } - } else if (msg.what == LocaleConstant.SEARCH_MARKER) { - try { - // 读取到信号强度 - when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { - "4E" -> { - val signalEnergy = buffer.toSignalStrength() + } else if (segment == CurrentSegment.SearchMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { + handleMarker(buffer.toMarkerId()) + } else if (flag == "4E" && buffer.size == 5) { + val signalEnergy = buffer.toSignalStrength() - //声音 - updateSound(signalEnergy) + //声音 + updateSound(signalEnergy) - //信号强度显示 - updateProgressBar(signalEnergy) + //信号强度显示 + updateProgressBar(signalEnergy) - //搜索信号文字显示 - updateTextResult(signalEnergy) - } - - "30" -> { - handleMarker(buffer.toMarkerId()) - } - - else -> { - Log.d(kTag, "乱码数据: ${buffer.contentToString()}") - } - } - } catch (e: Exception) { - e.printStackTrace() - Log.e(kTag, "Error processing buffer data", e) + //搜索信号文字显示 + updateTextResult(signalEnergy) + } else { + Log.d(kTag, "乱码数据: ${buffer.contentToString()}") } } - return true - } - - /** - * 搜索标识器 - * */ - private fun searchMarker() { - RuntimeCache.currentSegment = CurrentSegment.SearchMarker - serialPortService?.writeCommand('2', '6') } private fun updateSound(signalEnergy: Int) { @@ -492,9 +421,8 @@ override fun onDestroy() { super.onDestroy() + closeSerialPort() soundPool.autoPause() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt index ddc5a32..8641c79 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt @@ -13,17 +13,17 @@ import com.casic.common.detector.gd.databinding.ActivityVersionControlBinding import com.casic.common.detector.gd.extensions.appendDownloadUrl import com.casic.common.detector.gd.extensions.initImmersionBar -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType import com.casic.common.detector.gd.vm.VersionViewModel -import com.casic.common.detector.gd.widgets.ProgressDialog import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.ProgressDialog import java.io.File class VersionControlActivity : KotlinBaseActivity() { @@ -118,7 +118,7 @@ installApk(file) } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() progressDialog.dismiss() } diff --git a/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt b/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt deleted file mode 100644 index af480b0..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.casic.common.detector.gd.widgets - -import android.app.Dialog -import android.content.Context -import android.os.Bundle -import com.casic.common.detector.gd.databinding.DialogProgressBinding -import com.pengxh.kt.lite.R -import com.pengxh.kt.lite.extensions.binding -import com.pengxh.kt.lite.extensions.initDialogLayoutParams - -class ProgressDialog(context: Context) : Dialog(context, R.style.UserDefinedDialogStyle) { - - private val binding: DialogProgressBinding by binding() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - this.initDialogLayoutParams(0.5f) - setCanceledOnTouchOutside(false) - setCancelable(false) - binding.progressBar.progress = 0 - binding.progressText.text = "0 %" - } - - fun setMaxProgress(maxProgress: Long) { - binding.progressBar.max = maxProgress.toInt() - } - - private fun getMaxProgress() = binding.progressBar.max - - fun updateProgress(progress: Long) { - binding.progressBar.progress = progress.toInt() - - val percent = (progress.toFloat() / getMaxProgress()) * 100 - binding.progressText.text = String.format("%.2f %%", percent) - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_version_control.xml b/app/src/main/res/layout/activity_version_control.xml index 887248f..15f3a91 100644 --- a/app/src/main/res/layout/activity_version_control.xml +++ b/app/src/main/res/layout/activity_version_control.xml @@ -81,7 +81,6 @@ - diff --git a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt index 6cb496c..e5fe1f5 100644 --- a/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/common/detector/gd/base/BaseApplication.kt @@ -1,6 +1,7 @@ package com.casic.common.detector.gd.base import android.app.Application +import android.util.Log import com.casic.common.detector.gd.greendao.DaoMaster import com.casic.common.detector.gd.greendao.DaoSession import com.casic.common.detector.gd.uart.SerialPort @@ -52,6 +53,7 @@ //千寻报文数据下发串口号 add(SerialPort(File("/dev/ttyS1"), 9600, 0)) } + Log.d(kTag, "onCreate: 已初始化 ${serialPorts.size} 个串口") } catch (e: SecurityException) { "您没有串口的读写权限!".show(this) } catch (e: IOException) { diff --git a/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt new file mode 100644 index 0000000..08b3e19 --- /dev/null +++ b/app/src/main/java/com/casic/common/detector/gd/base/SerialPortActivity.kt @@ -0,0 +1,204 @@ +package com.casic.common.detector.gd.base + +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.casic.common.detector.gd.utils.CurrentSegment +import com.casic.common.detector.gd.utils.GpioManager +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import java.io.IOException +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicInteger + +abstract class SerialPortActivity : AppCompatActivity(), Handler.Callback { + + private val kTag = "SerialPortActivity" + private val taskTimer by lazy { Timer() } + private val weakReferenceHandler by lazy { WeakReferenceHandler(this) } + + //输出流 + private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } + + //输入流 + private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } + private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } + + private var segment: CurrentSegment? = null + + protected lateinit var binding: VB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = initViewBinding() + setContentView(binding.root) + setupTopBarLayout() + initOnCreate(savedInstanceState) + observeRequestState() + initEvent() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = firstInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + + Thread { + val buffer = ByteArray(128) + try { + while (!Thread.interrupted()) { + val bytesRead = lastInStream.read(buffer) + if (bytesRead > 0) { + weakReferenceHandler.let { + val message = it.obtainMessage() + message.what = 2025012001 + message.obj = buffer.copyOf(bytesRead) + it.sendMessage(message) + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + }.start() + } + + /** + * 初始化ViewBinding + */ + abstract fun initViewBinding(): VB + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initOnCreate(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + abstract fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) + + override fun handleMessage(msg: Message): Boolean { + if (msg.what == 2025012001) { + val bytes = msg.obj as ByteArray + if (segment == null) { + return true + } + Log.d(kTag, "$segment - ${bytes.contentToString()}") + onDataReceived(bytes, segment!!) + } + return true + } + + private var readMarkerIdTask: TimerTask? = null + private var searchMarkerTask: TimerTask? = null + + fun readMarkerId() { + this.segment = CurrentSegment.InstallMarker + readMarkerIdTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + } + } + taskTimer.schedule(readMarkerIdTask, 0, 300) + } + + fun detectDepth(tag: Char) { + this.segment = CurrentSegment.DetectDepth + outStream.write('3'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write(tag.code) + outStream.flush() + } + + fun updateSegment(segment: CurrentSegment) { + this.segment = segment + } + + fun searchMarker(segment: CurrentSegment) { + this.segment = segment + searchMarkerTask = object : TimerTask() { + override fun run() { + outStream.write('2'.code) + outStream.flush() + + Thread.sleep(50) + + outStream.write('6'.code) + outStream.flush() + } + } + taskTimer.schedule(searchMarkerTask, 0, 300) + } + + /***************************************************************************************/ + private val gpioManager by lazy { GpioManager() } + + /** + * 读取数据 1 + * 暂停读取 0 + * */ + private val gpioState = AtomicInteger(0) + fun openSerialPort() { + if (gpioState.get() != 1) { + gpioManager.setGpioHigh("18") + gpioState.set(1) + Log.d(kTag, "openSerialPort: 调高串口电位") + } else { + Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") + } + } + + fun closeSerialPort() { + if (segment != null) { + segment = null + } + readMarkerIdTask?.cancel() + searchMarkerTask?.cancel() + if (gpioState.get() == 0) { + Log.d(kTag, "closeSerialPort: 已经是低电位,不响应") + return + } + // 降低串口电位 + gpioManager.setGpioLow("18") + gpioState.set(0) + Log.d(kTag, "closeSerialPort: 降低串口电位") + } + + override fun onDestroy() { + super.onDestroy() + closeSerialPort() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt index 442189e..5c3ee01 100644 --- a/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt +++ b/app/src/main/java/com/casic/common/detector/gd/extensions/ByteArray.kt @@ -1,42 +1,20 @@ package com.casic.common.detector.gd.extensions -import android.util.Log - /** * 因为数据频率很高,而且每一帧数据至少有一个信号值,如果有多个信号强度值,取平均值 * * [78, 53, 49, 55, 51] * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * - * [78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51, 78, 53, 49, 55, 51] - * * */ fun ByteArray.toSignalStrength(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var strength = 0 + for (byte in dataBytes) { + strength = strength * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return strength } /** @@ -44,32 +22,13 @@ * * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51] * - * [48, 48, 48, 56, 56, 57, 49, 48, 55, 51, 48, 48, 48, 56, 56, 57, 49, 48, 55, 51] - * * */ fun ByteArray.toMarkerId(): String { - //判断数据合理性 - val validBytes = if (this.size % 10 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 10) * 10 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + val id = this.map { it.toInt().toChar() }.joinToString("") + if (id.matches("-?\\d+".toRegex())) { + return id } - - val list = ArrayList() - for (i in validBytes.indices step 10) { - // 每10个字节取一次数据转为ASCII - val tempBytes = validBytes.sliceArray(i until minOf(i + 10, validBytes.size)) - val string = tempBytes.map { it.toInt().toChar() }.joinToString("") - if (string.matches("-?\\d+".toRegex())) { - list.add(string) - } else { - Log.d("kTag", "toDeviceCode: 编号解析失败") - } - } - return list.last() + return "" } /** @@ -77,31 +36,14 @@ * * [83, 48, 48, 48, 57] * - * [83, 48, 48, 48, 57, 83, 48, 48, 48, 57] * */ fun ByteArray.toBuryDepth(): Int { - //判断数据合理性 - val validBytes = if (this.size % 5 == 0) { - this - } else { - // 计算最大能取到的5的倍数的数据长度 - val maxMultipleOfFour = (this.size / 5) * 5 - // 获取最大能取到的5的倍数的数据 - this.sliceArray(0 until maxMultipleOfFour) + // [78, 53, 49, 55, 51] + // 第一个字节是标志位,丢弃,将后面的数据转为十进制 + val dataBytes = this.sliceArray(1 until this.size) + var depth = 0 + for (byte in dataBytes) { + depth = depth * 10 + (byte - '0'.code.toByte()) } - - val list = ArrayList() - for (i in validBytes.indices step 5) { - // 每5个字节取一次数据 - val tempBytes = validBytes.sliceArray(i until minOf(i + 5, validBytes.size)) - // [78, 53, 49, 55, 51] - // 第一个字节是标志位,丢弃,将后面的数据转为十进制 - val dataBytes = tempBytes.sliceArray(1 until tempBytes.size) - var decimalValue = 0 - for (byte in dataBytes) { - decimalValue = decimalValue * 10 + (byte - '0'.code.toByte()) - } - list.add(decimalValue) - } - return list.average().toInt() + return depth } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt b/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt deleted file mode 100644 index 76cf583..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/service/SerialPortService.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.casic.common.detector.gd.service - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder -import android.util.Log -import com.casic.common.detector.gd.base.BaseApplication -import com.casic.common.detector.gd.utils.CurrentSegment -import com.casic.common.detector.gd.utils.LocaleConstant -import com.casic.common.detector.gd.utils.RuntimeCache -import com.casic.common.detector.gd.view.InstallMarkerActivity -import com.casic.common.detector.gd.view.MainActivity -import com.casic.common.detector.gd.view.SearchMarkerActivity -import java.io.IOException -import java.io.InputStream -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SerialPortService : Service() { - - private val kTag = "SerialPortService" - private val sendExecutor = Executors.newFixedThreadPool(5) - private val readExecutor = Executors.newSingleThreadScheduledExecutor() - - //输出流 - private val outStream by lazy { BaseApplication.get().getSerialPorts().first().outputStream } - - //输入流 - private val firstInStream by lazy { BaseApplication.get().getSerialPorts().first().inputStream } - private val lastInStream by lazy { BaseApplication.get().getSerialPorts().last().inputStream } - - override fun onCreate() { - super.onCreate() - try { - // 使用单线程调度器读取数据 - readExecutor.scheduleAtFixedRate({ - readData(firstInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - - readExecutor.scheduleAtFixedRate({ - readData(lastInStream) - }, 100, 150, TimeUnit.MILLISECONDS) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun readData(inputStream: InputStream) { - try { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.available() > 0) { - bytesRead = inputStream.read(buffer) - if (bytesRead > 0) { - when (RuntimeCache.currentSegment) { - CurrentSegment.DetectMarkerDepth -> { - //深度 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.DETECT_MARKER_DEPTH - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.InstallMarker -> { - //安装点 - InstallMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.INSTALL_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.FreeInspection -> { - //自由巡检 - MainActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.FREE_INSPECTION - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - CurrentSegment.SearchMarker -> { - //搜索标识器 - SearchMarkerActivity.weakReferenceHandler?.apply { - val message = obtainMessage() - message.what = LocaleConstant.SEARCH_MARKER - message.obj = buffer.copyOf(bytesRead) - sendMessage(message) - } - } - - else -> { - val bytes = buffer.copyOf(bytesRead) - Log.d(kTag, "空状态,不处理 ===> ${bytes.contentToString()}") - } - } - } - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun writeCommand(vararg command: Char) { - Log.d(kTag, "writeCommand: ${command.contentToString()}") - if (command.size == 1) { - sendExecutor.submit { - outStream.write(command.first().code) - outStream.flush() - } - } else { - command.forEach { - sendExecutor.submit { - outStream.write(it.code) - outStream.flush() - } - Thread.sleep(1000) - } - } - } - - override fun onBind(intent: Intent?): IBinder { - return ServiceBinder() - } - - inner class ServiceBinder : Binder() { - fun getSerialPortService(): SerialPortService { - return this@SerialPortService - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt index 0606b72..9efa097 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/CurrentSegment.kt @@ -1,23 +1,23 @@ package com.casic.common.detector.gd.utils -sealed class CurrentSegment { +enum class CurrentSegment { /** * 安装标识器 * */ - object InstallMarker : CurrentSegment() + InstallMarker, /** * 自由巡检 * */ - object FreeInspection : CurrentSegment() + FreeInspection, /** * 搜索标识器 * */ - object SearchMarker : CurrentSegment() + SearchMarker, /** * 探测标识器深度 * */ - object DetectMarkerDepth : CurrentSegment() + DetectDepth } diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt b/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt deleted file mode 100644 index c9151a8..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/FileDownloadManager.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.casic.common.detector.gd.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean - -class FileDownloadManager(builder: Builder) { - - private val httpClient by lazy { OkHttpClient() } - - class Builder { - lateinit var url: String - lateinit var suffix: String - lateinit var directory: File - lateinit var downloadListener: OnFileDownloadListener - - /** - * 文件下载地址 - * */ - fun setDownloadFileSource(url: String): Builder { - this.url = url - return this - } - - /** - * 文件后缀 - * 如:apk等 - * */ - fun setFileSuffix(suffix: String): Builder { - this.suffix = if (suffix.contains(".")) { - //去掉前缀的点 - suffix.drop(1) - } else { - suffix - } - return this - } - - /** - * 文件保存的地址 - * */ - fun setFileSaveDirectory(directory: File): Builder { - this.directory = directory - return this - } - - /** - * 设置文件下载回调监听 - * */ - fun setOnFileDownloadListener(downloadListener: OnFileDownloadListener): Builder { - this.downloadListener = downloadListener - return this - } - - fun build(): FileDownloadManager { - if (!::url.isInitialized || !::suffix.isInitialized || !::directory.isInitialized || !::downloadListener.isInitialized) { - throw IllegalStateException("All properties must be initialized before building.") - } - return FileDownloadManager(this) - } - } - - private val url = builder.url - private val suffix = builder.suffix - private val directory = builder.directory - private val listener = builder.downloadListener - - /** - * 开始下载 - * */ - fun start() { - val job = SupervisorJob() - val scope = CoroutineScope(Dispatchers.Main + job) - - val request = Request.Builder().get().url(url).build() - val newCall = httpClient.newCall(request) - val isExecuting = AtomicBoolean(false) - - /** - * 如果已被加入下载队列,则取消之前的,重新下载 - */ - if (isExecuting.getAndSet(true)) { - newCall.cancel() - } - - //异步下载文件 - newCall.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - scope.launch(Dispatchers.Main) { - listener.onFailed(e) - } - } - - override fun onResponse(call: Call, response: Response) { - scope.launch(Dispatchers.IO) { - val body = response.body - if (body == null) { - listener.onFailed(IOException("Response body is null")) - throw IOException("Response body is null") - } else { - val inputStream = body.byteStream() - val fileSize = body.contentLength() - - if (fileSize <= 0) { - throw IllegalArgumentException("Invalid file size") - } - listener.onDownloadStart(fileSize) - - val file = File(directory, "${System.currentTimeMillis()}.${suffix}") - file.outputStream().use { fos -> - val buffer = ByteArray(2048) - var sum = 0L - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - fos.write(buffer, 0, read) - sum += read.toLong() - withContext(Dispatchers.Main) { - listener.onProgressChanged(sum) - } - } - } - - withContext(Dispatchers.Main) { - listener.onDownloadEnd(file) - } - - job.cancel() - } - } - } - }) - } - - interface OnFileDownloadListener { - fun onDownloadStart(total: Long) - fun onProgressChanged(progress: Long) - fun onDownloadEnd(file: File) - fun onFailed(t: Throwable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt index 2334680..c658924 100644 --- a/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/common/detector/gd/utils/LocaleConstant.kt @@ -14,10 +14,6 @@ const val PERMISSIONS_CODE = 999 const val MAX_DISTANCE = 5.5f //表盘最大显示距离 - const val FREE_INSPECTION = 20250101 - const val INSTALL_MARKER = 20250102 - const val DETECT_MARKER_DEPTH = 20250103 - const val SEARCH_MARKER = 20250104 const val AUTO_SAVE = "AUTO_SAVE" const val USER_ACCOUNT = "USER_ACCOUNT" diff --git a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt b/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt deleted file mode 100644 index 104f971..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/utils/RuntimeCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.casic.common.detector.gd.utils - -object RuntimeCache { - var currentSegment: CurrentSegment? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt index c5f18a6..b3dce3c 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/InstallMarkerActivity.kt @@ -1,26 +1,19 @@ package com.casic.common.detector.gd.view import android.app.DatePickerDialog -import android.content.ComponentName -import android.content.Context import android.content.DialogInterface -import android.content.Intent -import android.content.ServiceConnection import android.hardware.Camera import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message -import android.util.Log import android.view.View import android.widget.AdapterView import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.common.detector.gd.R import com.casic.common.detector.gd.adapter.EditableImageAdapter +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.callback.OnImageCompressListener @@ -33,18 +26,15 @@ import com.casic.common.detector.gd.extensions.toColor import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toObjectType -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel 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.dateToTimestamp import com.pengxh.kt.lite.extensions.navigatePageTo @@ -55,13 +45,12 @@ import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import java.io.File import java.util.Calendar import java.util.Date -class InstallMarkerActivity : KotlinBaseActivity(), Handler.Callback { +class InstallMarkerActivity : SerialPortActivity() { private val kTag = "InstallMarkerActivity" private val context = this @@ -73,7 +62,6 @@ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build() private val soundPool = SoundPool.Builder().setMaxStreams(16).setAudioAttributes(attr).build() private var soundResourceId = 0 - private var serialPortService: SerialPortService? = null private lateinit var imageAdapter: EditableImageAdapter override fun initEvent() { @@ -255,18 +243,18 @@ //读标识器 binding.readMarkerButton.setOnClickListener { + openSerialPort() LoadingDialog.show(this, "标识器读取中,请稍后...") countDownTimer.start() binding.readMarkerButton.isEnabled = false - RuntimeCache.currentSegment = CurrentSegment.InstallMarker - serialPortService?.writeCommand('2') + readMarkerId() } } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.INSTALL_MARKER) { - val buffer = msg.obj as ByteArray - if ((buffer.first().toInt() and 0xFF).toString(16).uppercase() == "30") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.InstallMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { val markerId = buffer.toMarkerId() LoadingDialog.dismiss() countDownTimer.cancel() @@ -274,7 +262,6 @@ binding.identifierInclude.identifierIdView.text = markerId } } - return true } /** @@ -363,37 +350,7 @@ }) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - //在连接正常关闭的情况下不会被调用, 只在Service被破坏了或者被杀死的时候调用 - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - locationKit.getCurrentLocation(true, object : OnGetLocationListener { override fun onSuccess(location: AMapLocation) { binding.identifierInclude.lngView.text = location.longitude.toString() @@ -552,8 +509,6 @@ override fun onDestroy() { super.onDestroy() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt index 8a798b6..2c48cc9 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/MainActivity.kt @@ -1,18 +1,12 @@ package com.casic.common.detector.gd.view import android.app.Activity -import android.content.ComponentName -import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.graphics.BitmapFactory import android.graphics.Color import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.View @@ -34,6 +28,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.casic.common.detector.gd.R import com.casic.common.detector.gd.base.BaseApplication +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.bean.MarkerLocalBean import com.casic.common.detector.gd.bean.TaskLocalBean import com.casic.common.detector.gd.callback.OnGetAddressListener @@ -47,32 +42,27 @@ import com.casic.common.detector.gd.model.TaskDetailLocalModel import com.casic.common.detector.gd.model.TaskModel import com.casic.common.detector.gd.service.NtripConnectService -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.ExcelKit -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType -import com.casic.common.detector.gd.utils.GpioManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit import com.casic.common.detector.gd.utils.RouteOnMap -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.MarkerDetailDialog import com.casic.common.detector.gd.widgets.QueryMarkerDialog import com.casic.common.detector.gd.widgets.SamplePopupWindow -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.dp2px 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.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog @@ -82,45 +72,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.concurrent.atomic.AtomicInteger -class MainActivity : KotlinBaseActivity(), AMap.OnMarkerClickListener, - OnMapLongClickListener, Handler.Callback { +class MainActivity : SerialPortActivity(), AMap.OnMarkerClickListener, + OnMapLongClickListener { private val kTag = "MainActivity" private val context = this - - /***************************************************************************************/ - private val gpioManager by lazy { GpioManager() } - - /** - * 读取数据 1 - * 暂停读取 0 - * */ - private val gpioState = AtomicInteger(0) - private fun openSerialPort() { - if (gpioState.get() != 1) { - gpioManager.setGpioHigh("18") - gpioState.set(1) - Log.d(kTag, "openSerialPort: 调高串口电位") - } else { - Log.d(kTag, "openSerialPort: 已经是高电位,直接读数据") - } - } - - private fun closeSerialPort() { - if (gpioState.get() == 0) { - Log.d(kTag, "closeSerialPort: 已经是低电位,直接返回") - return - } - // 降低串口电位 - gpioManager.setGpioLow("18") - gpioState.set(0) - Log.d(kTag, "closeSerialPort: 降低串口电位") - } - - /***************************************************************************************/ private val samplePopupWindow by lazy { SamplePopupWindow(this) } private val detailDialog by lazy { MarkerDetailDialog(this) } private val locationKit by lazy { LocationKit(this) } @@ -133,7 +91,6 @@ private var freeTaskTitle = "" private var ids = HashSet() private var freeTaskId: String? = null - private var serialPortService: SerialPortService? = null private var soundResourceId = 0 private var isExecuteTask = false private lateinit var aMap: AMap @@ -146,43 +103,13 @@ binding.rootView.initImmersionBar(this, false, R.color.themeColor) } - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定") - openSerialPort() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } - override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - //地图初始化 initMapConfig(savedInstanceState) - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } - Intent(this, NtripConnectService::class.java).also { - startService(it) - } +// Intent(this, NtripConnectService::class.java).also { +// startService(it) +// } soundResourceId = soundPool.load(this, R.raw.ring3, 1) //右上角菜单 @@ -211,7 +138,7 @@ showMarkersOnMap() } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() } @@ -446,7 +373,7 @@ override fun onConfirmClick() { isFreeTask = false soundPool.autoPause() - RuntimeCache.currentSegment = null + closeSerialPort() binding.stopFreeTaskButton.visibility = View.GONE if (freeTaskId.isNullOrBlank()) { @@ -499,11 +426,7 @@ } else { "此工单下无标识器,将执行自由巡检任务".show(context) freeTaskId = task.taskId - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } } @@ -524,21 +447,22 @@ freeTaskTitle = value val userId = SaveKeyValues.getValue(LocaleConstant.USER_ID, "") as String taskViewModel.createFreeTask(context, userId, value) - - binding.stopFreeTaskButton.visibility = View.VISIBLE - isFreeTask = true - RuntimeCache.currentSegment = CurrentSegment.FreeInspection - serialPortService?.writeCommand('2', '6') + detectMarker() } override fun onCancelClick() {} }).build().show() } - override fun handleMessage(msg: Message): Boolean { - if (msg.what == LocaleConstant.FREE_INSPECTION) { - val buffer = msg.obj as ByteArray - //只响一次,因为探测频率高,所以依旧是连续的报警声 + private fun detectMarker() { + binding.stopFreeTaskButton.visibility = View.VISIBLE + isFreeTask = true + openSerialPort() + searchMarker(CurrentSegment.FreeInspection) + } + + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.FreeInspection) { soundPool.play(soundResourceId, 1f, 1f, 0, 0, 1f) when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { "4E" -> { @@ -552,27 +476,30 @@ val markerId = buffer.toMarkerId() //添加地图Marker if (!ids.contains(markerId)) { - //根据markerId查询标识器经纬度 - val labels = DataBaseManager.get.queryMarkerById(markerId) - if (labels.isNotEmpty()) { - val bean = labels.first() - aMap.addMarker( - MarkerOptions() - .position(LatLng(bean.lat.toDouble(), bean.lng.toDouble())) - .icon(BitmapDescriptorFactory.fromResource(R.mipmap.label_blue1)) + val markers = DataBaseManager.get.queryMarkerById(markerId) + if (markers.isNotEmpty()) { + val marker = markers.first() + val latLng = LatLng( + marker.lat.toDouble(), marker.lng.toDouble() ) + val resource = BitmapDescriptorFactory.fromResource( + R.mipmap.label_blue1 + ) + val options = MarkerOptions().position(latLng).icon(resource) + aMap.addMarker(options) } } ids.add(markerId) //显示标识器详细信息 if (!detailDialog.isShowing) { - val markerBean = DataBaseManager.get.queryMarkerById(markerId).firstOrNull() - if (markerBean == null) { - "无法查询到此ID【${markerId}】的信息".show(context) - } else { - detailDialog.setMarker(markerBean) - detailDialog.show() + DataBaseManager.get.queryMarkerById(markerId).firstOrNull().apply { + if (this == null) { + "无法查询到此ID【${markerId}】的信息".show(context) + } else { + detailDialog.setMarker(this) + detailDialog.show() + } } } } @@ -582,7 +509,6 @@ } } } - return true } override fun observeRequestState() { @@ -865,9 +791,6 @@ override fun onResume() { super.onResume() binding.mapView.onResume() - if (serialPortService != null) { - openSerialPort() - } } override fun onPause() { @@ -885,8 +808,6 @@ binding.mapView.onDestroy() soundPool.autoPause() locationKit.stopLocation() - unbindService(serviceConnection) - closeSerialPort() BaseApplication.get().closeSerialPort() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt index 9f2d5cd..0f1611d 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SatelliteStatusActivity.kt @@ -74,7 +74,7 @@ locationManager.registerGnssStatusCallback(gnssStatusListener, null) satelliteAdapter = SatelliteRecyclerAdapter(this, satelliteCollection) binding.recyclerView.adapter = satelliteAdapter - binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(1, Color.WHITE)) + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.WHITE)) //高德定位 locationKit.getCurrentLocation(false, object : OnGetLocationListener { diff --git a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt index c7c8bb9..5417e1f 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/SearchMarkerActivity.kt @@ -1,9 +1,5 @@ package com.casic.common.detector.gd.view -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection import android.graphics.Color import android.hardware.Sensor import android.hardware.SensorEvent @@ -13,9 +9,6 @@ import android.media.SoundPool import android.os.Bundle import android.os.CountDownTimer -import android.os.Handler -import android.os.IBinder -import android.os.Message import android.util.Log import android.view.View import android.view.WindowManager @@ -25,28 +18,25 @@ import com.amap.api.maps.AMapUtils import com.amap.api.maps.model.LatLng import com.casic.common.detector.gd.R +import com.casic.common.detector.gd.base.SerialPortActivity import com.casic.common.detector.gd.callback.OnGetLocationListener import com.casic.common.detector.gd.databinding.ActivitySearchMarkerBinding import com.casic.common.detector.gd.extensions.toBuryDepth import com.casic.common.detector.gd.extensions.toMarkerId import com.casic.common.detector.gd.extensions.toSignalStrength import com.casic.common.detector.gd.model.MarkerDistanceData -import com.casic.common.detector.gd.service.SerialPortService import com.casic.common.detector.gd.utils.CurrentSegment import com.casic.common.detector.gd.utils.DataBaseManager import com.casic.common.detector.gd.utils.LocaleConstant import com.casic.common.detector.gd.utils.LocationKit -import com.casic.common.detector.gd.utils.RuntimeCache import com.casic.common.detector.gd.vm.TaskViewModel import com.casic.common.detector.gd.widgets.RadarScanView -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.getSystemService import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.LiteKitConstant import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.dialog.AlertMessageDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -55,8 +45,8 @@ import java.util.TimerTask import kotlin.math.atan2 -class SearchMarkerActivity : KotlinBaseActivity(), - SensorEventListener, Handler.Callback { +class SearchMarkerActivity : SerialPortActivity(), + SensorEventListener { private val kTag = "SearchMarkerActivity" private val context = this @@ -76,38 +66,10 @@ private var signalEnergy = 0 //标识器信号强度 private var gravity: FloatArray? = null private var geomagnetic: FloatArray? = null - private var serialPortService: SerialPortService? = null - private lateinit var searchSignalEnergyTimer: Timer - - private val serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { - if (name != null && iBinder != null && iBinder is SerialPortService.ServiceBinder) { - try { - serialPortService = iBinder.getSerialPortService() - Log.d(kTag, "onServiceConnected: 服务已绑定,开始搜索标识器") - searchMarker() - } catch (e: Exception) { - Log.e(kTag, "onServiceConnected: 绑定服务时发生异常", e) - } - } else { - Log.w(kTag, "onServiceConnected: name 或 iBinder 为 null") - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - - } - } - - companion object { - var weakReferenceHandler: WeakReferenceHandler? = null - } override fun initOnCreate(savedInstanceState: Bundle?) { - weakReferenceHandler = WeakReferenceHandler(this) - - val flag = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String - isExecuteTask = flag != "0" + val taskState = intent.getStringExtra(LiteKitConstant.INTENT_PARAM_KEY) as String + isExecuteTask = taskState != "0" slowSoundResourceId = soundPool.load(this, R.raw.ring4, 1) fastSoundResourceId = soundPool.load(this, R.raw.ring2, 1) @@ -131,14 +93,11 @@ binding.taskStateView.isSelected = true } - //绑定串口通信服务 - Intent(this, SerialPortService::class.java).also { - bindService(it, serviceConnection, Context.BIND_AUTO_CREATE) - } + openSerialPort() + searchMarker(CurrentSegment.SearchMarker) //在标识器信号强度很强的时候,从数据库中计算出距离最近的点,以防出现探测不到ID的情况 - searchSignalEnergyTimer = Timer() - searchSignalEnergyTimer.schedule(object : TimerTask() { + Timer().schedule(object : TimerTask() { override fun run() { if (signalEnergy <= 700) {//18° runOnUiThread { @@ -183,21 +142,8 @@ }, 0, 2000) } - private val countDownTimer = object : CountDownTimer(10 * 1000, 1000) { - override fun onTick(millisUntilFinished: Long) { - - } - - override fun onFinish() { - LoadingDialog.dismiss() - "标识器深度探测超时,请移动位置重试".show(context) - searchMarker() - } - } - override fun initEvent() { binding.depthButton.setOnClickListener { - soundPool.autoPause() val result = DataBaseManager.get.queryMarkerById(markerId) if (result.isNotEmpty()) { val tag = when (result.first().markerType) { @@ -208,18 +154,25 @@ } if (tag == '1') { "此标识器无法读取埋深!".show(this) - searchMarker() } else { + soundPool.autoPause() LoadingDialog.show(this, "正在探测标识器埋深,请稍后...") - countDownTimer.start() // 发送读取标识器埋设深度指令 - RuntimeCache.currentSegment = CurrentSegment.DetectMarkerDepth - serialPortService?.writeCommand(tag) + detectDepth(tag) + object : CountDownTimer(3 * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + LoadingDialog.dismiss() + updateSegment(CurrentSegment.SearchMarker) + } + }.start() } } else { LoadingDialog.dismiss() "标识器未安装,安装成功后才可读取埋深!".show(this) - searchMarker() } } @@ -236,13 +189,11 @@ } } - override fun handleMessage(msg: Message): Boolean { - val buffer = msg.obj as ByteArray - if (msg.what == LocaleConstant.DETECT_MARKER_DEPTH) { - val tag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() - if (tag == "53") { + override fun onDataReceived(buffer: ByteArray, segment: CurrentSegment) { + if (segment == CurrentSegment.DetectDepth) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "53" && buffer.size == 5) { LoadingDialog.dismiss() - countDownTimer.cancel() try { AlertMessageDialog.Builder().setContext(context) .setTitle("温馨提示") @@ -251,54 +202,32 @@ .setOnDialogButtonClickListener(object : AlertMessageDialog.OnDialogButtonClickListener { override fun onConfirmClick() { - searchMarker() + updateSegment(CurrentSegment.SearchMarker) } }).build().show() } catch (e: WindowManager.BadTokenException) { e.printStackTrace() - } catch (e: NumberFormatException) { - e.printStackTrace() } } - } else if (msg.what == LocaleConstant.SEARCH_MARKER) { - try { - // 读取到信号强度 - when ((buffer.first().toInt() and 0xFF).toString(16).uppercase()) { - "4E" -> { - val signalEnergy = buffer.toSignalStrength() + } else if (segment == CurrentSegment.SearchMarker) { + val flag = (buffer.first().toInt() and 0xFF).toString(16).uppercase() + if (flag == "30" && buffer.size == 10) { + handleMarker(buffer.toMarkerId()) + } else if (flag == "4E" && buffer.size == 5) { + val signalEnergy = buffer.toSignalStrength() - //声音 - updateSound(signalEnergy) + //声音 + updateSound(signalEnergy) - //信号强度显示 - updateProgressBar(signalEnergy) + //信号强度显示 + updateProgressBar(signalEnergy) - //搜索信号文字显示 - updateTextResult(signalEnergy) - } - - "30" -> { - handleMarker(buffer.toMarkerId()) - } - - else -> { - Log.d(kTag, "乱码数据: ${buffer.contentToString()}") - } - } - } catch (e: Exception) { - e.printStackTrace() - Log.e(kTag, "Error processing buffer data", e) + //搜索信号文字显示 + updateTextResult(signalEnergy) + } else { + Log.d(kTag, "乱码数据: ${buffer.contentToString()}") } } - return true - } - - /** - * 搜索标识器 - * */ - private fun searchMarker() { - RuntimeCache.currentSegment = CurrentSegment.SearchMarker - serialPortService?.writeCommand('2', '6') } private fun updateSound(signalEnergy: Int) { @@ -492,9 +421,8 @@ override fun onDestroy() { super.onDestroy() + closeSerialPort() soundPool.autoPause() - RuntimeCache.currentSegment = null locationKit.stopLocation() - unbindService(serviceConnection) } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt b/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt index ddc5a32..8641c79 100644 --- a/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt +++ b/app/src/main/java/com/casic/common/detector/gd/view/VersionControlActivity.kt @@ -13,17 +13,17 @@ import com.casic.common.detector.gd.databinding.ActivityVersionControlBinding import com.casic.common.detector.gd.extensions.appendDownloadUrl import com.casic.common.detector.gd.extensions.initImmersionBar -import com.casic.common.detector.gd.utils.FileDownloadManager import com.casic.common.detector.gd.utils.FileType import com.casic.common.detector.gd.vm.VersionViewModel -import com.casic.common.detector.gd.widgets.ProgressDialog import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createDownloadFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.FileDownloadManager import com.pengxh.kt.lite.utils.LoadState import com.pengxh.kt.lite.utils.LoadingDialog import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.ProgressDialog import java.io.File class VersionControlActivity : KotlinBaseActivity() { @@ -118,7 +118,7 @@ installApk(file) } - override fun onFailed(t: Throwable) { + override fun onDownloadFailed(t: Throwable) { t.printStackTrace() progressDialog.dismiss() } diff --git a/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt b/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt deleted file mode 100644 index af480b0..0000000 --- a/app/src/main/java/com/casic/common/detector/gd/widgets/ProgressDialog.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.casic.common.detector.gd.widgets - -import android.app.Dialog -import android.content.Context -import android.os.Bundle -import com.casic.common.detector.gd.databinding.DialogProgressBinding -import com.pengxh.kt.lite.R -import com.pengxh.kt.lite.extensions.binding -import com.pengxh.kt.lite.extensions.initDialogLayoutParams - -class ProgressDialog(context: Context) : Dialog(context, R.style.UserDefinedDialogStyle) { - - private val binding: DialogProgressBinding by binding() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - this.initDialogLayoutParams(0.5f) - setCanceledOnTouchOutside(false) - setCancelable(false) - binding.progressBar.progress = 0 - binding.progressText.text = "0 %" - } - - fun setMaxProgress(maxProgress: Long) { - binding.progressBar.max = maxProgress.toInt() - } - - private fun getMaxProgress() = binding.progressBar.max - - fun updateProgress(progress: Long) { - binding.progressBar.progress = progress.toInt() - - val percent = (progress.toFloat() / getMaxProgress()) * 100 - binding.progressText.text = String.format("%.2f %%", percent) - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_version_control.xml b/app/src/main/res/layout/activity_version_control.xml index 887248f..15f3a91 100644 --- a/app/src/main/res/layout/activity_version_control.xml +++ b/app/src/main/res/layout/activity_version_control.xml @@ -81,7 +81,6 @@ - - - - - - - - - \ No newline at end of file