Frankensteinly 1 年間 前
コミット
4cd5ed6b88

+ 67 - 3
app/src/main/java/com/grkj/iscs/activity/ModbusActivity.kt

@@ -2,9 +2,13 @@ package com.grkj.iscs.activity
 
 import com.grkj.iscs.base.BaseMvpActivity
 import com.grkj.iscs.databinding.ActivityModbusBinding
+import com.grkj.iscs.extentions.toHexStrings
 import com.grkj.iscs.iview.IModbusView
+import com.grkj.iscs.modbus.DeviceBean
 import com.grkj.iscs.modbus.ModBusController
 import com.grkj.iscs.presenter.ModBusPresenter
+import com.grkj.iscs.util.ToastUtils
+import com.grkj.iscs.util.log.LogUtil
 
 class ModbusActivity : BaseMvpActivity<IModbusView, ModBusPresenter, ActivityModbusBinding>() {
 
@@ -15,9 +19,69 @@ class ModbusActivity : BaseMvpActivity<IModbusView, ModBusPresenter, ActivityMod
 
         presenter?.initModbus()
 
-//        mBinding?.send?.setOnClickListener {
-//            PortManager.plcPort?.send(byteArrayOf(0x03, 0x00, 0x6B, 0x00, 0x03))
-//        }
+        ModBusController.registerStatusListener(this) { res ->
+            LogUtil.i("设备状态:${(res as List<ByteArray>).map { it.toHexStrings() }}")
+            res.forEach { bytes ->
+                ModBusController.checkStatus(bytes)
+            }
+        }
+
+        mBinding?.buckleStatus?.setOnClickListener {
+            ModBusController.readBuckleStatus(null, { res ->
+                LogUtil.i("单卡扣状态 : ${res.toHexStrings()}")
+            },
+            { res ->
+                LogUtil.i("多卡扣状态 : ${res.map { it.toHexStrings()}}")
+                res.forEach { bytes ->
+
+                }
+            })
+        }
+
+        mBinding?.deviceType?.setOnClickListener {
+            println("____________________________________")
+            ModBusController.readDeviceType { res ->
+                LogUtil.i("设备类型数量 : ${res.size}")
+                LogUtil.i("设备类型 : ${res.map { it.toHexStrings()}}")
+                // TODO 暂时先清空
+                ModBusController.deviceList.clear()
+                res.forEach { bytes ->
+                    if (bytes.size < 5) return@forEach
+                    // TODO status由0x0011寄存器提供
+                    ModBusController.deviceList.add(DeviceBean(bytes[0], bytes[4], null))
+                    val type = when (bytes[4]) {
+                        0x00.toByte() -> "钥匙底座"
+                        0x01.toByte() -> "锁具底座"
+                        0x02.toByte() -> "电磁锁控制板"
+                        else -> "未知"
+                    }
+                    LogUtil.i("设备(${bytes[0].toInt()})类型:$type")
+                }
+                println("____________________________________")
+            }
+
+        }
+
+        mBinding?.openLockBuckle?.setOnClickListener {
+            if (mBinding?.etIdx?.text.toString().isEmpty()) {
+                ToastUtils.tip("请输入设备编号")
+                return@setOnClickListener
+            }
+            ModBusController.openLockBuckle(mBinding?.etIdx?.text.toString().toInt()) { res ->
+                LogUtil.i("开锁卡扣成功 : ${res.toHexStrings()}")
+            }
+        }
+
+        mBinding?.closeLockBuckle?.setOnClickListener {
+            if (mBinding?.etIdx?.text.toString().isEmpty()) {
+                ToastUtils.tip("请输入设备编号")
+                return@setOnClickListener
+            }
+            ModBusController.closeLockBuckle(mBinding?.etIdx?.text.toString().toInt()) { res ->
+                LogUtil.i("关锁卡扣成功 : ${res.toHexStrings()}")
+            }
+        }
+
         mBinding?.exit?.setOnClickListener { finish() }
     }
 

