ModBusController.kt 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. package com.grkj.iscs.modbus
  2. import android.content.Context
  3. import com.clj.fastble.BleManager
  4. import com.grkj.iscs.BusinessManager
  5. import com.grkj.iscs.R
  6. import com.grkj.iscs.extentions.removeLeadingZeros
  7. import com.grkj.iscs.extentions.toHexStrings
  8. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_ELEC_LOCK_BOARD
  9. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_KEY
  10. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_LOCK
  11. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_PORTABLE
  12. import com.grkj.iscs.util.Executor
  13. import com.grkj.iscs.util.ToastUtils
  14. import com.grkj.iscs.util.log.LogUtil
  15. import java.util.concurrent.Executors
  16. import java.util.stream.Collectors
  17. /**
  18. * ModBus 主控板控制器
  19. */
  20. object ModBusController {
  21. /**
  22. * 底座列表
  23. */
  24. var dockList: MutableList<DockBean> = mutableListOf()
  25. private const val LISTENER_TYPE_STATUS = 3
  26. // 主控板管理器
  27. private var modBusManager: ModBusManager? = null
  28. private var slaveCount: Int = 0
  29. private val threadPool = Executors.newScheduledThreadPool(4)
  30. private val listeners = ArrayList<StatusListener>()
  31. // 是否中断读取状态
  32. private var interruptReadStatus: ArrayList<Boolean> = ArrayList()
  33. var shouldStopUpgrade = false
  34. // TODO 临时改成5s
  35. const val REPEAT_FREQUENCY = 800L
  36. fun setSlaveCount(count: Int) {
  37. modBusManager?.slaveCount = count
  38. slaveCount = count
  39. }
  40. class StatusListener(
  41. val key: Any,
  42. val listener: (Any) -> Unit,
  43. val type: Int
  44. )
  45. fun interruptReadTrashBinStatus(interrupt: Boolean) {
  46. interruptReadStatus.clear()
  47. interruptReadStatus.add(interrupt)
  48. }
  49. @ExperimentalUnsignedTypes
  50. fun start(ctx: Context) {
  51. modBusManager?.stop()
  52. PortManager.openCtrlBord(ctx)
  53. ?.let { pm ->
  54. return@let ModBusManager(slaveCount, pm, true)
  55. }
  56. // 间隔 1 秒读一遍桶的状态
  57. ?.repeatSendToAll(MBFrame.READ_STATUS, {
  58. interruptReadStatus
  59. }, { res ->
  60. // // Logger.d("ModbusController", "res: ${res.map { it.toHexString() }}")
  61. LogUtil.i("****************************************************************************")
  62. // 过滤非空的数据,重置slaveCount
  63. val onlineCount = res.filter { it.isNotEmpty() }.size
  64. setSlaveCount(onlineCount)
  65. for (l in listeners) {
  66. if (l.type == LISTENER_TYPE_STATUS) {
  67. l.listener(res)
  68. }
  69. }
  70. }, REPEAT_FREQUENCY)
  71. ?.also {
  72. modBusManager = it
  73. Executor.runOnIO {
  74. // refreshAllowOpenDoorUnidentified(ctx, it)
  75. }
  76. }
  77. ?.start()
  78. }
  79. fun registerStatusListener(key: Any, listener: (Any) -> Unit) {
  80. listeners.add(StatusListener(key, listener, LISTENER_TYPE_STATUS))
  81. }
  82. fun unregisterListener(key: Any) {
  83. val it = listeners.iterator()
  84. while (it.hasNext()) {
  85. if (it.next().key == key) {
  86. it.remove()
  87. }
  88. }
  89. }
  90. fun stop() {
  91. modBusManager?.stop()
  92. }
  93. /*****************************************************************************************/
  94. /**
  95. * 初始化所有设备的状态
  96. */
  97. // TODO 通电后多久执行?App每次重启的执行是什么
  98. fun initDevicesStatus() {
  99. readDeviceType { res ->
  100. res.forEach { bytes ->
  101. if (bytes.size < 5) return@forEach
  102. // 设备具体数据由0x0011寄存器提供
  103. updateDeviceType(bytes[0], bytes[4])
  104. val type = when (bytes[4]) {
  105. DOCK_TYPE_KEY -> "钥匙底座"
  106. DOCK_TYPE_LOCK -> "锁具底座"
  107. DOCK_TYPE_ELEC_LOCK_BOARD -> "电磁锁控制板"
  108. DOCK_TYPE_PORTABLE -> "便携式底座"
  109. else -> "未知"
  110. }
  111. LogUtil.i("initDevicesStatus 设备(${bytes[0].toInt()})类型:$type")
  112. // TODO 待完善
  113. Executor.delayOnMain(REPEAT_FREQUENCY * 3) {
  114. initLock() // 打开所有无锁的卡扣、关闭所有有锁的卡扣、读取所有锁的RFID
  115. initKey() // 打开所有无钥匙的卡扣、关闭所有有钥匙的卡扣、关闭所有钥匙灯光、读取所有钥匙的RFID
  116. }
  117. // TODO 设置所有钥匙的模式
  118. // TODO 通过HTTP获取所有钥匙的Mac
  119. }
  120. }
  121. }
  122. /**
  123. * 初始化锁具——打开所有无锁的卡扣、读取RFID
  124. */
  125. private fun initLock() {
  126. LogUtil.i("initLock : $dockList")
  127. dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
  128. val hasLockIdxList = dockBean.getLockList().filter { it.isExist }.map { it.idx } as MutableList<Int>
  129. val noLockIdxList = dockBean.getLockList().filter { !it.isExist }.map { it.idx } as MutableList<Int>
  130. hasLockIdxList.forEach { idx ->
  131. readLockRfid(dockBean.addr.toInt() - 1, idx) { res ->
  132. val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  133. LogUtil.i("初始化锁具 RFID : $rfid")
  134. updateLockRfid(dockBean.addr.toInt(), idx, rfid)
  135. }
  136. }
  137. controlLockBuckle(false, dockBean.addr.toInt() - 1, hasLockIdxList)
  138. controlLockBuckle(true, dockBean.addr.toInt() - 1, noLockIdxList)
  139. }
  140. }
  141. /**
  142. * 初始化钥匙
  143. */
  144. private fun initKey() {
  145. LogUtil.i("initKey : $dockList")
  146. dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
  147. dockBean.getKeyList().forEach { key ->
  148. if (key.isExist) {
  149. LogUtil.i("initKey : ${dockBean.addr.toInt() - 1} : ${key.isLeft}")
  150. readKeyRfid(dockBean.addr.toInt() - 1, if (key.isLeft) 0 else 1) { isLeft, res ->
  151. val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  152. LogUtil.i("初始化钥匙 RFID : $rfid")
  153. updateKeyRfid(dockBean.addr.toInt(), isLeft, rfid)
  154. // TODO 蓝牙准备操作
  155. // BusinessManager.prepareBle(mac, ActivityUtils.currentActivity() as BaseActivity<*>, { _,_,_ -> }) { _,_ ->
  156. //
  157. // }
  158. }
  159. controlKeyBuckle(false, key.isLeft, dockBean.addr.toInt() - 1)
  160. } else {
  161. controlKeyBuckle(true, key.isLeft, dockBean.addr.toInt() - 1)
  162. }
  163. }
  164. }
  165. }
  166. /**
  167. * 更新状态
  168. */
  169. fun updateStatus(byteArray: ByteArray): DockBean? {
  170. if (byteArray.isEmpty()) {
  171. return null
  172. }
  173. val dockB = dockList.find { it.addr == byteArray[0] }
  174. return dockB?.parseStatus(byteArray)
  175. }
  176. /**
  177. * 读取设备类型
  178. */
  179. private fun readDeviceType(done: ((res: List<ByteArray>) -> Unit)? = null) {
  180. modBusManager?.sendToAll(MBFrame.READ_DEVICE_TYPE) { res ->
  181. done?.invoke(res)
  182. }
  183. }
  184. /**
  185. * 更新设备类型
  186. */
  187. private fun updateDeviceType(idx: Byte, type: Byte?) {
  188. val dock = dockList.find { it.addr == idx }
  189. dock?.let {
  190. it.type = type
  191. } ?: let {
  192. dockList.add(DockBean(idx, type, mutableListOf()))
  193. }
  194. }
  195. /**
  196. * 读取卡扣状态
  197. *
  198. * @param isLock true:读锁具底座 false:读钥匙底座
  199. * @param type 0:钥匙底座 1:锁具底座1-8 2:锁具底座9、10
  200. */
  201. fun readBuckleStatus(isLock: Boolean, slaveIdx: Int?, doneSingle: ((type: Int, res: ByteArray) -> Unit)? = null) {
  202. // TODO 电磁锁控制板可能不是,并且锁和钥匙的读取不一样
  203. slaveIdx?.let {
  204. modBusManager?.sendTo(it, MBFrame.READ_BUCKLE_STATUS) { res ->
  205. doneSingle?.invoke(if (isLock) 1 else 0, res)
  206. }
  207. if (isLock) {
  208. modBusManager?.sendTo(it, MBFrame.READ_LOCK_BUCKLE_EXTRA_STATUS) { res ->
  209. doneSingle?.invoke(2, res)
  210. }
  211. }
  212. }
  213. }
  214. /**
  215. * 开/关锁具卡扣
  216. */
  217. fun controlLockBuckle(isOpen: Boolean, slaveIdx: Int?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null) {
  218. slaveIdx?.let {
  219. modBusManager?.generateLockBuckleCmd(isOpen, lockIdx)?.let { cmd ->
  220. modBusManager?.sendTo(it, cmd) { res ->
  221. done?.invoke(res)
  222. }
  223. }
  224. }
  225. }
  226. fun controlLockBuckle(isOpen: Boolean, slaveIdx: Int?, lockIdxList: MutableList<Int>, done: ((res: ByteArray) -> Unit)? = null) {
  227. slaveIdx?.let {
  228. modBusManager?.generateLockBuckleCmd(isOpen, lockIdxList)?.let { cmdList ->
  229. cmdList.forEach { cmd ->
  230. modBusManager?.sendTo(it, cmd) { res ->
  231. done?.invoke(res)
  232. }
  233. }
  234. }
  235. }
  236. }
  237. /**
  238. * 读取钥匙RFID
  239. */
  240. fun readKeyRfid(slaveIdx: Int?, idx: Int, done: ((isLeft: Boolean, res: ByteArray) -> Unit)? = null) {
  241. slaveIdx?.let {
  242. modBusManager?.generateRfidCmd(idx)?.let { cmd ->
  243. modBusManager?.sendTo(it, cmd) {
  244. done?.invoke(idx == 0, it)
  245. }
  246. }
  247. }
  248. }
  249. /**
  250. * 读取锁具RFID
  251. */
  252. fun readLockRfid(slaveIdx: Int?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null) {
  253. slaveIdx?.let {
  254. modBusManager?.generateRfidCmd(lockIdx)?.let { cmd ->
  255. modBusManager?.sendTo(it, cmd) { res ->
  256. done?.invoke(res)
  257. }
  258. }
  259. }
  260. }
  261. /**
  262. * 读便携式底座卡RFID
  263. */
  264. fun readPortalCaseCardRfid(slaveIdx: Int?, done: ((res: ByteArray) -> Unit)? = null) {
  265. slaveIdx?.let {
  266. modBusManager?.generateRfidCmd(8)?.let { cmd ->
  267. modBusManager?.sendTo(it, cmd) { res ->
  268. done?.invoke(res)
  269. }
  270. }
  271. }
  272. }
  273. /**
  274. * 更新钥匙RFID
  275. */
  276. fun updateKeyRfid(slaveIdx: Int, isLeft: Boolean, rfid: String) {
  277. dockList.find { it.addr.toInt() == slaveIdx }?.getKeyList()?.find { it.isLeft == isLeft }?.rfid = rfid
  278. }
  279. /**
  280. * 更新钥匙MAC
  281. */
  282. fun updateKeyMac(slaveIdx: Int, isLeft: Boolean, mac: String) {
  283. dockList.find { it.addr.toInt() == slaveIdx }?.getKeyList()?.find { it.isLeft == isLeft }?.mac = mac
  284. }
  285. /**
  286. * 通过RFID更新对应的Mac
  287. */
  288. fun updateKeyMacByRfid(rfid: String, mac: String) {
  289. dockList.find { it.type == DOCK_TYPE_KEY }?.getKeyList()?.find { it.rfid == rfid }?.mac = mac
  290. }
  291. /**
  292. * 更新锁具RFID
  293. */
  294. fun updateLockRfid(slaveIdx: Int, lockIdx: Int, rfid: String) {
  295. dockList.find { it.addr.toInt() == slaveIdx }?.getLockList()?.find { it.idx == lockIdx }?.rfid = rfid
  296. }
  297. /**
  298. * 操作钥匙灯
  299. *
  300. * @param leftAction、rightAction 0:保持当前状态 1:点亮 2:熄灭 默认0
  301. */
  302. fun controlKeyLight(slaveIdx: Int?, leftAction: Int = 0, rightAction: Int = 0, done: ((res: ByteArray) -> Unit)? = null) {
  303. slaveIdx?.let {
  304. modBusManager?.generateKeyLightCmd(leftAction, rightAction)?.let { cmd ->
  305. modBusManager?.sendTo(it, cmd) {
  306. done?.invoke(it)
  307. }
  308. }
  309. }
  310. }
  311. /**
  312. * 开/关钥匙卡扣
  313. *
  314. * @param isOpen true:开操作 false:关操作
  315. * @param isLeft true:左卡扣 false:右卡扣
  316. */
  317. fun controlKeyBuckle(isOpen: Boolean, isLeft: Boolean, slaveIdx: Int?, done: ((res: ByteArray) -> Unit)? = null) {
  318. slaveIdx?.let {
  319. modBusManager?.generateKeyBuckleCmd(isOpen, if (isLeft) 0 else 1)?.let { cmd ->
  320. modBusManager?.sendTo(it, cmd) { res ->
  321. done?.invoke(res)
  322. }
  323. }
  324. }
  325. }
  326. /**
  327. * 根据RFID找钥匙
  328. */
  329. fun getKeyByRfid(rfid: String): DockBean.KeyBean? {
  330. return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.rfid == rfid }
  331. }
  332. /**
  333. * 根据Mac找钥匙
  334. */
  335. fun getKeyByMac(mac: String): DockBean.KeyBean? {
  336. return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.mac == mac }
  337. }
  338. /**
  339. * 根据RFID找锁具
  340. */
  341. fun getLockByRfid(rfid: String): DockBean.LockBean? {
  342. return dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getLockList() }.find { it.rfid == rfid }
  343. }
  344. /**
  345. * 根据钥匙Mac获取底座
  346. */
  347. fun getDockByKeyMac(mac: String): DockBean? {
  348. return dockList.find {
  349. (it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE)
  350. && it.getKeyList().any { it.mac == mac }
  351. }
  352. }
  353. /**
  354. * 根据类型获取底座列表
  355. */
  356. fun getDockByType(type: Byte): List<DockBean> {
  357. return dockList.filter { it.type == type }
  358. }
  359. fun getKeyByDockType(type: Byte): MutableList<DockBean.KeyBean>? {
  360. return dockList.find { it.type == type }?.let {
  361. it.getKeyList()
  362. }
  363. }
  364. fun controlAllLockBuckles(isOpen: Boolean) {
  365. dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
  366. val list = dockBean.getLockList().stream().map { it.idx }.collect(Collectors.toList())
  367. controlLockBuckle(isOpen, dockBean.addr.toInt() - 1, list) {
  368. LogUtil.i("${if (isOpen) "开启" else "关闭"}所有锁卡扣 : ${it.toHexStrings()}")
  369. }
  370. }
  371. }
  372. fun printDockInfo() {
  373. println("当前底座列表 : $dockList")
  374. dockList.forEach { dockBean ->
  375. when (dockBean.type) {
  376. DOCK_TYPE_LOCK -> {
  377. dockBean.getLockList().forEach { lockBean ->
  378. println("${dockBean.addr}锁${lockBean.idx} : ${lockBean.rfid}")
  379. }
  380. }
  381. DOCK_TYPE_KEY -> {
  382. dockBean.getKeyList().forEach { keyBean ->
  383. println("${dockBean.addr}钥${keyBean.idx} : ${keyBean.rfid}")
  384. }
  385. }
  386. DOCK_TYPE_PORTABLE -> {
  387. dockBean.getLockList().forEach { lockBean ->
  388. println("${dockBean.addr}柜锁${lockBean.idx} : ${lockBean.rfid}")
  389. }
  390. dockBean.getKeyList().forEach { keyBean ->
  391. println("${dockBean.addr}柜钥${keyBean.idx} : ${keyBean.rfid}")
  392. }
  393. }
  394. }
  395. }
  396. }
  397. fun updateDeviceType() {
  398. println("____________________________________")
  399. readDeviceType { res ->
  400. LogUtil.i("设备类型数量 : ${res.size}")
  401. LogUtil.i("设备类型 : ${res.map { it.toHexStrings()}}")
  402. res.forEach { bytes ->
  403. if (bytes.size < 5) return@forEach
  404. // 设备具体数据由0x0011寄存器提供
  405. updateDeviceType(bytes[0], bytes[4])
  406. val type = when (bytes[4]) {
  407. DOCK_TYPE_KEY -> "钥匙底座"
  408. DOCK_TYPE_LOCK -> "锁具底座"
  409. DOCK_TYPE_ELEC_LOCK_BOARD -> "电磁锁控制板"
  410. DOCK_TYPE_PORTABLE -> "便携式底座"
  411. else -> "未知"
  412. }
  413. LogUtil.i("设备(${bytes[0].toInt()})类型:$type")
  414. }
  415. println("____________________________________")
  416. }
  417. }
  418. /**
  419. * 获取一个钥匙(存在的、有RFID、有Mac、连接的)
  420. *
  421. * @return 底座地址,钥匙
  422. */
  423. fun getOneKey(): Pair<Byte, DockBean.KeyBean?>? {
  424. val keyDockList = dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
  425. val keyList = keyDockList.flatMap { it.getKeyList() }.filter { it.isExist }
  426. if (keyList.isEmpty()) {
  427. ToastUtils.tip(R.string.key_is_not_enough)
  428. return null
  429. }
  430. val key = keyList.find { it.isExist && it.rfid != null && it.mac != null &&
  431. BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(it.mac!!)?.bleDevice) }
  432. if (key == null) {
  433. LogUtil.e("getOneKey : no key match")
  434. return null
  435. }
  436. val address = keyDockList.find { it.getKeyList().any { it.rfid == key.rfid } }?.addr
  437. if (address == null) {
  438. LogUtil.e("getOneKey : no dock match")
  439. return null
  440. }
  441. return Pair(address, key)
  442. }
  443. /**
  444. * 根据数量获取锁具(基于锁柜和便携柜不存在接一起的情况)
  445. *
  446. * @param needLockCount 需要打开的锁具数量
  447. *
  448. * @return key: dock地址,value: 锁具RFID列表
  449. */
  450. fun getLocks(needLockCount: Int): MutableMap<Byte, MutableList<DockBean.LockBean>> {
  451. val map = mutableMapOf<Byte, MutableList<DockBean.LockBean>>()
  452. if (needLockCount == 0) {
  453. return map
  454. }
  455. val lockDockList = dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
  456. var provideCount = 0
  457. lockDockList.forEach loop@ { lockDock ->
  458. val lockList = lockDock.getLockList().filter { it.isExist }.toMutableList()
  459. if (lockList.size < (needLockCount - provideCount)) {
  460. provideCount += lockList.size
  461. map[lockDock.addr] = lockList
  462. } else {
  463. val rfidList = lockList.subList(0, needLockCount - provideCount)
  464. map[lockDock.addr] = rfidList
  465. return@loop
  466. }
  467. }
  468. return map
  469. }
  470. }