ModBusController.kt 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  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 * 3) {
  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. dockBean.getKeyList().forEach { key ->
  146. if (key.isExist) {
  147. LogUtil.i("initKey : ${dockBean.addr.toInt() - 1} : ${key.isLeft}")
  148. readKeyRfid(dockBean.addr.toInt() - 1, if (key.isLeft) 0 else 1) { isLeft, res ->
  149. val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  150. LogUtil.i("初始化钥匙 RFID : $rfid")
  151. updateKeyRfid(dockBean.addr.toInt(), isLeft, rfid)
  152. }
  153. controlKeyBuckle(false, key.isLeft, dockBean.addr.toInt() - 1)
  154. } else {
  155. controlKeyBuckle(true, key.isLeft, dockBean.addr.toInt() - 1)
  156. }
  157. }
  158. }
  159. }
  160. /**
  161. * 更新状态
  162. */
  163. fun updateStatus(byteArray: ByteArray): DockBean? {
  164. if (byteArray.isEmpty()) {
  165. return null
  166. }
  167. val dockB = dockList.find { it.addr == byteArray[0] }
  168. return dockB?.parseStatus(byteArray)
  169. }
  170. /**
  171. * 读取设备类型
  172. */
  173. private fun readDeviceType(done: ((res: List<ByteArray>) -> Unit)? = null) {
  174. modBusManager?.sendToAll(MBFrame.READ_DEVICE_TYPE) { res ->
  175. done?.invoke(res)
  176. }
  177. }
  178. /**
  179. * 更新设备类型
  180. */
  181. private fun updateDeviceType(idx: Byte, type: Byte?) {
  182. val dock = dockList.find { it.addr == idx }
  183. dock?.let {
  184. it.type = type
  185. } ?: let {
  186. dockList.add(DockBean(idx, type, mutableListOf()))
  187. }
  188. }
  189. /**
  190. * 读取卡扣状态
  191. *
  192. * @param isLock true:读锁具底座 false:读钥匙底座
  193. * @param type 0:钥匙底座 1:锁具底座1-8 2:锁具底座9、10
  194. */
  195. fun readBuckleStatus(isLock: Boolean, slaveIdx: Int?, doneSingle: ((type: Int, res: ByteArray) -> Unit)? = null) {
  196. // TODO 电磁锁控制板可能不是,并且锁和钥匙的读取不一样
  197. slaveIdx?.let {
  198. modBusManager?.sendTo(it, MBFrame.READ_BUCKLE_STATUS) { res ->
  199. doneSingle?.invoke(if (isLock) 1 else 0, res)
  200. }
  201. if (isLock) {
  202. modBusManager?.sendTo(it, MBFrame.READ_LOCK_BUCKLE_EXTRA_STATUS) { res ->
  203. doneSingle?.invoke(2, res)
  204. }
  205. }
  206. }
  207. }
  208. /**
  209. * 开/关锁具卡扣
  210. */
  211. fun controlLockBuckle(isOpen: Boolean, slaveIdx: Int?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null) {
  212. slaveIdx?.let {
  213. modBusManager?.generateLockBuckleCmd(isOpen, lockIdx)?.let { cmd ->
  214. modBusManager?.sendTo(it, cmd) { res ->
  215. done?.invoke(res)
  216. }
  217. }
  218. }
  219. }
  220. fun controlLockBuckle(isOpen: Boolean, slaveIdx: Int?, lockIdxList: MutableList<Int>, done: ((res: ByteArray) -> Unit)? = null) {
  221. slaveIdx?.let {
  222. modBusManager?.generateLockBuckleCmd(isOpen, lockIdxList)?.let { cmdList ->
  223. cmdList.forEach { cmd ->
  224. modBusManager?.sendTo(it, cmd) { res ->
  225. done?.invoke(res)
  226. }
  227. }
  228. }
  229. }
  230. }
  231. /**
  232. * 读取钥匙RFID
  233. */
  234. fun readKeyRfid(slaveIdx: Int?, idx: Int, done: ((isLeft: Boolean, res: ByteArray) -> Unit)? = null) {
  235. slaveIdx?.let {
  236. modBusManager?.generateRfidCmd(idx)?.let { cmd ->
  237. modBusManager?.sendTo(it, cmd) {
  238. done?.invoke(idx == 0, it)
  239. }
  240. }
  241. }
  242. }
  243. /**
  244. * 读取锁具RFID
  245. */
  246. fun readLockRfid(slaveIdx: Int?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null) {
  247. slaveIdx?.let {
  248. modBusManager?.generateRfidCmd(lockIdx)?.let { cmd ->
  249. modBusManager?.sendTo(it, cmd) { res ->
  250. done?.invoke(res)
  251. }
  252. }
  253. }
  254. }
  255. /**
  256. * 读便携式底座卡RFID
  257. */
  258. fun readPortalCaseCardRfid(slaveIdx: Int?, done: ((res: ByteArray) -> Unit)? = null) {
  259. slaveIdx?.let {
  260. modBusManager?.generateRfidCmd(8)?.let { cmd ->
  261. modBusManager?.sendTo(it, cmd) { res ->
  262. done?.invoke(res)
  263. }
  264. }
  265. }
  266. }
  267. /**
  268. * 更新钥匙RFID
  269. */
  270. fun updateKeyRfid(slaveIdx: Int, isLeft: Boolean, rfid: String) {
  271. dockList.find { it.addr.toInt() == slaveIdx }?.getKeyList()?.find { it.isLeft == isLeft }?.rfid = rfid
  272. }
  273. /**
  274. * 更新钥匙MAC
  275. */
  276. fun updateKeyMac(slaveIdx: Int, isLeft: Boolean, mac: String) {
  277. dockList.find { it.addr.toInt() == slaveIdx }?.getKeyList()?.find { it.isLeft == isLeft }?.mac = mac
  278. }
  279. /**
  280. * 通过RFID更新对应的Mac
  281. */
  282. fun updateKeyMacByRfid(rfid: String, mac: String) {
  283. dockList.find { it.type == DOCK_TYPE_KEY }?.getKeyList()?.find { it.rfid == rfid }?.mac = mac
  284. }
  285. /**
  286. * 更新锁具RFID
  287. */
  288. fun updateLockRfid(slaveIdx: Int, lockIdx: Int, rfid: String) {
  289. dockList.find { it.addr.toInt() == slaveIdx }?.getLockList()?.find { it.idx == lockIdx }?.rfid = rfid
  290. }
  291. /**
  292. * 操作钥匙灯
  293. *
  294. * @param leftAction、rightAction 0:保持当前状态 1:点亮 2:熄灭 默认0
  295. */
  296. fun controlKeyLight(slaveIdx: Int?, leftAction: Int = 0, rightAction: Int = 0, done: ((res: ByteArray) -> Unit)? = null) {
  297. slaveIdx?.let {
  298. modBusManager?.generateKeyLightCmd(leftAction, rightAction)?.let { cmd ->
  299. modBusManager?.sendTo(it, cmd) {
  300. done?.invoke(it)
  301. }
  302. }
  303. }
  304. }
  305. /**
  306. * 开/关钥匙卡扣
  307. *
  308. * @param isOpen true:开操作 false:关操作
  309. * @param isLeft true:左卡扣 false:右卡扣
  310. */
  311. fun controlKeyBuckle(isOpen: Boolean, isLeft: Boolean, slaveIdx: Int?, done: ((res: ByteArray) -> Unit)? = null) {
  312. slaveIdx?.let {
  313. modBusManager?.generateKeyBuckleCmd(isOpen, if (isLeft) 0 else 1)?.let { cmd ->
  314. modBusManager?.sendTo(it, cmd) { res ->
  315. done?.invoke(res)
  316. }
  317. }
  318. }
  319. }
  320. /**
  321. * 根据RFID找钥匙
  322. */
  323. fun getKeyByRfid(rfid: String): DockBean.KeyBean? {
  324. return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.rfid == rfid }
  325. }
  326. /**
  327. * 根据Mac找钥匙
  328. */
  329. fun getKeyByMac(mac: String): DockBean.KeyBean? {
  330. return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.mac == mac }
  331. }
  332. /**
  333. * 根据RFID找锁具
  334. */
  335. fun getLockByRfid(rfid: String): DockBean.LockBean? {
  336. return dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getLockList() }.find { it.rfid == rfid }
  337. }
  338. /**
  339. * 根据钥匙Mac获取底座
  340. */
  341. fun getDockByKeyMac(mac: String): DockBean? {
  342. return dockList.find {
  343. (it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE)
  344. && it.getKeyList().any { it.mac == mac }
  345. }
  346. }
  347. /**
  348. * 根据类型获取底座列表
  349. */
  350. fun getDockByType(type: Byte): List<DockBean> {
  351. return dockList.filter { it.type == type }
  352. }
  353. fun getKeyByDockType(type: Byte): MutableList<DockBean.KeyBean>? {
  354. return dockList.find { it.type == type }?.let {
  355. it.getKeyList()
  356. }
  357. }
  358. fun controlAllLockBuckles(isOpen: Boolean) {
  359. dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
  360. val list = dockBean.getLockList().stream().map { it.idx }.collect(Collectors.toList())
  361. controlLockBuckle(isOpen, dockBean.addr.toInt() - 1, list) {
  362. LogUtil.i("${if (isOpen) "开启" else "关闭"}所有锁卡扣 : ${it.toHexStrings()}")
  363. }
  364. }
  365. }
  366. fun printDockInfo() {
  367. println("当前底座列表 : $dockList")
  368. dockList.forEach { dockBean ->
  369. when (dockBean.type) {
  370. DOCK_TYPE_LOCK -> {
  371. dockBean.getLockList().forEach { lockBean ->
  372. println("${dockBean.addr}锁${lockBean.idx} : ${lockBean.rfid}")
  373. }
  374. }
  375. DOCK_TYPE_KEY -> {
  376. dockBean.getKeyList().forEach { keyBean ->
  377. println("${dockBean.addr}钥${keyBean.idx} : ${keyBean.rfid}")
  378. }
  379. }
  380. DOCK_TYPE_PORTABLE -> {
  381. dockBean.getLockList().forEach { lockBean ->
  382. println("${dockBean.addr}柜锁${lockBean.idx} : ${lockBean.rfid}")
  383. }
  384. dockBean.getKeyList().forEach { keyBean ->
  385. println("${dockBean.addr}柜钥${keyBean.idx} : ${keyBean.rfid}")
  386. }
  387. }
  388. }
  389. }
  390. }
  391. fun updateDeviceType() {
  392. println("____________________________________")
  393. readDeviceType { res ->
  394. LogUtil.i("设备类型数量 : ${res.size}")
  395. LogUtil.i("设备类型 : ${res.map { it.toHexStrings()}}")
  396. res.forEach { bytes ->
  397. if (bytes.size < 5) return@forEach
  398. // 设备具体数据由0x0011寄存器提供
  399. updateDeviceType(bytes[0], bytes[4])
  400. val type = when (bytes[4]) {
  401. DOCK_TYPE_KEY -> "钥匙底座"
  402. DOCK_TYPE_LOCK -> "锁具底座"
  403. DOCK_TYPE_ELEC_LOCK_BOARD -> "电磁锁控制板"
  404. DOCK_TYPE_PORTABLE -> "便携式底座"
  405. else -> "未知"
  406. }
  407. LogUtil.i("设备(${bytes[0].toInt()})类型:$type")
  408. }
  409. println("____________________________________")
  410. }
  411. }
  412. /**
  413. * 获取一个钥匙(基于钥匙柜和便携柜不存在接一起的情况)
  414. */
  415. fun getOneKey(): Pair<Byte, DockBean.KeyBean?>? {
  416. val keyDockList = dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
  417. val keyList = keyDockList.flatMap { it.getKeyList() }.filter { it.isExist }
  418. if (keyList.isEmpty()) {
  419. ToastUtils.tip(R.string.key_is_not_enough)
  420. return null
  421. }
  422. val key = getKeyByRfid(keyList[0].rfid!!)
  423. val addr = keyDockList.find { it.getKeyList().any { it.rfid == key?.rfid } }?.addr
  424. return Pair(addr!!, key)
  425. }
  426. /**
  427. * 根据数量获取锁具(基于锁柜和便携柜不存在接一起的情况)
  428. *
  429. * @param needLockCount 需要打开的锁具数量
  430. *
  431. * @return key: dock地址,value: 锁具RFID列表
  432. */
  433. fun getLocks(needLockCount: Int): MutableMap<Byte, MutableList<DockBean.LockBean>> {
  434. val map = mutableMapOf<Byte, MutableList<DockBean.LockBean>>()
  435. if (needLockCount == 0) {
  436. return map
  437. }
  438. val lockDockList = dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
  439. var provideCount = 0
  440. lockDockList.forEach loop@ { lockDock ->
  441. val lockList = lockDock.getLockList().filter { it.isExist }.toMutableList()
  442. if (lockList.size < (needLockCount - provideCount)) {
  443. provideCount += lockList.size
  444. map[lockDock.addr] = lockList
  445. } else {
  446. val rfidList = lockList.subList(0, needLockCount - provideCount)
  447. map[lockDock.addr] = rfidList
  448. return@loop
  449. }
  450. }
  451. return map
  452. }
  453. }