ModBusController.kt 18 KB

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