package com.grkj.iscs.modbus import android.content.Context import com.clj.fastble.BleManager import com.grkj.iscs.BusinessManager import com.grkj.iscs.R import com.grkj.iscs.extentions.removeLeadingZeros import com.grkj.iscs.extentions.toHexStrings import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_KEY import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_LOCK import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_ELEC_LOCK_BOARD import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_KEY import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_LOCK import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_PORTABLE import com.grkj.iscs.util.Executor import com.grkj.iscs.util.NetApi import com.grkj.iscs.util.ToastUtils import com.grkj.iscs.util.log.LogUtil import java.util.concurrent.Executors import java.util.stream.Collectors /** * ModBus 主控板控制器 */ object ModBusController { var isInitReady = false /** * 底座列表 */ var dockList: MutableList = mutableListOf() private const val LISTENER_TYPE_STATUS = 3 // 主控板管理器 var modBusManager: ModBusManager? = null private var slaveCount: Int = 0 private val threadPool = Executors.newScheduledThreadPool(4) private val listeners = ArrayList() // 是否中断读取状态 private var interruptReadStatus: ArrayList = ArrayList() var shouldStopUpgrade = false // TODO 临时改成5s const val REPEAT_FREQUENCY = 800L fun setSlaveCount(count: Int) { modBusManager?.slaveCount = count slaveCount = count } class StatusListener( val key: Any, val listener: (Any) -> Unit, val type: Int ) fun interruptReadTrashBinStatus(interrupt: Boolean) { interruptReadStatus.clear() interruptReadStatus.add(interrupt) } @ExperimentalUnsignedTypes fun start(ctx: Context) { modBusManager?.stop() PortManager.openCtrlBord(ctx) ?.let { pm -> return@let ModBusManager(slaveCount, pm, true) } // 间隔 1 秒读一遍桶的状态 ?.repeatSendToAll(MBFrame.READ_STATUS, { interruptReadStatus }, { res -> // // Logger.d("ModbusController", "res: ${res.map { it.toHexString() }}") LogUtil.i("****************************************************************************") // 过滤非空的数据,重置slaveCount // 不再使用slaveCount,改用地址池 // val onlineCount = res.filter { it.isNotEmpty() }.size // setSlaveCount(onlineCount) for (l in listeners) { if (l.type == LISTENER_TYPE_STATUS) { l.listener(res) } } }, REPEAT_FREQUENCY) ?.also { modBusManager = it Executor.runOnIO { // refreshAllowOpenDoorUnidentified(ctx, it) } } ?.start() } fun registerStatusListener(key: Any, listener: (Any) -> Unit) { listeners.add(StatusListener(key, listener, LISTENER_TYPE_STATUS)) } fun unregisterListener(key: Any) { val it = listeners.iterator() while (it.hasNext()) { if (it.next().key == key) { it.remove() } } } fun stop() { modBusManager?.stop() } fun isRunning(): Boolean? { return modBusManager?.isRunning() } /*****************************************************************************************/ /** * 初始化所有设备的状态 */ fun initDevicesStatus() { readDeviceType { res -> res.forEach { bytes -> if (bytes.size < 5) return@forEach // 设备具体数据由0x0011寄存器提供 updateDeviceType(bytes[0], bytes[4]) val type = when (bytes[4]) { DOCK_TYPE_KEY -> "钥匙底座" DOCK_TYPE_LOCK -> "锁具底座" DOCK_TYPE_ELEC_LOCK_BOARD -> "电磁锁控制板" DOCK_TYPE_PORTABLE -> "便携式底座" else -> "未知" } LogUtil.i("initDevicesStatus 设备(${bytes[0].toInt()})类型:$type") } // TODO 待完善 Executor.repeatOnMain({ if (isInitReady) { initLock() // 打开所有无锁的卡扣、关闭所有有锁的卡扣、读取所有锁的RFID initKey() // 打开所有无钥匙的卡扣、关闭所有有钥匙的卡扣、读取所有钥匙的RFID return@repeatOnMain false } else { return@repeatOnMain true } }, REPEAT_FREQUENCY, true) } } /** * 初始化锁具——打开所有无锁的卡扣、读取RFID */ private fun initLock() { LogUtil.i("initLock : $dockList") dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean -> val hasLockIdxList = dockBean.getLockList().filter { it.isExist }.map { it.idx } as MutableList val noLockIdxList = dockBean.getLockList().filter { !it.isExist }.map { it.idx } as MutableList hasLockIdxList.forEach { idx -> readLockRfid(dockBean.addr, idx) { res -> if (res.size < 11) { LogUtil.e("Lock rfid error") return@readLockRfid } val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros() LogUtil.i("初始化锁具 RFID : $rfid") updateLockRfid(dockBean.addr, idx, rfid) } } controlLockBuckle(false, dockBean.addr, hasLockIdxList) controlLockBuckle(true, dockBean.addr, noLockIdxList) } } /** * 初始化钥匙 */ private fun initKey() { LogUtil.i("initKey : $dockList") dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean -> dockBean.getKeyList().forEach { key -> if (key.isExist) { LogUtil.i("initKey : ${dockBean.addr} : ${key.isLeft}") readKeyRfid(dockBean.addr, if (key.isLeft) 0 else 1) { isLeft, res -> if (res.size < 11) { LogUtil.e("Key rfid error") return@readKeyRfid } val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros() LogUtil.i("初始化钥匙 RFID : $rfid") // 更新rfid updateKeyRfid(dockBean.addr, isLeft, rfid) // 蓝牙准备操作 NetApi.getKeyInfo(rfid) { LogUtil.i("getKeyInfo : $rfid - ${it?.macAddress}") if (it != null && !it.macAddress.isNullOrEmpty()) { // 更新mac updateKeyMac(dockBean.addr, key.isLeft, it.macAddress) BusinessManager.registerConnectListener(it.macAddress) { isDone, bleBean -> if (isDone && bleBean?.bleDevice != null) { Executor.delayOnMain(500) { BusinessManager.getCurrentStatus(3, bleBean.bleDevice) } } } } else { ToastUtils.tip(R.string.get_key_info_fail) } } } controlKeyBuckle(false, key.isLeft, dockBean.addr) } else { controlKeyBuckle(true, key.isLeft, dockBean.addr) } } } } /** * 更新状态 */ fun updateStatus(byteArray: ByteArray): DockBean? { if (byteArray.isEmpty()) { return null } val dockB = dockList.find { it.addr == byteArray[0] } return dockB?.parseStatus(byteArray) } /** * 读取设备类型 */ private fun readDeviceType(done: ((res: List) -> Unit)? = null) { modBusManager?.sendToAll(MBFrame.READ_DEVICE_TYPE) { res -> done?.invoke(res) } } /** * 更新设备类型 */ private fun updateDeviceType(idx: Byte, type: Byte?) { val dock = dockList.find { it.addr == idx } dock?.let { it.type = type } ?: let { dockList.add(DockBean(idx, type, mutableListOf())) } } /** * 读取卡扣状态 * * @param isLock true:读锁具底座 false:读钥匙底座 * @param type 0:钥匙底座 1:锁具底座1-8 2:锁具底座9、10 */ fun readBuckleStatus(isLock: Boolean, slaveAddress: Byte?, doneSingle: ((type: Int, res: ByteArray) -> Unit)? = null) { // TODO 电磁锁控制板可能不是,并且锁和钥匙的读取不一样 slaveAddress?.let { modBusManager?.sendTo(it, MBFrame.READ_BUCKLE_STATUS) { res -> doneSingle?.invoke(if (isLock) 1 else 0, res) } if (isLock) { modBusManager?.sendTo(it, MBFrame.READ_LOCK_BUCKLE_EXTRA_STATUS) { res -> doneSingle?.invoke(2, res) } } } } /** * 开/关锁具卡扣 */ fun controlLockBuckle(isOpen: Boolean, slaveAddress: Byte?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null) { slaveAddress?.let { modBusManager?.generateLockBuckleCmd(isOpen, lockIdx)?.let { cmd -> modBusManager?.sendTo(it, cmd) { res -> done?.invoke(res) } } } } fun controlLockBuckle(isOpen: Boolean, slaveAddress: Byte?, lockIdxList: MutableList, done: ((res: ByteArray) -> Unit)? = null) { slaveAddress?.let { modBusManager?.generateLockBuckleCmd(isOpen, lockIdxList)?.let { cmdList -> cmdList.forEach { cmd -> modBusManager?.sendTo(it, cmd) { res -> done?.invoke(res) } } } } } /** * 读取钥匙RFID */ fun readKeyRfid(slaveAddress: Byte?, idx: Int, done: ((isLeft: Boolean, res: ByteArray) -> Unit)? = null) { slaveAddress?.let { modBusManager?.generateRfidCmd(idx)?.let { cmd -> modBusManager?.sendTo(it, cmd) { done?.invoke(idx == 0, it) } } } } /** * 读取锁具RFID */ fun readLockRfid(slaveAddress: Byte?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null) { slaveAddress?.let { modBusManager?.generateRfidCmd(lockIdx)?.let { cmd -> modBusManager?.sendTo(it, cmd) { res -> done?.invoke(res) } } } } /** * 读便携式底座卡RFID */ fun readPortalCaseCardRfid(slaveAddress: Byte?, done: ((res: ByteArray) -> Unit)? = null) { slaveAddress?.let { modBusManager?.generateRfidCmd(8)?.let { cmd -> modBusManager?.sendTo(it, cmd) { res -> done?.invoke(res) } } } } /** * 更新钥匙RFID */ fun updateKeyRfid(slaveAddress: Byte, isLeft: Boolean, rfid: String) { dockList.find { it.addr == slaveAddress }?.getKeyList()?.find { it.isLeft == isLeft }?.rfid = rfid } /** * 更新钥匙MAC */ fun updateKeyMac(slaveAddress: Byte, isLeft: Boolean, mac: String) { dockList.find { it.addr == slaveAddress }?.getKeyList()?.find { it.isLeft == isLeft }?.mac = mac } /** * 通过RFID更新对应的Mac */ fun updateKeyMacByRfid(rfid: String, mac: String) { dockList.find { it.type == DOCK_TYPE_KEY }?.getKeyList()?.find { it.rfid == rfid }?.mac = mac } /** * 更新锁具RFID */ fun updateLockRfid(slaveAddress: Byte, lockIdx: Int, rfid: String) { dockList.find { it.addr == slaveAddress }?.getLockList()?.find { it.idx == lockIdx }?.rfid = rfid } /** * 设备是否存在,加入deviceType防止有重复的但是不同类型的 * * @param deviceType {@link [com.grkj.iscs.model.bo.DeviceTakeUpdateBO]#[deviceType]} */ fun isDeviceExist(rfid: String, deviceType: Int) : Boolean { return when (deviceType) { DEVICE_TYPE_KEY -> { dockList.find { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }?.getKeyList()?.find { it.rfid == rfid } != null } DEVICE_TYPE_LOCK -> { dockList.find { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }?.getLockList()?.find { it.rfid == rfid } != null } else -> { false } } } /** * 操作钥匙灯 * * @param leftAction、rightAction 0:保持当前状态 1:点亮 2:熄灭 默认0 */ fun controlKeyLight(slaveAddress: Byte?, leftAction: Int = 0, rightAction: Int = 0, done: ((res: ByteArray) -> Unit)? = null) { slaveAddress?.let { modBusManager?.generateKeyLightCmd(leftAction, rightAction)?.let { cmd -> modBusManager?.sendTo(it, cmd) { done?.invoke(it) } } } } /** * 控制钥匙卡扣 */ fun controlKeyBuckle(isOpen: Boolean, mac: String, done: ((res: ByteArray) -> Unit)? = null) { val dockBean = getDockByKeyMac(mac) dockBean ?: return val key = getKeyByMac(mac) key ?: return controlKeyBuckle(isOpen, key.isLeft, dockBean.addr, done) } /** * 开/关钥匙卡扣 * * @param isOpen true:开操作 false:关操作 * @param isLeft true:左卡扣 false:右卡扣 */ fun controlKeyBuckle(isOpen: Boolean, isLeft: Boolean, slaveAddress: Byte?, done: ((res: ByteArray) -> Unit)? = null) { slaveAddress?.let { modBusManager?.generateKeyBuckleCmd(isOpen, if (isLeft) 0 else 1)?.let { cmd -> modBusManager?.sendTo(it, cmd) { res -> done?.invoke(res) } } } } /** * 根据RFID找钥匙 */ fun getKeyByRfid(rfid: String): DockBean.KeyBean? { return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.rfid == rfid } } /** * 根据Mac找钥匙 */ fun getKeyByMac(mac: String): DockBean.KeyBean? { return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.mac == mac } } fun getKeyByDock(dockAddr: Byte?, isLeft: Boolean): DockBean.KeyBean? { dockAddr ?: return null return dockList.find { it.addr == dockAddr }?.getKeyList()?.find { it.isLeft == isLeft } } /** * 根据RFID找锁具 */ fun getLockByRfid(rfid: String): DockBean.LockBean? { return dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getLockList() }.find { it.rfid == rfid } } fun isKeyExist(dockAddr: Byte?, isLeft: Boolean): Boolean { dockAddr ?: return false return dockList.find { it.addr == dockAddr }?.getKeyList() ?.find { it.isLeft == isLeft && it.isExist } != null } fun isLockExist(dockAddr: Byte?, lockIdx: Int): Boolean { dockAddr ?: return false return dockList.find { it.addr == dockAddr }?.getLockList() ?.find { it.idx == lockIdx && it.isExist } != null } /** * 获取钥匙状态 * * @return 0:不存在 1:存在 2:有RFID 3:有MAC 4:连接上 5:待机模式 6: 待机模式无异常 */ fun getKeyStatus(dockAddr: Byte?, isLeft: Boolean): Int { dockAddr ?: return 0 val key = dockList.find { it.addr == dockAddr }?.getKeyList()?.find { it.isLeft == isLeft } LogUtil.i("getKeyStatus key : $key") key ?: return 0 var status = 0 if (key.isExist) { status = 1 } else { return status } if (key.rfid != null) { status = 2 } else { return status } if (key.mac != null) { status = 3 } else { return status } if (BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(key.mac)?.bleDevice)) { status = 4 } else { return status } if (key.isReady) { status = 5 } else { return status } if (key.rfid != null && BusinessManager.mExceptionKeyList.none { it == key.rfid }) { status = 6 } else { return status } return status } /** * 更新钥匙的准备状态 */ fun updateKeyReadyStatus(mac: String, isReady: Boolean, from: Int) { LogUtil.i("updateKeyReadyStatus mac : $mac - $isReady - $from") dockList.forEach { if (it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE) { if (it.getKeyList().any { it.mac == mac }) { it.getKeyList().find { it.mac == mac }?.isReady = isReady } } } } /** * 根据钥匙Mac获取底座 */ fun getDockByKeyMac(mac: String): DockBean? { return dockList.find { (it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE) && it.getKeyList().any { it.mac == mac } } } fun getDockByLockNfc(nfc: String): DockBean? { return dockList.find { (it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE) && it.getLockList().any { it.rfid == nfc } } } /** * 根据类型获取底座列表 */ fun getDockByType(type: Byte): List { return dockList.filter { it.type == type } } fun getKeyByDockType(type: Byte): MutableList? { return dockList.find { it.type == type }?.let { it.getKeyList() } } fun controlAllLockBuckles(isOpen: Boolean) { dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean -> val list = dockBean.getLockList().stream().map { it.idx }.collect(Collectors.toList()) controlLockBuckle(isOpen, dockBean.addr, list) { LogUtil.i("${if (isOpen) "开启" else "关闭"}所有锁卡扣 : ${it.toHexStrings()}") } } } fun printDockInfo() { LogUtil.i("当前底座列表 : $dockList") dockList.forEach { dockBean -> when (dockBean.type) { DOCK_TYPE_LOCK -> { dockBean.getLockList().forEach { lockBean -> LogUtil.i("${dockBean.addr}锁${lockBean.idx} : ${lockBean.rfid}") } } DOCK_TYPE_KEY -> { dockBean.getKeyList().forEach { keyBean -> LogUtil.i("${dockBean.addr}钥${keyBean.idx} : ${keyBean.rfid}") } } DOCK_TYPE_PORTABLE -> { dockBean.getLockList().forEach { lockBean -> LogUtil.i("${dockBean.addr}柜锁${lockBean.idx} : ${lockBean.rfid}") } dockBean.getKeyList().forEach { keyBean -> LogUtil.i("${dockBean.addr}柜钥${keyBean.idx} : ${keyBean.rfid}") } } } } } fun updateDeviceType() { LogUtil.i("____________________________________") readDeviceType { res -> LogUtil.i("设备类型数量 : ${res.size}") LogUtil.i("设备类型 : ${res.map { it.toHexStrings()}}") res.forEach { bytes -> if (bytes.size < 5) return@forEach // 设备具体数据由0x0011寄存器提供 updateDeviceType(bytes[0], bytes[4]) val type = when (bytes[4]) { DOCK_TYPE_KEY -> "钥匙底座" DOCK_TYPE_LOCK -> "锁具底座" DOCK_TYPE_ELEC_LOCK_BOARD -> "电磁锁控制板" DOCK_TYPE_PORTABLE -> "便携式底座" else -> "未知" } LogUtil.i("设备(${bytes[0].toInt()})类型:$type") } LogUtil.i("____________________________________") } } /** * 随机获取一个钥匙(存在的、有RFID、有Mac、连接的、是待机模式的) * * @return 底座地址,钥匙 */ fun getOneKey(): Pair? { val keyDockList = dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE } val keyList = keyDockList.flatMap { it.getKeyList() }.filter { it.isExist } LogUtil.i("keyList : $keyList") if (keyList.isEmpty()) { ToastUtils.tip(R.string.no_available_key) return null } keyList.forEach { LogUtil.i("keyStatus : ${it.isExist} - ${it.rfid} - ${it.mac} - ${it.isReady} - " + "${BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice != null} - " + "${BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice)} - " + "${!BusinessManager.mExceptionKeyList.contains(it.rfid)}") } val key = keyList.filter { it.isExist && it.rfid != null && it.mac != null && it.isReady && BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice) && !BusinessManager.mExceptionKeyList.contains(it.rfid) } .shuffled().firstOrNull() if (key == null) { LogUtil.e("getOneKey : no key match") return null } val address = keyDockList.find { it.getKeyList().any { it.rfid == key.rfid } }?.addr if (address == null) { LogUtil.e("getOneKey : no dock match") return null } return Pair(address, key) } /** * 根据数量获取锁具(基于锁柜和便携柜不存在接一起的情况) * * @param needLockCount 需要打开的锁具数量 * * @return key: dock地址,value: 锁具RFID列表 */ fun getLocks(needLockCount: Int): MutableMap> { val map = mutableMapOf>() if (needLockCount == 0) { return map } val lockDockList = dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE } var provideCount = 0 lockDockList.forEach loop@ { lockDock -> val lockList = lockDock.getLockList().filter { it.isExist }.toMutableList() if (lockList.size < (needLockCount - provideCount)) { provideCount += lockList.size map[lockDock.addr] = lockList } else { val rfidList = lockList.subList(0, needLockCount - provideCount) map[lockDock.addr] = rfidList return@loop } } return map } // fun getLocks(needLockCount: Int): MutableMap> { // val map = mutableMapOf>() // if (needLockCount == 0) { // return map // } // // val lockList = // dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE } // .flatMap { it.getLockList() }.filter { it.isExist }.shuffled().take(needLockCount) // lockList.forEach loop@{ lock -> // lock.rfid ?: return@loop // val dock = getDockByLockNfc(lock.rfid!!) // dock ?: return@loop // if (map[dock.addr] == null) { // map[dock.addr] = mutableListOf() // } // map[dock.addr]!!.add(lock) // } // return map // } }