| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- package com.grkj.iscs.modbus
- import com.grkj.iscs.extentions.crc16
- import com.grkj.iscs.extentions.toHexStrings
- import com.grkj.iscs.util.Executor
- import com.grkj.iscs.util.jvmSeconds
- import com.grkj.iscs.util.log.LogUtil
- import java.util.concurrent.LinkedBlockingQueue
- /**
- * modbus 最小发送间隔(150豪秒)
- */
- // TODO 超时的可能也是用的这个,看情况是否增加到500
- const val MODBUS_MIN_SEND_INTERVAL = 150_000_000
- /**
- * 最大从机数量
- */
- const val MODBUS_MAX_SLAVE_COUNT = 16
- /**
- * 从机状态:未变化
- */
- const val MODBUS_SLAVE_STATUS__NO_CHANGE = -1
- /**
- * 从机状态:已经满溢
- */
- const val MODBUS_SLAVE_STATUS__FULL = 0b00100000
- /**
- * ModBus 协议管理器
- */
- class ModBusManager(
- // 从机数量
- var slaveCount: Int,
- // 串口管理器
- val portManager: PortManager,
- // 是否输出详细信息
- val verbose: Boolean = false
- ) {
- @Volatile
- private var running = true
- /**
- * 正在发送的任务
- */
- @Volatile
- private var sending: FrameTask? = null
- /**
- * 等待发送队列
- */
- private val pendings = LinkedBlockingQueue<FrameTask>()
- /**
- * 线程锁
- */
- private val lock = Any()
- private var thread: Thread? = null
- init {
- portManager.listen { res ->
- if (verbose) {
- LogUtil.i("接收:${res.toHexStrings()}")
- }
- synchronized(lock) {
- sending?.run {
- if (match(res) && running) {
- done?.let { it(res) }
- sending = null
- } else {
- LogUtil.w("响应: ${res.toHexStrings()}未匹配, running:${running}")
- }
- }
- }
- }
- }
- /**
- * 发送队列的消息
- */
- private fun takePendingToSend() {
- if (sending == null) {
- sending = pendings.take()
- }
- if (!running) {
- return
- }
- sending?.run {
- waitIfNecessary()
- }
- synchronized(lock) {
- sending?.run {
- if (shouldSend()) {
- if (portManager.send(req)) {
- afterSent()
- if (verbose) {
- LogUtil.i("发送:${req.toHexStrings()}")
- }
- } else {
- // Tip.toast("无法与主控板通讯")
- LogUtil.w("无法与主控板通讯")
- }
- } else {
- LogUtil.i("未响应: ${req.toHexStrings()}")
- // 放弃处理,回调空数据
- done?.let { it(byteArrayOf()) }
- sending = null
- // onFrameTimeout()
- }
- }
- }
- }
- private var timeouts = 0
- private var lastTimeout = 0L
- private fun onFrameTimeout() {
- val now = jvmSeconds()
- if (now - lastTimeout > 10) {
- timeouts = 0
- }
- // 如果连续超时达到 15 次,则重建 Modbus 连接
- if (++timeouts > 15) {
- // EventBus.getDefault().post(ConfigEvent())
- }
- lastTimeout = now
- }
- /**
- * 循环发送给所有从机
- * @param frame 发送报文
- * @param listener 每轮发送完后的数据监听
- * @param delayMills 每轮发送的间隔
- */
- fun repeatSendToAll(
- frame: MBFrame,
- interrupt: (() -> List<Boolean>)? = null,
- listener: (res: List<ByteArray>) -> Unit,
- delayMills: Long
- ): ModBusManager {
- val keep = interrupt?.invoke()?.run { !this[0] } ?: false
- if (keep) {
- sendToAll(frame) {
- if (running) {
- listener(it)
- Executor.delayOnIO({
- if (running) {
- repeatSendToAll(frame, interrupt, listener, delayMills)
- }
- }, delayMills)
- }
- }
- } else {
- Executor.delayOnIO({
- if (running) {
- repeatSendToAll(frame, interrupt, listener, delayMills)
- }
- }, delayMills)
- }
- return this
- }
- /**
- * 发送给所有从机
- * @param frame 发送报文
- * @param done 所有从机都发送完成后的回调
- */
- fun sendToAll(frame: MBFrame, done: ((res: List<ByteArray>) -> Unit)? = null) {
- if (slaveCount == 0) {
- done?.let { it(listOf()) }
- return
- }
- sendUp(0, frame, done, ArrayList())
- }
- private fun sendUp(
- index: Int,
- frame: MBFrame,
- done: ((res: List<ByteArray>) -> Unit)?,
- resList: ArrayList<ByteArray>
- ) {
- sendTo(index, frame) { res ->
- resList.add(res)
- if (index >= slaveCount - 1) {
- // 已经发送完
- if (running) {
- done?.let { it(resList) }
- }
- } else {
- // 发送给下一个从机
- sendUp(index + 1, frame, done, resList)
- }
- }
- }
- /**
- * 发送给序号为 index 的从机
- * @param index 从机序号
- * @param frame 发送报文
- * @param done 完成回调
- */
- fun sendTo(
- index: Int,
- frame: MBFrame,
- allowRetransmission: Boolean = true,
- minSendIntervalNanoSeconds: Int = MODBUS_MIN_SEND_INTERVAL,
- done: ((res: ByteArray) -> Unit)? = null
- ) {
- if (slaveCount <= 0) {
- LogUtil.i("sendTo($index), slaveCount为0, 返回空数据")
- done?.invoke(byteArrayOf())
- return
- }
- if (index < 0 || index >= slaveCount) {
- throw IllegalArgumentException("index [${index}] out of bound [${slaveCount}]")
- }
- val task = FrameTask(frame.compile(index), done)
- task.allowRetransmission = allowRetransmission
- task.minSendInterval = minSendIntervalNanoSeconds
- pendings.add(task)
- }
- fun start() {
- thread = Thread {
- while (running) {
- try {
- takePendingToSend()
- } catch (e: InterruptedException) {
- }
- }
- }
- thread?.isDaemon = true
- thread?.start()
- }
- fun isRunning(): Boolean {
- return running
- }
- fun stop() {
- running = false
- thread?.interrupt()
- portManager.close()
- }
- /**
- * 生成锁具卡扣开关指令
- */
- fun generateLockBuckleCmd(isOpen: Boolean, lockIndex: Int): MBFrame {
- var str = ""
- val idx = lockIndex - (lockIndex / 8) * 8
- for (i in 7 downTo 0) {
- str += if (i == idx) {
- "1"
- } else {
- "0"
- }
- }
- // 第三位是 是否响应,第四位是操作哪个,操作默认全是0或者1,使用第三位响应来进行操作
- return MBFrame(
- FRAME_TYPE_WRITE,
- byteArrayOf(0x00,
- if (lockIndex in 0..7) 0x11 else 0x12,
- str.toInt(2).toByte(),
- if (isOpen) 0x00 else 0xFF.toByte()
- )
- )
- }
- fun generateKeyRfidCmd(isLeft: Boolean): MBFrame {
- return MBFrame(
- FRAME_TYPE_READ,
- byteArrayOf(0x00, if (isLeft) 0x20 else 0x24, 0x00, 0x04)
- )
- }
- /**
- * 生成钥匙底座灯光指令
- *
- * @param leftAction、rightAction 0:保持当前状态 1:点亮 2:熄灭 默认0
- */
- fun generateKeyLightCmd(leftAction: Int = 0, rightAction: Int = 0): MBFrame {
- return MBFrame(
- FRAME_TYPE_WRITE,
- byteArrayOf(0x00, 0x15, leftAction.toByte(), rightAction.toByte())
- )
- }
- /**
- * 操作钥匙底座卡扣,一次只操作一个卡扣
- *
- * @param isOpen true:开操作 false:关操作
- * @param isLeft true:左卡扣 false:右卡扣
- */
- fun generateKeyBuckleCmd(isOpen: Boolean, isLeft: Boolean): MBFrame {
- return MBFrame(
- FRAME_TYPE_WRITE,
- byteArrayOf(0x00, 0x11, if (isLeft) 0b10000000.toByte() else 0b00001000, if (isOpen) 0x00 else 0xFF.toByte())
- )
- }
- /**
- * 生成锁具 RFID 读指令
- *
- * @param lockIdx 0-9
- */
- fun generateLockRfidCmd(lockIdx: Int): MBFrame {
- return MBFrame(
- FRAME_TYPE_READ,
- byteArrayOf(0x00, (0x20 + lockIdx * 4).toByte(), 0x00, 0x04)
- )
- }
- }
- class FrameTask(
- val req: ByteArray,
- val done: ((res: ByteArray) -> Unit)? // 响应回调
- ) {
- /**
- * 是否允许重发
- */
- var allowRetransmission: Boolean = true
- /**
- * 上次发送时间
- */
- var lastSent: Long = 0
- /**
- * 已发送次数
- */
- var sentCount = 0
- /**
- * 最小发送间隔
- */
- var minSendInterval: Int = MODBUS_MIN_SEND_INTERVAL
- fun waitIfNecessary() {
- val interval = System.nanoTime() - lastSent
- if (interval < minSendInterval) {
- Thread.sleep((minSendInterval - interval) / 1000_000)
- }
- }
- fun shouldSend(): Boolean {
- return if (allowRetransmission) {
- sentCount < 3
- } else {
- sentCount < 1
- }
- }
- fun afterSent() {
- sentCount++
- lastSent = System.nanoTime()
- }
- /**
- * 判断 res 是否是 frame 的响应
- */
- fun match(res: ByteArray): Boolean {
- // 从机地址 和 功能码 必须相同
- if (res.size < 5 || req[0] != res[0] || req[1] != res[1]) {
- return false
- }
- // 报文2 的 CRC校验得正确
- val crc16 = res.crc16(0, res.size - 2)
- return crc16[0] == res[res.size - 2] && crc16[1] == res[res.size - 1]
- }
- }
- private const val FRAME_TYPE_READ: Byte = 0x03
- private const val FRAME_TYPE_WRITE: Byte = 0x06
- private const val FRAME_TYPE_SCANNER: Byte = 0x43
- const val FRAME_TYPE_WRITE_MULTI: Byte = 0x10
- const val FRAME_TYPE_WRITE_FILE: Byte = 0x15
- /**
- * ModBus 数据帧
- */
- class MBFrame(
- // 类型
- val type: Byte,
- // 数据域:D1 和 D2
- val data: ByteArray
- ) {
- /**
- * @param index 从机序号
- */
- fun compile(index: Int): ByteArray {
- val bytes = ByteArray(4 + data.size)
- // TODO 从机开始地址0x01
- // bytes[0] = (0x80 + index).toByte()
- bytes[0] = (1 + index).toByte()
- bytes[1] = type
- for (i in data.indices) {
- bytes[2 + i] = data[i]
- }
- val crc16 = bytes.crc16(0, bytes.size - 2)
- bytes[bytes.size - 2] = crc16[0]
- bytes[bytes.size - 1] = crc16[1]
- return bytes
- // val cmd = byteArrayOf((1 + index).toByte()) + byteArrayOf(type) + data
- // return cmd + cmd.crc16()
- }
- companion object {
- /**
- * 读取设备类型
- */
- val READ_DEVICE_TYPE = MBFrame(
- FRAME_TYPE_READ,
- byteArrayOf(0x00, 0x00, 0x00, 0x01)
- )
- /**
- * 读钥匙/锁具底座状态
- */
- val READ_STATUS = MBFrame(
- FRAME_TYPE_READ,
- byteArrayOf(0x00, 0x10, 0x00, 0x01)
- )
- /**
- * 读卡扣状态(钥匙、锁的0-7)
- */
- val READ_BUCKLE_STATUS = MBFrame(
- FRAME_TYPE_READ,
- byteArrayOf(0x00, 0x11, 0x00, 0x01)
- )
- /**
- * 读卡扣状态(锁的9、10)
- */
- val READ_LOCK_BUCKLE_EXTRA_STATUS = MBFrame(
- FRAME_TYPE_READ,
- byteArrayOf(0x00, 0x12, 0x00, 0x01)
- )
- }
- }
|