+ 55 - 0
app/src/main/java/com/grkj/iscs/modbus/DeviceBean.kt

@@ -0,0 +1,55 @@
+package com.grkj.iscs.modbus
+
+import com.grkj.iscs.util.log.LogUtil
+import kotlin.experimental.and
+
+/**
+ * RS-485 设备信息 Bean
+ *
+ * @param idx 设备序号(地址)
+ * @param type 0x00:钥匙底座 0x01:锁具底座  0x02:电磁锁控制板
+ * @param status 底座当前的状态
+ */
+class DeviceBean(var idx: Byte?, var type: Byte?, var status: ByteArray?) {
+
+    fun parseStatus(byteArray: ByteArray): Boolean {
+        if (byteArray.isEmpty()) {
+            return false
+        }
+        type?.let {
+            when (it) {
+                0x00.toByte() -> {
+                    // TODO 判断有问题
+                    val leftHasKey = byteArray[3] and 0x01.toByte() == 0x01.toByte()
+                    val isLeftCharging = byteArray[3] and 0x02.toByte() == 0x01.toByte()
+                    val rightHasKey = byteArray[4] and 0x01.toByte() == 0x01.toByte()
+                    val isRightCharging = byteArray[4] and 0x02.toByte() == 0x01.toByte()
+                    LogUtil.i("钥匙刷新状态 : $leftHasKey - $isLeftCharging - $rightHasKey - $isRightCharging")
+                    if (status == null) {
+                        return false
+                    }
+                    return status!![4] and 0x01.toByte() == 0x01.toByte() == leftHasKey
+                }
+                0x01.toByte() -> {
+                    // TODO 判断有问题
+                    val is1HasLock = byteArray[3] and 0x01.toByte() == 0x01.toByte()
+                    val is2HasLock = byteArray[3] and 0x02.toByte() == 0x01.toByte()
+                    val is3HasLock = byteArray[3] and 0x03.toByte() == 0x01.toByte()
+                    val is4HasLock = byteArray[3] and 0x04.toByte() == 0x01.toByte()
+                    val is5HasLock = byteArray[3] and 0x05.toByte() == 0x01.toByte()
+                    val is6HasLock = byteArray[3] and 0x06.toByte() == 0x01.toByte()
+                    val is7HasLock = byteArray[3] and 0x07.toByte() == 0x01.toByte()
+                    val is8HasLock = byteArray[4] and 0x01.toByte() == 0x01.toByte()
+                    val is9HasLock = byteArray[4] and 0x02.toByte() == 0x01.toByte()
+                    LogUtil.i("锁具刷新状态 : $is1HasLock - $is2HasLock - $is3HasLock - $is4HasLock - $is5HasLock - $is6HasLock - $is7HasLock - $is8HasLock - $is9HasLock")
+                    return false
+                }
+                0x02.toByte() -> {
+                    // TODO 临时占位
+                    return false
+                }
+                else -> return false
+            }
+        } ?: return false
+    }
+}

+ 100 - 42
app/src/main/java/com/grkj/iscs/modbus/ModBusController.kt

@@ -1,7 +1,6 @@
 package com.grkj.iscs.modbus
 
 import android.content.Context
-import com.grkj.iscs.extentions.toHexStrings
 import com.grkj.iscs.util.Executor
 import com.grkj.iscs.util.log.LogUtil
 import java.util.*
