ModBusManager.kt 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. package com.grkj.iscs.modbus
  2. import com.grkj.iscs.extentions.crc16
  3. import com.grkj.iscs.extentions.toHexStrings
  4. import com.grkj.iscs.util.Executor
  5. import com.grkj.iscs.util.jvmSeconds
  6. import com.grkj.iscs.util.log.LogUtil
  7. import java.util.concurrent.LinkedBlockingQueue
  8. /**
  9. * modbus 最小发送间隔(150豪秒)
  10. */
  11. // TODO 超时的可能也是用的这个,看情况是否增加到500
  12. const val MODBUS_MIN_SEND_INTERVAL = 150_000_000
  13. /**
  14. * 最大从机数量
  15. */
  16. const val MODBUS_MAX_SLAVE_COUNT = 16
  17. /**
  18. * 从机状态:未变化
  19. */
  20. const val MODBUS_SLAVE_STATUS__NO_CHANGE = -1
  21. /**
  22. * 从机状态:已经满溢
  23. */
  24. const val MODBUS_SLAVE_STATUS__FULL = 0b00100000
  25. /**
  26. * ModBus 协议管理器
  27. */
  28. class ModBusManager(
  29. // 从机数量
  30. var slaveCount: Int,
  31. // 串口管理器
  32. val portManager: PortManager,
  33. // 是否输出详细信息
  34. val verbose: Boolean = false
  35. ) {
  36. @Volatile
  37. private var running = true
  38. /**
  39. * 正在发送的任务
  40. */
  41. @Volatile
  42. private var sending: FrameTask? = null
  43. /**
  44. * 等待发送队列
  45. */
  46. private val pendings = LinkedBlockingQueue<FrameTask>()
  47. /**
  48. * 线程锁
  49. */
  50. private val lock = Any()
  51. private var thread: Thread? = null
  52. init {
  53. portManager.listen { res ->
  54. if (verbose) {
  55. LogUtil.i("接收:${res.toHexStrings()}")
  56. }
  57. synchronized(lock) {
  58. sending?.run {
  59. if (match(res) && running) {
  60. done?.let { it(res) }
  61. sending = null
  62. } else {
  63. LogUtil.w("响应: ${res.toHexStrings()}未匹配, running:${running}")
  64. }
  65. }
  66. }
  67. }
  68. }
  69. /**
  70. * 发送队列的消息
  71. */
  72. private fun takePendingToSend() {
  73. if (sending == null) {
  74. sending = pendings.take()
  75. }
  76. if (!running) {
  77. return
  78. }
  79. sending?.run {
  80. waitIfNecessary()
  81. }
  82. synchronized(lock) {
  83. sending?.run {
  84. if (shouldSend()) {
  85. if (portManager.send(req)) {
  86. afterSent()
  87. if (verbose) {
  88. LogUtil.i("发送:${req.toHexStrings()}")
  89. }
  90. } else {
  91. // Tip.toast("无法与主控板通讯")
  92. LogUtil.w("无法与主控板通讯")
  93. }
  94. } else {
  95. LogUtil.i("未响应: ${req.toHexStrings()}")
  96. // 放弃处理,回调空数据
  97. done?.let { it(byteArrayOf()) }
  98. sending = null
  99. // onFrameTimeout()
  100. }
  101. }
  102. }
  103. }
  104. private var timeouts = 0
  105. private var lastTimeout = 0L
  106. private fun onFrameTimeout() {
  107. val now = jvmSeconds()
  108. if (now - lastTimeout > 10) {
  109. timeouts = 0
  110. }
  111. // 如果连续超时达到 15 次,则重建 Modbus 连接
  112. if (++timeouts > 15) {
  113. // EventBus.getDefault().post(ConfigEvent())
  114. }
  115. lastTimeout = now
  116. }
  117. /**
  118. * 循环发送给所有从机
  119. * @param frame 发送报文
  120. * @param listener 每轮发送完后的数据监听
  121. * @param delayMills 每轮发送的间隔
  122. */
  123. fun repeatSendToAll(
  124. frame: MBFrame,
  125. interrupt: (() -> List<Boolean>)? = null,
  126. listener: (res: List<ByteArray>) -> Unit,
  127. delayMills: Long
  128. ): ModBusManager {
  129. val keep = interrupt?.invoke()?.run { !this[0] } ?: false
  130. if (keep) {
  131. sendToAll(frame) {
  132. if (running) {
  133. listener(it)
  134. Executor.delayOnIO({
  135. if (running) {
  136. repeatSendToAll(frame, interrupt, listener, delayMills)
  137. }
  138. }, delayMills)
  139. }
  140. }
  141. } else {
  142. Executor.delayOnIO({
  143. if (running) {
  144. repeatSendToAll(frame, interrupt, listener, delayMills)
  145. }
  146. }, delayMills)
  147. }
  148. return this
  149. }
  150. /**
  151. * 发送给所有从机
  152. * @param frame 发送报文
  153. * @param done 所有从机都发送完成后的回调
  154. */
  155. fun sendToAll(frame: MBFrame, done: ((res: List<ByteArray>) -> Unit)? = null) {
  156. if (slaveCount == 0) {
  157. done?.let { it(listOf()) }
  158. return
  159. }
  160. sendUp(0, frame, done, ArrayList())
  161. }
  162. private fun sendUp(
  163. index: Int,
  164. frame: MBFrame,
  165. done: ((res: List<ByteArray>) -> Unit)?,
  166. resList: ArrayList<ByteArray>
  167. ) {
  168. sendTo(index, frame) { res ->
  169. resList.add(res)
  170. if (index >= slaveCount - 1) {
  171. // 已经发送完
  172. if (running) {
  173. done?.let { it(resList) }
  174. }
  175. } else {
  176. // 发送给下一个从机
  177. sendUp(index + 1, frame, done, resList)
  178. }
  179. }
  180. }
  181. /**
  182. * 发送给序号为 index 的从机
  183. * @param index 从机序号
  184. * @param frame 发送报文
  185. * @param done 完成回调
  186. */
  187. fun sendTo(
  188. index: Int,
  189. frame: MBFrame,
  190. allowRetransmission: Boolean = true,
  191. minSendIntervalNanoSeconds: Int = MODBUS_MIN_SEND_INTERVAL,
  192. done: ((res: ByteArray) -> Unit)? = null
  193. ) {
  194. if (slaveCount <= 0) {
  195. LogUtil.i("sendTo($index), slaveCount为0, 返回空数据")
  196. done?.invoke(byteArrayOf())
  197. return
  198. }
  199. if (index < 0 || index >= slaveCount) {
  200. throw IllegalArgumentException("index [${index}] out of bound [${slaveCount}]")
  201. }
  202. val task = FrameTask(frame.compile(index), done)
  203. task.allowRetransmission = allowRetransmission
  204. task.minSendInterval = minSendIntervalNanoSeconds
  205. pendings.add(task)
  206. }
  207. fun start() {
  208. thread = Thread {
  209. while (running) {
  210. try {
  211. takePendingToSend()
  212. } catch (e: InterruptedException) {
  213. }
  214. }
  215. }
  216. thread?.isDaemon = true
  217. thread?.start()
  218. }
  219. fun isRunning(): Boolean {
  220. return running
  221. }
  222. fun stop() {
  223. running = false
  224. thread?.interrupt()
  225. portManager.close()
  226. }
  227. /**
  228. * 生成锁具卡扣开关指令
  229. */
  230. fun generateLockBuckleCmd(isOpen: Boolean, lockIndex: Int): MBFrame {
  231. var str = ""
  232. val idx = lockIndex - (lockIndex / 8) * 8
  233. for (i in 7 downTo 0) {
  234. str += if (i == idx) {
  235. "1"
  236. } else {
  237. "0"
  238. }
  239. }
  240. // 第三位是 是否响应,第四位是操作哪个,操作默认全是0或者1,使用第三位响应来进行操作
  241. return MBFrame(
  242. FRAME_TYPE_WRITE,
  243. byteArrayOf(0x00,
  244. if (lockIndex in 0..7) 0x11 else 0x12,
  245. str.toInt(2).toByte(),
  246. if (isOpen) 0x00 else 0xFF.toByte()
  247. )
  248. )
  249. }
  250. fun generateKeyRfidCmd(isLeft: Boolean): MBFrame {
  251. return MBFrame(
  252. FRAME_TYPE_READ,
  253. byteArrayOf(0x00, if (isLeft) 0x20 else 0x24, 0x00, 0x04)
  254. )
  255. }
  256. /**
  257. * 生成钥匙底座灯光指令
  258. *
  259. * @param leftAction、rightAction 0:保持当前状态 1:点亮 2:熄灭 默认0
  260. */
  261. fun generateKeyLightCmd(leftAction: Int = 0, rightAction: Int = 0): MBFrame {
  262. return MBFrame(
  263. FRAME_TYPE_WRITE,
  264. byteArrayOf(0x00, 0x15, leftAction.toByte(), rightAction.toByte())
  265. )
  266. }
  267. /**
  268. * 操作钥匙底座卡扣,一次只操作一个卡扣
  269. *
  270. * @param isOpen true:开操作 false:关操作
  271. * @param isLeft true:左卡扣 false:右卡扣
  272. */
  273. fun generateKeyBuckleCmd(isOpen: Boolean, isLeft: Boolean): MBFrame {
  274. return MBFrame(
  275. FRAME_TYPE_WRITE,
  276. byteArrayOf(0x00, 0x11, if (isLeft) 0b10000000.toByte() else 0b00001000, if (isOpen) 0x00 else 0xFF.toByte())
  277. )
  278. }
  279. /**
  280. * 生成锁具 RFID 读指令
  281. *
  282. * @param lockIdx 0-9
  283. */
  284. fun generateLockRfidCmd(lockIdx: Int): MBFrame {
  285. return MBFrame(
  286. FRAME_TYPE_READ,
  287. byteArrayOf(0x00, (0x20 + lockIdx * 4).toByte(), 0x00, 0x04)
  288. )
  289. }
  290. }
  291. class FrameTask(
  292. val req: ByteArray,
  293. val done: ((res: ByteArray) -> Unit)? // 响应回调
  294. ) {
  295. /**
  296. * 是否允许重发
  297. */
  298. var allowRetransmission: Boolean = true
  299. /**
  300. * 上次发送时间
  301. */
  302. var lastSent: Long = 0
  303. /**
  304. * 已发送次数
  305. */
  306. var sentCount = 0
  307. /**
  308. * 最小发送间隔
  309. */
  310. var minSendInterval: Int = MODBUS_MIN_SEND_INTERVAL
  311. fun waitIfNecessary() {
  312. val interval = System.nanoTime() - lastSent
  313. if (interval < minSendInterval) {
  314. Thread.sleep((minSendInterval - interval) / 1000_000)
  315. }
  316. }
  317. fun shouldSend(): Boolean {
  318. return if (allowRetransmission) {
  319. sentCount < 3
  320. } else {
  321. sentCount < 1
  322. }
  323. }
  324. fun afterSent() {
  325. sentCount++
  326. lastSent = System.nanoTime()
  327. }
  328. /**
  329. * 判断 res 是否是 frame 的响应
  330. */
  331. fun match(res: ByteArray): Boolean {
  332. // 从机地址 和 功能码 必须相同
  333. if (res.size < 5 || req[0] != res[0] || req[1] != res[1]) {
  334. return false
  335. }
  336. // 报文2 的 CRC校验得正确
  337. val crc16 = res.crc16(0, res.size - 2)
  338. return crc16[0] == res[res.size - 2] && crc16[1] == res[res.size - 1]
  339. }
  340. }
  341. private const val FRAME_TYPE_READ: Byte = 0x03
  342. private const val FRAME_TYPE_WRITE: Byte = 0x06
  343. private const val FRAME_TYPE_SCANNER: Byte = 0x43
  344. const val FRAME_TYPE_WRITE_MULTI: Byte = 0x10
  345. const val FRAME_TYPE_WRITE_FILE: Byte = 0x15
  346. /**
  347. * ModBus 数据帧
  348. */
  349. class MBFrame(
  350. // 类型
  351. val type: Byte,
  352. // 数据域:D1 和 D2
  353. val data: ByteArray
  354. ) {
  355. /**
  356. * @param index 从机序号
  357. */
  358. fun compile(index: Int): ByteArray {
  359. val bytes = ByteArray(4 + data.size)
  360. // TODO 从机开始地址0x01
  361. // bytes[0] = (0x80 + index).toByte()
  362. bytes[0] = (1 + index).toByte()
  363. bytes[1] = type
  364. for (i in data.indices) {
  365. bytes[2 + i] = data[i]
  366. }
  367. val crc16 = bytes.crc16(0, bytes.size - 2)
  368. bytes[bytes.size - 2] = crc16[0]
  369. bytes[bytes.size - 1] = crc16[1]
  370. return bytes
  371. // val cmd = byteArrayOf((1 + index).toByte()) + byteArrayOf(type) + data
  372. // return cmd + cmd.crc16()
  373. }
  374. companion object {
  375. /**
  376. * 读取设备类型
  377. */
  378. val READ_DEVICE_TYPE = MBFrame(
  379. FRAME_TYPE_READ,
  380. byteArrayOf(0x00, 0x00, 0x00, 0x01)
  381. )
  382. /**
  383. * 读钥匙/锁具底座状态
  384. */
  385. val READ_STATUS = MBFrame(
  386. FRAME_TYPE_READ,
  387. byteArrayOf(0x00, 0x10, 0x00, 0x01)
  388. )
  389. /**
  390. * 读卡扣状态(钥匙、锁的0-7)
  391. */
  392. val READ_BUCKLE_STATUS = MBFrame(
  393. FRAME_TYPE_READ,
  394. byteArrayOf(0x00, 0x11, 0x00, 0x01)
  395. )
  396. /**
  397. * 读卡扣状态(锁的9、10)
  398. */
  399. val READ_LOCK_BUCKLE_EXTRA_STATUS = MBFrame(
  400. FRAME_TYPE_READ,
  401. byteArrayOf(0x00, 0x12, 0x00, 0x01)
  402. )
  403. }
  404. }