ModBusController.kt 17 KB

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