@@ -13,11 +12,17 @@ import java.util.concurrent.Executors
  */
 object ModBusController {
 
+    /**
+     * 所有的设备列表
+     */
+    var deviceList: MutableList<DeviceBean> = mutableListOf()
+
     private const val LISTENER_TYPE_BOTTLE = 1
     private const val LISTENER_TYPE_WEIGHT = 2
     private const val LISTENER_TYPE_STATUS = 3
     private const val LISTENER_TYPE_TEMPERA = 4
 
+
     // 主控板管理器
     private var modBusManager: ModBusManager? = null
     private var slaveCount: Int = 0
@@ -38,7 +43,7 @@ object ModBusController {
 
     class StatusListener(
         val key: Any,
-        val listener: (List<Int>) -> Unit,
+        val listener: (Any) -> Unit,
         val type: Int
     )
 
@@ -59,44 +64,47 @@ object ModBusController {
                 interruptReadTrashBinStatus
             }, { res ->
 //                // Logger.d("ModbusController", "res: ${res.map { it.toHexString() }}")
-                // 第 0 个字节是 从机地址,第 1 个字节是 功能码,第 2 个字节是 字节数
-                // 瓶类计数
-                val bottles = res.map { bytes ->
-                    if (bytes.size > 4) bytes[4].toUByte()
-                        .toInt() else MODBUS_SLAVE_STATUS__NO_CHANGE
-                }
-                // 桶的重量
-                val weights = res.map { bytes ->
-                    if (bytes.size > 6) (bytes[5].toUByte() * 256u + bytes[6].toUByte()).toInt() else MODBUS_SLAVE_STATUS__NO_CHANGE
-                }.map {
-                    if (it > 32767) 0 else it     // 大于 32767 的表示负数,作 0 处理
-                }
-                // 其它状态
-                val status = res.map { bytes ->
-                    if (bytes.size > 8) (bytes[7].toUByte() * 256u + bytes[8].toUByte()).toInt() else MODBUS_SLAVE_STATUS__NO_CHANGE
-                }
+                LogUtil.i("****************************************************************************")
+//                // 第 0 个字节是 从机地址,第 1 个字节是 功能码,第 2 个字节是 字节数
+//                // 瓶类计数
+//                val bottles = res.map { bytes ->
+//                    if (bytes.size > 4) bytes[4].toUByte()
+//                        .toInt() else MODBUS_SLAVE_STATUS__NO_CHANGE
+//                }
+//                // 桶的重量
+//                val weights = res.map { bytes ->
+//                    if (bytes.size > 6) (bytes[5].toUByte() * 256u + bytes[6].toUByte()).toInt() else MODBUS_SLAVE_STATUS__NO_CHANGE
+//                }.map {
+//                    if (it > 32767) 0 else it     // 大于 32767 的表示负数,作 0 处理
+//                }
+//                // 其它状态
+//                val status = res.map { bytes ->
+//                    if (bytes.size > 8) (bytes[7].toUByte() * 256u + bytes[8].toUByte()).toInt() else MODBUS_SLAVE_STATUS__NO_CHANGE
+//                }
                 // 箱体温度
-                val temperatures = res.map { bytes ->
-                    if (bytes.size > 10) {
-//                            // Logger.d("ModBus", "桶状态: ${bytes.toHexString()}")
-                        (bytes[9].toUByte() * 256u + bytes[10].toUByte()).toInt()
-                    } else MODBUS_SLAVE_STATUS__NO_CHANGE
-                }
+//                val temperatures = res.map { bytes ->
+//                    if (bytes.size > 10) {
+////                            // Logger.d("ModBus", "桶状态: ${bytes.toHexString()}")
+//                        (bytes[9].toUByte() * 256u + bytes[10].toUByte()).toInt()
+//                    } else MODBUS_SLAVE_STATUS__NO_CHANGE
+//                }
                 for (l in listeners) {
-                    if (l.type == LISTENER_TYPE_BOTTLE) {
-                        l.listener(bottles)
-                    }
-                    if (l.type == LISTENER_TYPE_WEIGHT) {
-                        l.listener(weights)
-                    }
+//                    if (l.type == LISTENER_TYPE_BOTTLE) {
+//                        l.listener(bottles)
+//                    }
+//                    if (l.type == LISTENER_TYPE_WEIGHT) {
+//                        l.listener(weights)
+//                    }
                     if (l.type == LISTENER_TYPE_STATUS) {
-                        l.listener(status)
-                    }
-                    if (l.type == LISTENER_TYPE_TEMPERA) {
-                        l.listener(temperatures)
+                        l.listener(res)
                     }
+//                    if (l.type == LISTENER_TYPE_TEMPERA) {
+//                        l.listener(temperatures)
+//                    }
                 }
-            }, 1000)
+                // TODO 临时改成5s
+//            }, 1000)
+            }, 5000)
             ?.also {
                 modBusManager = it
                 Executor.runOnIO {
@@ -109,17 +117,17 @@ object ModBusController {
     /**
      * 回调的重量单位:0.01kg、10g
      */
-    fun registerWeightListener(key: Any, listener: (List<Int>) -> Unit) {
-        listeners.add(StatusListener(key, listener, LISTENER_TYPE_WEIGHT))
-    }
+//    fun registerWeightListener(key: Any, listener: (List<Int>) -> Unit) {
+//        listeners.add(StatusListener(key, listener, LISTENER_TYPE_WEIGHT))
+//    }
 
-    fun registerStatusListener(key: Any, listener: (List<Int>) -> Unit) {
+    fun registerStatusListener(key: Any, listener: (Any) -> Unit) {
         listeners.add(StatusListener(key, listener, LISTENER_TYPE_STATUS))
     }
 
-    fun registerTemperaListener(key: Any, listener: (List<Int>) -> Unit) {
-        listeners.add(StatusListener(key, listener, LISTENER_TYPE_TEMPERA))
-    }
+//    fun registerTemperaListener(key: Any, listener: (List<Int>) -> Unit) {
+//        listeners.add(StatusListener(key, listener, LISTENER_TYPE_TEMPERA))
+//    }
 
     fun unregisterListener(key: Any) {
         val it = listeners.iterator()
@@ -169,4 +177,54 @@ object ModBusController {
         modBusManager?.stop()
     }
 
+
+    /*****************************************************************************************/
+
+    fun checkStatus(byteArray: ByteArray): Boolean? {
+        if (byteArray.isEmpty()) {
+            return null
+        }
+        return deviceList.find { it.idx == byteArray[0] }?.parseStatus(byteArray)
+    }
+
+
+    /**
+     * 读取设备类型
+     */
+    fun readDeviceType(done: ((res: List<ByteArray>) -> Unit)? = null) {
+        modBusManager?.sendToAll(MBFrame.READ_DEVICE_TYPE) { res ->
+            done?.invoke(res)
+        }
+    }
+
+    /**
+     * 读取卡扣状态
+     */
+    fun readBuckleStatus(index: Int?, doneSingle: ((res: ByteArray) -> Unit)? = null, doneAll: ((res: List<ByteArray>) -> Unit)? = null) {
+        index?.let {
+            modBusManager?.sendTo(it, MBFrame.READ_BUCKLE_STATUS) { res ->
+                doneSingle?.invoke(res)
+            }
+        } ?: let {
+            modBusManager?.sendToAll(MBFrame.READ_BUCKLE_STATUS) { res ->
+                doneAll?.invoke(res)
+            }
+        }
+    }
+
+    fun openLockBuckle(index: Int?, done: ((res: ByteArray) -> Unit)? = null)  {
+        index?.let {
+            modBusManager?.sendTo(it, MBFrame.WRITE_LOCK_BUCKLE_OPEN) { res ->
+                done?.invoke(res)
+            }
+        }
+    }
+
+    fun closeLockBuckle(index: Int?, done: ((res: ByteArray) -> Unit)? = null)  {
+        index?.let {
+            modBusManager?.sendTo(it, MBFrame.WRITE_LOCK_BUCKLE_CLOSE) { res ->
+                done?.invoke(res)
+            }
+        }
+    }
 }

+ 50 - 71
app/src/main/java/com/grkj/iscs/modbus/ModBusManager.kt

@@ -8,9 +8,10 @@ import com.grkj.iscs.util.log.LogUtil
 import java.util.concurrent.LinkedBlockingQueue
 
 /**
- * modbus 最小发送间隔(80豪秒)
+ * modbus 最小发送间隔(150豪秒)
  */
-const val MODBUS_MIN_SEND_INTERVAL = 80_000_000
+// TODO 超时的可能也是用的这个,看情况是否增加到500
+const val MODBUS_MIN_SEND_INTERVAL = 150_000_000
 
 /**
  * 最大从机数量
@@ -27,66 +28,6 @@ const val MODBUS_SLAVE_STATUS__NO_CHANGE = -1
  */
 const val MODBUS_SLAVE_STATUS__FULL = 0b00100000
 
-/**
- * 从机状态:满溢百分比
- */
-const val MODBUS_SLAVE_STATUS__FULL_PERCENT = 0b00100000
-
-/**
- * 从机状态:烟雾报警
- * */
-const val MODBUS_SLAVE_STATUS__SMOKE = 0b01000000
-
-/**
- * 从机状态:是否已完全关闭
- */
-const val MODBUS_SLAVE_STATUS__DOOR_CLOSED = 0b00000001
-
-/**
- * 从机状态:是否已完全开启
- */
-const val MODBUS_SLAVE_STATUS__DOOR_OPENED = 0b00000010
-
-/**
- * 从机状态:开门按钮是否被按下
- */
-const val MODBUS_SLAVE_STATUS__OPEN_BUTTON_PRESSED = 0b00010000
-
-/**
- * 从机状态:是否有扫码头事件(刷卡|扫码)
- */
-const val MODBUS_SLAVE_STATUS__SCANNER = 0b00001000
-
-/**
- * 从机状态:托盘关到位
- */
-const val MODBUS_SLAVE_STATUS__PALLET_CLOSED = 0b00001000
-
-/**
- * 从机状态:托盘开到位
- */
-const val MODBUS_SLAVE_STATUS__PALLET_OPENED = 0b00010000
-
-/**
- * 从机状态:清运门状态
- */
-const val  MODBUS_SLAVE_STATUS__CLEANING_DOOR_STATUS = 0b00100000_00000000
-
-/**
- * 从机状态:是否有开门按钮事件
- */
-const val MODBUS_SLAVE_STATUS__OPEN_DOOR_BY_PHYSICAL_BUTTON: Byte = 0b00010000
-
-/**
- * 从机状态:是否允许无授权开门
- */
-const val MODBUS_SLAVE_CTRL__ALLOW_OPEN_DOOR_UNIDENTIFIED: Byte = 0b01000000
-
-/**
- * 从机状态:是否感应到人体
- */
-const val MODBUS_SLAVE_STATUS__BODY_NEAR = 0b00000100
-
 
 /**
  * ModBus 协议管理器
@@ -134,7 +75,7 @@ class ModBusManager(
                         done?.let { it(res) }
                         sending = null
                     } else {
-                        LogUtil.i("响应: ${res.toHexStrings()}未匹配, running:${running}")
+                        LogUtil.w("响应: ${res.toHexStrings()}未匹配, running:${running}")
                     }
                 }
             }
@@ -416,14 +357,6 @@ class MBFrame(
 
     companion object {
 
-        /**
-         * 读桶的状态(从桶的重量 0x000 开始读,读 4 个寄存器 即 8 个字节)
-         */
-        val READ_STATUS = MBFrame(
-            FRAME_TYPE_READ,
-            byteArrayOf(0x00, 0x00, 0x00, 0x01)
-        )
-
         /**
          * 读扫码头/刷卡板数据
          */
@@ -521,6 +454,52 @@ class MBFrame(
             val b2 = (count % 256).toByte()
             return MBFrame(FRAME_TYPE_READ, byteArrayOf(0xF0.toByte(), 0x04, b1, b2))
         }
+
+
+
+
+
+
+
+
+
+
+
+        /***********************************************************************************/
+        /**
+         * 读取设备类型
+         */
+        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)
+        )
+
+        /**
+         * 读卡扣状态
+         */
+        val READ_BUCKLE_STATUS = MBFrame(
+            FRAME_TYPE_READ,
+            byteArrayOf(0x00, 0x11, 0x00, 0x01)
+        )
+
+        val WRITE_LOCK_BUCKLE_OPEN = MBFrame(
+            FRAME_TYPE_WRITE,
+            // TODO 9号锁有问题
+            byteArrayOf(0x00, 0x11, 0b11111111.toByte(), 0b00000000.toByte())
+        )
+
+        val WRITE_LOCK_BUCKLE_CLOSE = MBFrame(
+            FRAME_TYPE_WRITE,
+            byteArrayOf(0x00, 0x11, 0b11111111.toByte(), 0b11111111.toByte())
+        )
     }
 
 }

