瀏覽代碼

add(新增)
- 初始化,设备扫描

周文健 5 月之前
父節點
當前提交
3933a2bf07
共有 30 個文件被更改,包括 232 次插入101 次删除
  1. 6 4
      app/src/main/java/com/grkj/iscs/ISCSApplication.kt
  2. 13 4
      app/src/main/java/com/grkj/iscs/features/init/fragment/InitDeviceRegistrationKeyAndLockFragment.kt
  3. 1 0
      app/src/main/java/com/grkj/iscs/features/init/fragment/InitPointRfidRegistrationFragment.kt
  4. 4 0
      app/src/main/java/com/grkj/iscs/features/init/fragment/InitSetAdminAccountFragment.kt
  5. 12 0
      app/src/main/java/com/grkj/iscs/features/init/fragment/InitWelcomeFragment.kt
  6. 11 0
      app/src/main/java/com/grkj/iscs/features/init/viewmodel/InitDeviceRegistrationKeyAndLockViewModel.kt
  7. 3 0
      app/src/main/java/com/grkj/iscs/features/init/viewmodel/InitViewModel.kt
  8. 1 0
      app/src/main/res/layout/fragment_init_set_admin_account.xml
  9. 7 5
      app/src/main/res/layout/item_device_registration_key.xml
  10. 5 4
      app/src/main/res/layout/item_device_registration_lock.xml
  11. 1 0
      app/src/main/res/values-en/strings.xml
  12. 1 0
      app/src/main/res/values-zh/strings.xml
  13. 1 0
      app/src/main/res/values/strings.xml
  14. 2 2
      data/src/main/java/com/grkj/data/dao/HardwareDao.kt
  15. 10 7
      data/src/main/java/com/grkj/data/database/ISCSDatabase.kt
  16. 6 0
      data/src/main/java/com/grkj/data/database/ISCSMigrations.kt
  17. 1 0
      data/src/main/java/com/grkj/data/di/AppEntryPoint.kt
  18. 2 10
      data/src/main/java/com/grkj/data/di/DatabaseModule.kt
  19. 1 0
      data/src/main/java/com/grkj/data/di/RepositoryManager.kt
  20. 1 0
      data/src/main/java/com/grkj/data/repository/impl/HardwareRepository.kt
  21. 二進制
      shared/libs/adh_series_sdk.jar
  22. 2 0
      shared/src/main/java/com/grkj/shared/config/Constants.kt
  23. 1 1
      ui-base/build.gradle.kts
  24. 13 6
      ui-base/src/main/java/com/grkj/ui_base/business/ModbusBusinessManager.kt
  25. 5 0
      ui-base/src/main/java/com/grkj/ui_base/config/ISCSConfig.kt
  26. 1 1
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleConnectionManager.kt
  27. 2 2
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleUtil.kt
  28. 17 17
      ui-base/src/main/java/com/grkj/ui_base/utils/modbus/ModBusController.kt
  29. 2 1
      ui-base/src/main/java/com/grkj/ui_base/utils/modbus/ModBusManager.kt
  30. 100 37
      ui-base/src/main/java/com/grkj/ui_base/utils/modbus/PortManager.kt

+ 6 - 4
app/src/main/java/com/grkj/iscs/ISCSApplication.kt

@@ -43,8 +43,8 @@ class ISCSApplication : Application() {
         if (!EventBus.getDefault().isRegistered(this)) {
             EventBus.getDefault().register(this)
         }
+        BleUtil.instance.initBle(this)
         ThreadUtils.runOnIO {
-            BleUtil.instance.initBle(this@ISCSApplication)
             RepositoryManager.init(this@ISCSApplication)
         }
     }