+ 1 - 1
app/src/main/java/com/grkj/iscs/model/FileBean.kt

@@ -1,3 +1,3 @@
 package com.grkj.iscs.model
 
-data class FileBean(var fileName:String?, var pathStr:String, var isAssets:Boolean)
+data class FileBean(var fileName: String?, var pathStr: String, var isAssets: Boolean)

+ 72 - 20
app/src/main/res/layout/activity_modbus.xml

@@ -6,27 +6,79 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    android:gravity="center"
     tools:context=".activity.ModbusActivity">
 
 
-    <Button
-        android:id="@+id/send"
-        android:layout_width="200dp"
-        android:layout_height="80dp"
-        android:minWidth="0dp"
-        android:minHeight="0dp"
-        android:text="Send"
-        android:textSize="30sp"
-        android:layout_margin="5dp"/>
-
-    <Button
-        android:id="@+id/exit"
-        android:layout_width="200dp"
-        android:layout_height="80dp"
-        android:minWidth="0dp"
-        android:minHeight="0dp"
-        android:text="Exit"
-        android:textSize="30sp"
-        android:layout_margin="5dp"/>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+        <Button
+            android:id="@+id/exit"
+            android:layout_width="100dp"
+            android:layout_height="40dp"
+            android:minWidth="0dp"
+            android:minHeight="0dp"
+            android:text="Exit"
+            android:textSize="10sp"
+            android:layout_margin="5dp"/>
+
+        <Button
+            android:id="@+id/buckleStatus"
+            android:layout_width="100dp"
+            android:layout_height="40dp"
+            android:minWidth="0dp"
+            android:minHeight="0dp"
+            android:text="Buckle Status"
+            android:textSize="8sp"
+            android:layout_margin="5dp"/>
+
+        <Button
+            android:id="@+id/deviceType"
+            android:layout_width="100dp"
+            android:layout_height="40dp"
+            android:minWidth="0dp"
+            android:minHeight="0dp"
+            android:text="Device Type"
+            android:textSize="8sp"
+            android:layout_margin="5dp"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+
+        <EditText
+            android:id="@+id/etIdx"
+            android:layout_width="100dp"
+            android:layout_height="40dp"
+            android:background="#852852"
+            android:hint="从机下标"
+            android:text="0"
+            android:textColorHint="@color/white"
+            android:textColor="@color/white"/>
+
+        <Button
+            android:id="@+id/openLockBuckle"
+            android:layout_width="100dp"
+            android:layout_height="40dp"
+            android:minWidth="0dp"
+            android:minHeight="0dp"
+            android:text="Open Lock Buckle"
+            android:textSize="8sp"
+            android:layout_margin="5dp"/>
+
+        <Button
+            android:id="@+id/closeLockBuckle"
+            android:layout_width="100dp"
+            android:layout_height="40dp"
+            android:minWidth="0dp"
+            android:minHeight="0dp"
+            android:text="Close Lock Buckle"
+            android:textSize="8sp"
+            android:layout_margin="5dp"/>
+    </LinearLayout>
 </LinearLayout>