@@ -53,9 +53,11 @@ class ISCSApplication : Application() {
     open fun onEvent(event: EventBean<*>) {
         when (event.code) {
             EventConstants.EVENT_START_MODBUS -> {
-                ModBusController.start()
-                ModBusController.initDevicesStatus()
-                ModbusBusinessManager.registerMainListener()
+                ThreadUtils.runOnIO {
+                    ModBusController.start()
+                    ModBusController.initDevicesStatus()
+                    ModbusBusinessManager.registerMainListener()
+                }
             }
         }
     }

+ 13 - 4
app/src/main/java/com/grkj/iscs/features/init/fragment/InitDeviceRegistrationKeyAndLockFragment.kt

@@ -4,6 +4,8 @@ import android.widget.LinearLayout
 import androidx.core.view.isVisible
 import androidx.fragment.app.viewModels
 import com.drake.brv.BindingAdapter
+import com.drake.brv.annotaion.DividerOrientation
+import com.drake.brv.utils.dividerSpace
 import com.drake.brv.utils.linear
 import com.drake.brv.utils.models
 import com.drake.brv.utils.setup
@@ -19,6 +21,7 @@ import com.grkj.ui_base.base.BaseFragment
 import com.grkj.ui_base.utils.modbus.DeviceConst
 import com.grkj.ui_base.utils.modbus.DockBean
 import com.grkj.ui_base.utils.modbus.ModBusController
+import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikcore.extension.setDebouncedClickListener
 import dagger.hilt.android.AndroidEntryPoint
 
@@ -34,8 +37,6 @@ class InitDeviceRegistrationKeyAndLockFragment :
     }
 
     override fun initView() {
-        viewModel.openAndDetectSlave()
-        //打开所有钥匙仓并关闭充电
         viewModel.registerInitListener().observe(this) {
             logger.debug("设备录入-初始化检测任务分发完成")
         }
@@ -46,11 +47,15 @@ class InitDeviceRegistrationKeyAndLockFragment :
             navController.popBackStack()
         }
         binding.nextBtn.setDebouncedClickListener {
+            if (viewModel.isLoadComplete.value == false) {
+                PopTip.tip(R.string.device_in_detect)
+                return@setDebouncedClickListener
+            }
             viewModel.deviceRegistrationData().observe(this) {
                 navController.navigate(R.id.action_initDeviceRegistrationKeyAndLockFragment_to_initCardRegistrationFragment)
             }
         }
-        binding.dockRv.linear().setup {
+        binding.dockRv.linear().dividerSpace(10, DividerOrientation.GRID).setup {
             addType<DockData.KeyDock>(R.layout.item_device_registration_key_layout)
             addType<DockData.LockDock>(R.layout.item_device_registration_lock_layout)
             addType<DockData.PortableDock>(R.layout.item_device_registration_portable_layout)
@@ -78,6 +83,9 @@ class InitDeviceRegistrationKeyAndLockFragment :
                     keyData.addAll(it.deviceList.filterIsInstance<DockBean.KeyBean>())
                 }
             }
+            val keyDatas = keyDock.map { it.keyData }.flatten()
+            val newKeyDock = DockData.KeyDock()
+            newKeyDock.keyData.addAll(keyDatas)
             val lockDock = dockData.filter { it.type == DeviceConst.DOCK_TYPE_LOCK }.map {
                 DockData.LockDock().apply {
                     lockData.addAll(it.deviceList.filterIsInstance<DockBean.LockBean>())
@@ -88,7 +96,7 @@ class InitDeviceRegistrationKeyAndLockFragment :
                     deviceData.addAll(it.deviceList.toList())
                 }
             }
-            binding.dockRv.models = keyDock + portableDock + lockDock
+            binding.dockRv.models = listOf(newKeyDock) + portableDock + lockDock
         }
     }
 
@@ -164,6 +172,7 @@ class InitDeviceRegistrationKeyAndLockFragment :
     override fun onDestroyView() {
         viewModel.isDestroy = true
         viewModel.unregisterInitListener()
+        viewModel.closeModbus()
         super.onDestroyView()
     }
 }

+ 1 - 0
app/src/main/java/com/grkj/iscs/features/init/fragment/InitPointRfidRegistrationFragment.kt

@@ -13,6 +13,7 @@ import com.grkj.iscs.features.init.viewmodel.InitViewModel
 import com.grkj.iscs.features.login.activity.LoginActivity
 import com.grkj.shared.model.EventBean
 import com.grkj.ui_base.base.BaseFragment
+import com.grkj.ui_base.config.ISCSConfig
 import com.grkj.ui_base.utils.event.CardSwipeEvent
 import com.sik.sikcore.extension.setDebouncedClickListener
 import kotlin.getValue

+ 4 - 0
app/src/main/java/com/grkj/iscs/features/init/fragment/InitSetAdminAccountFragment.kt

@@ -8,6 +8,7 @@ import com.grkj.ui_base.base.BaseFragment
 import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikcore.extension.setDebouncedClickListener
 import dagger.hilt.android.AndroidEntryPoint
+import razerdp.util.KeyboardUtils
 
 /**
  * 设置管理员账号
@@ -21,6 +22,9 @@ class InitSetAdminAccountFragment : BaseFragment<FragmentInitSetAdminAccountBind
     }
 
     override fun initView() {
+        binding.mainContainer.setDebouncedClickListener {
+            KeyboardUtils.close(requireActivity())
+        }
         binding.previousBtn.setDebouncedClickListener {
             navController.popBackStack()
         }

+ 12 - 0
app/src/main/java/com/grkj/iscs/features/init/fragment/InitWelcomeFragment.kt

@@ -1,19 +1,31 @@
 package com.grkj.iscs.features.init.fragment
 
+import androidx.fragment.app.viewModels
 import com.grkj.iscs.R
 import com.grkj.iscs.databinding.FragmentInitWelcomeBinding
+import com.grkj.iscs.features.init.viewmodel.InitViewModel
 import com.grkj.ui_base.base.BaseFragment
 import com.sik.sikcore.extension.setDebouncedClickListener
+import com.tencent.mmkv.MMKV
+import dagger.hilt.android.AndroidEntryPoint
 
 /**
  * 初始化欢迎界面
  */
+@AndroidEntryPoint
 class InitWelcomeFragment : BaseFragment<FragmentInitWelcomeBinding>() {
+    private val viewModel: InitViewModel by viewModels()
     override fun getLayoutId(): Int {
         return R.layout.fragment_init_welcome
     }
 
     override fun initView() {
+        binding.titleCn.setDebouncedClickListener {
+            MMKV.defaultMMKV().clearAll()
+        }
+        binding.titleEn.setDebouncedClickListener {
+
+        }
         binding.startBtn.setDebouncedClickListener {
             navController.navigate(R.id.action_initWelcomeFragment_to_initSetAdminAccountFragment)
         }

+ 11 - 0
app/src/main/java/com/grkj/iscs/features/init/viewmodel/InitDeviceRegistrationKeyAndLockViewModel.kt

@@ -6,6 +6,7 @@ import androidx.lifecycle.liveData
 import com.clj.fastble.BleManager
 import com.grkj.data.repository.IHardwareRepository
 import com.grkj.ui_base.base.BaseViewModel
+import com.grkj.ui_base.business.ModbusBusinessManager
 import com.grkj.ui_base.utils.ble.BleConnectionManager
 import com.grkj.ui_base.utils.modbus.DeviceConst
 import com.grkj.ui_base.utils.modbus.DockBean
@@ -15,6 +16,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.suspendCancellableCoroutine
+import razerdp.util.KeyboardUtils
 import java.util.concurrent.atomic.AtomicInteger
 import javax.inject.Inject
 import kotlin.coroutines.resume
@@ -35,6 +37,8 @@ class InitDeviceRegistrationKeyAndLockViewModel @Inject constructor(val hardware
      */
     fun openAndDetectSlave() {
         ModBusController.start()
+        ModBusController.initDevicesStatus()
+        ModbusBusinessManager.registerMainListener()
     }
 
     fun checkNewHardware(device: DockBean.DeviceBean, callback: () -> Unit) {
@@ -55,6 +59,7 @@ class InitDeviceRegistrationKeyAndLockViewModel @Inject constructor(val hardware
 
     fun registerInitListener(): LiveData<Boolean> {
         return liveData(Dispatchers.IO) {
+            openAndDetectSlave()
             isStartCheckKey = false
             newHardwareKeyBean.clear()
             alreadyUsedMac.clear()
@@ -63,6 +68,7 @@ class InitDeviceRegistrationKeyAndLockViewModel @Inject constructor(val hardware
                 BleConnectionManager.scanOnlineKeyLockMacAndSwitchModeToClose()
             logger.info("设备录入-是否所有关闭命令发送成功:${allDeviceCloseCmdSend}")
             BleManager.getInstance().disconnectAllDevice()
+            logger.info("断开所有蓝牙设备")
             ModBusController.registerStatusListener(this) {
                 if (isStartCheckKey) {
                     return@registerStatusListener
@@ -253,4 +259,9 @@ class InitDeviceRegistrationKeyAndLockViewModel @Inject constructor(val hardware
             emit(true)
         }
     }
+
+    fun closeModbus() {
+        ModBusController.stop()
+        ModbusBusinessManager.unregisterMainListener()
+    }
 }

+ 3 - 0
app/src/main/java/com/grkj/iscs/features/init/viewmodel/InitViewModel.kt

@@ -3,9 +3,12 @@ package com.grkj.iscs.features.init.viewmodel
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.liveData
+import com.grkj.data.database.ISCSDatabase
+import com.grkj.data.di.RepositoryManager
 import com.grkj.data.repository.IHardwareRepository
 import com.grkj.data.repository.IUserRepository
 import com.grkj.ui_base.base.BaseViewModel
+import com.sik.sikcore.thread.ThreadUtils
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
 import javax.inject.Inject

+ 1 - 0
app/src/main/res/layout/fragment_init_set_admin_account.xml

@@ -3,6 +3,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
     <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/main_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="@drawable/bg_card_white_r8"

+ 7 - 5
app/src/main/res/layout/item_device_registration_key.xml

@@ -2,13 +2,13 @@
 <layout xmlns:android="http://schemas.android.com/apk/res/android">
 
     <RelativeLayout
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content">
 
         <ImageView
             android:id="@+id/iv_key"
-            android:layout_width="50dp"
-            android:layout_height="35dp"
+            android:layout_width="150dp"
+            android:layout_height="105dp"
             android:layout_centerHorizontal="true"
             android:background="@drawable/dock_key_selector" />
 
@@ -18,7 +18,8 @@
             android:layout_below="@+id/iv_key"
             android:layout_alignLeft="@+id/iv_key"
             android:layout_alignRight="@+id/iv_key"
-            android:layout_marginTop="5dp"
+            android:layout_marginTop="15dp"
+            android:textSize="@dimen/common_text_size"
             android:background="@drawable/common_btn_red_bg"
             android:text="@string/new_device"
             android:visibility="invisible" />
@@ -29,7 +30,8 @@
             android:layout_below="@+id/tv_new_device"
             android:layout_alignLeft="@+id/iv_key"
             android:layout_alignRight="@+id/iv_key"
-            android:layout_marginTop="5dp"
+            android:layout_marginTop="15dp"
+            android:textSize="@dimen/common_text_size"
             android:background="@drawable/common_btn_green_bg"
             android:visibility="invisible" />
 

+ 5 - 4
app/src/main/res/layout/item_device_registration_lock.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout  xmlns:android="http://schemas.android.com/apk/res/android">
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
 
     <RelativeLayout
         android:layout_width="wrap_content"
@@ -9,8 +9,8 @@
 
         <FrameLayout
             android:id="@+id/root"
-            android:layout_width="20dp"
-            android:layout_height="70dp"
+            android:layout_width="60dp"
+            android:layout_height="210dp"
             android:background="@drawable/dock_lock_selector" />
 
         <TextView
@@ -19,9 +19,10 @@
             android:layout_below="@+id/root"
             android:layout_alignLeft="@+id/root"
             android:layout_alignRight="@+id/root"
-            android:layout_marginTop="5dp"
+            android:layout_marginTop="15dp"
             android:background="@drawable/common_btn_red_bg"
             android:text="@string/new_device"
+            android:textSize="@dimen/common_text_size"
             android:visibility="invisible" />
     </RelativeLayout>
 

+ 1 - 0
app/src/main/res/values-en/strings.xml

@@ -314,5 +314,6 @@
     <string name="card_code">Card code</string>
     <string name="username">Username</string>
     <string name="card_manage_title">Card Manage</string>
+    <string name="device_in_detect">In device recognition</string>
 
 </resources>

+ 1 - 0
app/src/main/res/values-zh/strings.xml

@@ -314,5 +314,6 @@
     <string name="card_code">卡片名称</string>
     <string name="username">用户名称</string>
     <string name="card_manage_title">卡片管理</string>
+    <string name="device_in_detect">设备识别中</string>
 
 </resources>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -317,5 +317,6 @@
     <string name="card_code">卡片名称</string>
     <string name="username">用户名称</string>
     <string name="card_manage_title">卡片管理</string>
+    <string name="device_in_detect">设备识别中</string>
 
 </resources>

+ 2 - 2
data/src/main/java/com/grkj/data/dao/HardwareDao.kt

@@ -41,13 +41,13 @@ interface HardwareDao {
      * 根据rfid获取钥匙信息
      */
     @Query("select * from is_lock where lock_nfc = :rfid")
-    fun getLockInfoByRfid(rfid: String): IsLock
+    fun getLockInfoByRfid(rfid: String): IsLock?
 
     /**
      * 根据钥匙rfid获取钥匙信息
      */
     @Query("select * from is_key where key_nfc = :rfid")
-    fun getKeyInfoByRfid(rfid: String): IsKey
+    fun getKeyInfoByRfid(rfid: String): IsKey?
 
     /**
      * 更新挂锁取出

+ 10 - 7
data/src/main/java/com/grkj/data/database/ISCSDatabase.kt

@@ -1,5 +1,6 @@
 package com.grkj.data.database
 
+import androidx.room.AutoMigration
 import androidx.room.Database
 import androidx.room.Room
 import androidx.room.RoomDatabase
@@ -24,7 +25,6 @@ import com.grkj.data.model.dos.IsKey
 import com.grkj.data.model.dos.IsLock
 import com.grkj.data.model.dos.IsLockCabinet
 import com.grkj.data.model.dos.IsLockCabinetSlots
-import com.grkj.data.model.dos.IsLockType
 import com.grkj.data.model.dos.IsLockset
 import com.grkj.data.model.dos.IsLocksetType
 import com.grkj.data.model.dos.IsRfidToken
@@ -39,6 +39,7 @@ import com.grkj.data.model.dos.SysRoleMenu
 import com.grkj.data.model.dos.SysUserCharacteristicDo
 import com.grkj.data.model.dos.SysUserDo
 import com.grkj.data.model.dos.SysUserRole
+import com.grkj.shared.config.Constants
 import com.sik.sikcore.SIKCore
 
 /**
@@ -52,7 +53,8 @@ import com.sik.sikcore.SIKCore
         IsJobTicketPoints::class, IsJobTicketStep::class, IsJobTicketUser::class,
         IsKey::class, IsLock::class, IsLockCabinet::class, IsLockCabinetSlots::class, IsLocksetType::class, IsLockset::class, SysMenu::class, SysRoleMenu::class
     ],
-    version = ISCSMigrations.VERSION
+    version = ISCSMigrations.VERSION,
+    exportSchema = true
 )
 abstract class ISCSDatabase : RoomDatabase() {
     companion object {
@@ -63,11 +65,12 @@ abstract class ISCSDatabase : RoomDatabase() {
         val instance: ISCSDatabase by lazy {
             Room.databaseBuilder(
                 SIKCore.getApplication(), ISCSDatabase::class.java, "iscs_database"
-            ).createFromAsset("data.db")
-                // 如需在主线程查询,可取消下行注释(不推荐):
-                // .allowMainThreadQueries()
-//                .fallbackToDestructiveMigration(true)
-                .build()
+            ).apply {
+                if (Constants.DEBUG) {
+                    fallbackToDestructiveMigration(true)
+                }
+
+            }.createFromAsset("data.db").build()
         }
     }
 

+ 6 - 0
data/src/main/java/com/grkj/data/database/ISCSMigrations.kt

@@ -1,5 +1,11 @@
 package com.grkj.data.database
 
+import android.util.Log
+import androidx.annotation.NonNull
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+
 /**
  * 数据库升级、版本相关信息
  */

+ 1 - 0
data/src/main/java/com/grkj/data/di/AppEntryPoint.kt

@@ -1,5 +1,6 @@
 package com.grkj.data.di
 
+import com.grkj.data.database.ISCSDatabase
 import com.grkj.data.repository.IHardwareRepository
 import com.grkj.data.repository.IIsolationPointRepository
 import com.grkj.data.repository.IJobTicketRepository

+ 2 - 10
data/src/main/java/com/grkj/data/di/DatabaseModule.kt

@@ -25,16 +25,8 @@ object DatabaseModule {
 
     @Provides
     @Singleton
-    fun provideDatabase(
-        @ApplicationContext ctx: Context
-    ): ISCSDatabase {
-        return Room.databaseBuilder(
-            ctx,
-            ISCSDatabase::class.java,
-            "iscs_database"
-        )
-            .createFromAsset("data.db")
-            .build()
+    fun provideDatabase(): ISCSDatabase {
+        return ISCSDatabase.instance
     }
 
     @Provides

+ 1 - 0
data/src/main/java/com/grkj/data/di/RepositoryManager.kt

@@ -1,6 +1,7 @@
 package com.grkj.data.di
 
 import android.app.Application
+import com.grkj.data.database.ISCSDatabase
 import com.grkj.data.repository.IHardwareRepository
 import com.grkj.data.repository.IIsolationPointRepository
 import com.grkj.data.repository.IJobTicketRepository

+ 1 - 0
data/src/main/java/com/grkj/data/repository/impl/HardwareRepository.kt

@@ -331,4 +331,5 @@ class HardwareRepository @Inject constructor(
             offset
         )
     }
+
 }

二進制
shared/libs/adh_series_sdk.jar


+ 2 - 0
shared/src/main/java/com/grkj/shared/config/Constants.kt

@@ -27,6 +27,8 @@ object Constants {
     const val USER_TYPE_LOCKER = "0"                // 上锁人
     const val USER_TYPE_COLOCKER = "1"              // 共锁人
 
+    const val DEBUG: Boolean = true
+
     /*************************  虹软ArcSoft  *************************/
     const val APP_ID = "FTN3G4pk8n2RKwjD955sRapRjbYQFefwhHd4sBZMYEz6"
     const val SDK_KEY = "BjJomNU2bQc2SYhT7NNqwvFd3WD4wTqecifNcDRuKD5G"

+ 1 - 1
ui-base/build.gradle.kts

@@ -65,7 +65,7 @@ dependencies {
     api("io.github.scwang90:refresh-layout-kernel:3.0.0-alpha")
     api("io.github.scwang90:refresh-header-classics:3.0.0-alpha")
     api("io.github.scwang90:refresh-footer-classics:3.0.0-alpha")
-    api("com.licheedev:android-serialport:2.1.5")
+//    api("com.licheedev:android-serialport:2.1.5")
     implementation(project(":data"))
     implementation(project(":shared"))
     testImplementation(libs.junit)

+ 13 - 6
ui-base/src/main/java/com/grkj/ui_base/business/ModbusBusinessManager.kt

@@ -7,6 +7,7 @@ import com.grkj.data.model.req.LockTakeUpdateReq
 import com.grkj.ui_base.R
 import com.grkj.data.data.DictConstants
 import com.grkj.data.data.MainDomainData
+import com.grkj.ui_base.config.ISCSConfig
 import com.grkj.ui_base.listeners.DeviceListener
 import com.grkj.ui_base.utils.CommonUtils
 import com.grkj.ui_base.utils.Executor
@@ -291,15 +292,20 @@ object ModbusBusinessManager {
                             ModBusController.readKeyRfid(
                                 dockBean.addr, keyBean.idx
                             ) { idx, res ->
-                                ModBusController.controlKeyCharge(
-                                    true, keyBean.idx, dockBean.addr
-                                )
+                                if (ISCSConfig.isInit){
+                                    ModBusController.controlKeyCharge(
+                                        true, keyBean.idx, dockBean.addr
+                                    )
+                                }
                                 if (res.size < 11) {
                                     logger.error("Key rfid error")
                                     return@readKeyRfid
                                 }
                                 val rfid =
                                     res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
+                                ModBusController.updateKeyRfid(
+                                    dockBean.addr, keyBean.idx, rfid
+                                )
                                 ThreadUtils.runOnIO {
                                     val slotStatus =
                                         async { DataBusiness.fetchDict(DictConstants.KEY_SLOT_STATUS) }
@@ -330,9 +336,6 @@ object ModbusBusinessManager {
                                             CommonUtils.getStr(R.string.slot_exception_tag)
                                         )
                                     } else {
-                                        ModBusController.updateKeyRfid(
-                                            dockBean.addr, keyBean.idx, rfid
-                                        )
                                         // 放回钥匙,上锁
                                         ModBusController.controlKeyBuckle(
                                             false, keyBean.idx, dockBean.addr
@@ -566,4 +569,8 @@ object ModbusBusinessManager {
             deviceStatusHandle(res)
         }
     }
+
+    fun unregisterMainListener() {
+        ModBusController.unregisterListener(this)
+    }
 }

+ 5 - 0
ui-base/src/main/java/com/grkj/ui_base/config/ISCSConfig.kt

@@ -12,6 +12,11 @@ object ISCSConfig {
      */
     var isTestMode: Boolean = false
 
+    /**
+     * 是否初始化中
+     */
+    val isInit: Boolean get() = MMKVConstants.APP_INIT.getMMKVData(false)
+
     /**
      * 是否开启区域
      */

+ 1 - 1
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleConnectionManager.kt

@@ -650,7 +650,7 @@ object BleConnectionManager {
      */
     suspend fun scanOnlineKeyLockMacAndSwitchModeToClose(): Boolean {
         return suspendCancellableCoroutine { parentCont ->
-            BleUtil.instance?.scan(object : CustomBleScanCallback() {
+            BleUtil.instance.scan(object : CustomBleScanCallback() {
                 override fun onPrompt(promptStr: String?) {
                     // 蓝牙未启动重试
                     logger.debug("设备录入-参数:${promptStr}")

+ 2 - 2
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleUtil.kt

@@ -26,10 +26,10 @@ class BleUtil private constructor() {
         const val OPERATE_TIMEOUT = 10 * 1000
     }
 
-    fun initBle(application: Application?) {
+    fun initBle(application: Application) {
         try {
             BleManager.getInstance().init(application)
-            BleManager.getInstance().enableLog(false).setConnectOverTime(10 * 1000L)
+            BleManager.getInstance().enableLog(true).setConnectOverTime(10 * 1000L)
                 .setReConnectCount(3, 300) // 设置重新连接次数和间隔时间,默认为0次,不重连
                 .setSplitWriteNum(500).operateTimeout =
                 OPERATE_TIMEOUT // 设置操作readRssi、setMtu、write、read、notify、indicate的超时时间(毫秒)

+ 17 - 17
ui-base/src/main/java/com/grkj/ui_base/utils/modbus/ModBusController.kt

@@ -7,6 +7,7 @@ import com.grkj.data.repository.IHardwareRepository
 import com.grkj.data.repository.impl.HardwareRepository
 import com.grkj.ui_base.R
 import com.grkj.ui_base.business.BleBusinessManager
+import com.grkj.ui_base.config.ISCSConfig
 import com.grkj.ui_base.utils.CommonUtils
 import com.grkj.ui_base.utils.Executor
 import com.grkj.ui_base.utils.ble.BleConnectionManager
@@ -181,9 +182,6 @@ object ModBusController {
                         val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
                         logger.info("初始化锁具 RFID : $rfid")
                         updateLockRfid(dockBean.addr, idx, rfid)
-//                        RepositoryManager.hardwareRepo.getLockInfo(rfid) {
-//                            updateLockNewHardware(dockBean.addr, idx, it == null)
-//                        }
                     }
                 }
                 controlLockBuckle(false, dockBean.addr, hasLockIdxList)
@@ -215,23 +213,25 @@ object ModBusController {
                             RepositoryManager.hardwareRepo.getKeyInfo(rfid) {
                                 logger.info("getKeyInfo : $rfid - ${it?.macAddress}")
                                 updateKeyNewHardware(dockBean.addr, key.idx, it == null)
-                                if (it != null && !it.macAddress.isNullOrEmpty()) {
-                                    // 更新mac
-                                    updateKeyMac(dockBean.addr, key.idx, it.macAddress!!)
-                                    BleConnectionManager.registerConnectListener(
-                                        it.macAddress!!
-                                    ) { isDone, bleBean ->
-                                        if (isDone && bleBean?.bleDevice != null) {
-                                            Executor.delayOnMain(500) {
-                                                BleConnectionManager.getCurrentStatus(
-                                                    3, bleBean.bleDevice
-                                                )
-                                                BleConnectionManager.getBatteryPower(bleBean.bleDevice)
+                                if (ISCSConfig.isInit) {
+                                    if (it != null && !it.macAddress.isNullOrEmpty()) {
+                                        // 更新mac
+                                        updateKeyMac(dockBean.addr, key.idx, it.macAddress!!)
+                                        BleConnectionManager.registerConnectListener(
+                                            it.macAddress!!
+                                        ) { isDone, bleBean ->
+                                            if (isDone && bleBean?.bleDevice != null) {
+                                                Executor.delayOnMain(500) {
+                                                    BleConnectionManager.getCurrentStatus(
+                                                        3, bleBean.bleDevice
+                                                    )
+                                                    BleConnectionManager.getBatteryPower(bleBean.bleDevice)
+                                                }
                                             }
                                         }
+                                    } else {
+                                        PopTip.tip(CommonUtils.getStr(R.string.get_key_info_fail))
                                     }
-                                } else {
-                                    PopTip.tip(CommonUtils.getStr(R.string.get_key_info_fail))
                                 }
                             }
                         }

+ 2 - 1
ui-base/src/main/java/com/grkj/ui_base/utils/modbus/ModBusManager.kt

@@ -183,7 +183,8 @@ class ModBusManager(
         listener: (List<ByteArray>) -> Unit,
         delayMills: Long
     ): ModBusManager {
-        val keep = interrupt?.invoke()?.run { !this[0] } ?: false
+        val keep = interrupt?.invoke()?.run { this.isEmpty() || !this[0] } ?: false
+        logger.info("是否中断:${!keep}")
         if (keep) {
             sendToAll(frame) {
                 if (running) {

+ 100 - 37
ui-base/src/main/java/com/grkj/ui_base/utils/modbus/PortManager.kt

@@ -1,21 +1,31 @@
 package com.grkj.ui_base.utils.modbus
 
-import android.serialport.SerialPort
 import androidx.annotation.WorkerThread
+import com.epton.sdk.SerialPort
 import com.grkj.data.data.MMKVConstants
 import com.grkj.ui_base.utils.SPUtils
+import com.grkj.ui_base.utils.extension.toHexStrings
 import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikcore.SIKCore
 import com.sik.sikcore.extension.getMMKVData
 import com.sik.sikcore.extension.saveMMKVData
 import com.sik.sikcore.extension.toJson
 import com.sik.sikcore.thread.ThreadUtils
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeout
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import java.io.File
 import java.io.IOException
 import java.io.InputStream
 import java.io.OutputStream
+import java.util.concurrent.Callable
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import kotlin.coroutines.cancellation.CancellationException
 
 /**
  * 串口通信管理器
@@ -23,6 +33,7 @@ import java.io.OutputStream
 class PortManager private constructor(
     private val input: InputStream, private val output: OutputStream
 ) {
+
     // 是否正在监听
     @Volatile
     private var listening = false
@@ -106,7 +117,7 @@ class PortManager private constructor(
             try {
                 val file = File(if (usb) "/dev/ttyUSB${port}" else "/dev/ttyS${port}")
                 logger.info("连接 port file")
-                SerialPort(file, bps).run {
+                SerialPort(file, bps, 0).run {
                     blocked = false
                     logger.info("建立 SerialPort")
                     return PortManager(inputStream, outputStream)
@@ -141,7 +152,7 @@ class PortManager private constructor(
             try {
                 val file = File(port)
                 logger.info("连接 port file")
-                SerialPort(file, bps).run {
+                SerialPort(file, bps, 0).run {
                     blocked = false
                     logger.info("建立 SerialPort")
                     return PortManager(inputStream, outputStream)
@@ -159,38 +170,93 @@ class PortManager private constructor(
          */
         @WorkerThread
         @JvmStatic
-        fun detectSlave(baudRate: Int = 115200): String? {
+        fun detectSlave(
+            baudRate: Int = 115200,
+            perAddrTimeoutMs: Long = 100,
+            perPortTimeoutMs: Long = 5000   // 每个端口最大 500ms
+        ): String? {
             val devs = File("/dev").listFiles { _, name ->
                 name.startsWith("ttyS") || name.startsWith("ttyUSB")
             }?.map { it.absolutePath } ?: return null
-            val dockConfig = mutableListOf<DockBean>()
-            devs.forEach { path ->
-                try {
-                    dockConfig.clear()
-                    SerialPort(File(path), baudRate).let { sp ->
-                        val input = sp.inputStream
-                        val output = sp.outputStream
-                        val slaveInfo = path
-                        for (addrInt in 1..0x16) {
-                            checkSlave(addrInt, input, output, dockConfig)
-                        }
-                        checkSlave(0xA1, input, output, dockConfig)
-                        if (slaveInfo.isNotEmpty() && dockConfig.isNotEmpty()) {
-                            SPUtils.saveDockConfig(SIKCore.getApplication(), dockConfig.toJson())
-                            logger.info("扫描到设备:${slaveInfo},从机:${dockConfig}")
-                            return slaveInfo
+
+            val executor: ExecutorService = Executors.newCachedThreadPool()
+            try {
+                devs.forEach { path ->
+                    val future = executor.submit(Callable<String?> {
+                        val dockConfig = mutableListOf<DockBean>()
+                        SerialPort(File(path), baudRate, 0).let { sp ->
+                            val input = sp.inputStream
+                            val output = sp.outputStream
+
+                            // 1..0x16
+                            for (addr in 1..0x16) {
+                                tryCheckSlave(
+                                    executor,
+                                    addr.toInt(),
+                                    input,
+                                    output,
+                                    dockConfig,
+                                    perAddrTimeoutMs
+                                )
+                            }
+                            // 0xA1
+                            tryCheckSlave(
+                                executor,
+                                0xA1.toInt(),
+                                input,
+                                output,
+                                dockConfig,
+                                perAddrTimeoutMs
+                            )
+                            if (dockConfig.isNotEmpty()) {
+                                MMKVConstants.KEY_DOCK_CONFIG.saveMMKVData(dockConfig.toJson())
+                                return@Callable path
+                            }
                         }
+                        null
+                    })
+
+                    try {
+                        val result = future.get(perPortTimeoutMs, TimeUnit.MILLISECONDS)
+                        if (result != null) return result
+                    } catch (e: TimeoutException) {
+                        future.cancel(true)
+                        logger.warn("[$path] 打开/扫描超时 ${perPortTimeoutMs}ms,跳过")
+                    } catch (e: Exception) {
+                        future.cancel(true)
+                        logger.warn("[$path] 扫描出错:${e.message}")
                     }
-                } catch (e: Exception) {
-                    logger.warn("扫描 $path 失败:${e.message}")
                 }
+            } finally {
+                executor.shutdownNow()
             }
             return null
         }
 
-        /**
-         * 检查从机
-         */
+        private fun tryCheckSlave(
+            executor: ExecutorService,
+            addrInt: Int,
+            input: InputStream,
+            output: OutputStream,
+            dockConfig: MutableList<DockBean>,
+            timeoutMs: Long
+        ): Boolean {
+            return try {
+                val future = executor.submit(Callable {
+                    checkSlave(addrInt, input, output, dockConfig)
+                    dockConfig.any { it.addr.toInt() == addrInt }
+                })
+                future.get(timeoutMs, TimeUnit.MILLISECONDS)
+            } catch (_: TimeoutException) {
+                logger.warn("addr=$addrInt 检测超时 ${timeoutMs}ms")
+                false
+            } catch (e: Exception) {
+                logger.warn("addr=$addrInt 检测出错:${e.message}")
+                false
+            }
+        }
+
+        // 原始阻塞版,不变
         private fun checkSlave(
             addrInt: Int,
             input: InputStream,
@@ -198,12 +264,9 @@ class PortManager private constructor(
             dockConfig: MutableList<DockBean>
         ) {
             val addr = addrInt.toByte()
-            val cmdFrame = ModBusCMDHelper.generateReadDeviceTypeCmd()
-            val cmdBytes = cmdFrame.compile(addr)
+            val cmd = ModBusCMDHelper.generateReadDeviceTypeCmd().compile(addr)
+            output.write(cmd); output.flush()
 
-            output.write(cmdBytes)
-            output.flush()
-            // 简单阻塞读 7 字节
             val buf = ByteArray(7)
             var received = 0
             while (received < buf.size) {
@@ -211,16 +274,16 @@ class PortManager private constructor(
                 if (n <= 0) break
                 received += n
             }
+
             if (received == buf.size && buf[1] == 0x03.toByte()) {
-                val hi = buf[3].toInt() and 0xFF
-                val lo = buf[4].toInt() and 0xFF
-                val deviceType = (hi shl 8) or lo
+                val deviceType = ((buf[3].toInt() and 0xFF) shl 8) or (buf[4].toInt() and 0xFF)
                 dockConfig.add(
                     DockBean(
-                        addr,
-                        addrInt,
-                        dockConfig.count { it.type == deviceType.toByte() } + 1,
-                        deviceType.toByte(), deviceList = mutableListOf()
+                        addr = addr,
+                        row = dockConfig.count(),
+                        col = dockConfig.count { it.type == deviceType.toByte() } + 1,
+                        type = deviceType.toByte(),
+                        deviceList = mutableListOf()
                     )
                 )
             }