Browse Source

tcp callback修改

weizhengliang 2 năm trước cách đây
mục cha
commit
648fadae54
26 tập tin đã thay đổi với 2199 bổ sung1009 xóa
  1. 1 1
      app/build.gradle
  2. 145 140
      conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/activity/MainActivity.kt
  3. 61 0
      conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/bean/CallingItem.java
  4. 12 4
      conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/AsyncPlayer.java
  5. 18 1
      conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/RingPlayHelper.java
  6. 0 2
      conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/SerialPortHelper.java
  7. BIN
      conversion_box/src/main/res/raw/ring_tone.mp3
  8. BIN
      conversion_box/src/main/res/raw/ring_tone.ogg
  9. 2 0
      middleware/build.gradle
  10. 588 30
      middleware/src/main/code/com/wdkl/ncs/android/middleware/model/dos/InteractionDO.java
  11. 49 3
      middleware/src/main/code/com/wdkl/ncs/android/middleware/model/dos/RoleDO.java
  12. 45 669
      middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/InteractionVO.java
  13. 245 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TaskSchedule.java
  14. 39 7
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TcpClient.java
  15. 121 47
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TcpClientHandler.java
  16. 5 24
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/DeviceChannel.java
  17. 55 3
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/EventUtil.java
  18. 31 6
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/ImUtil.java
  19. 2 2
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/OtherUtil.java
  20. 51 21
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/VoiceUtil.java
  21. 43 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/dto/TcpCallback.java
  22. 31 44
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/dto/TcpModel.java
  23. 49 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/dto/TcpSendModel.java
  24. 106 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/RoleTypeEnum.java
  25. 17 5
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/TcpAction.java
  26. 483 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/util/DateUtil.java

+ 1 - 1
app/build.gradle

@@ -31,7 +31,7 @@ android {
     buildToolsVersion build_tools_version
     aaptOptions.cruncherEnabled = false
     aaptOptions.useNewCruncher = false
-    aaptOptions.noCompress("mp3","wav")
+    aaptOptions.noCompress("mp3","wav","ogg")
 
     defaultConfig {
         applicationId "com.wdkl.app.ncs.conversion_box"

+ 145 - 140
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/activity/MainActivity.kt

@@ -13,11 +13,13 @@ import android.text.TextUtils
 import android.util.Log
 import android.view.View
 import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
 import com.enation.javashop.android.jrouter.external.annotation.Router
 import com.enation.javashop.net.engine.model.NetState
 import com.google.gson.Gson
 import com.wdkl.app.ncs.conversion_box.BuildConfig
 import com.wdkl.app.ncs.conversion_box.R
+import com.wdkl.app.ncs.conversion_box.bean.CallingItem
 import com.wdkl.app.ncs.conversion_box.bean.SosItem
 import com.wdkl.app.ncs.conversion_box.databinding.MainActivityLayoutBinding
 import com.wdkl.app.ncs.conversion_box.fragment.*
@@ -44,8 +46,10 @@ import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
 import com.wdkl.ncs.android.middleware.tcp.TcpClient
 import com.wdkl.ncs.android.middleware.tcp.channel.OtherUtil
 import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpCallback
 import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
 import com.wdkl.ncs.android.middleware.tcp.enums.DeviceTypeEnum
+import com.wdkl.ncs.android.middleware.tcp.enums.RoleTypeEnum
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
 import com.wdkl.ncs.android.middleware.udp.ServerInfoUtil
@@ -95,7 +99,6 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
     private var clickTime : Long = 0
     private var clickSosTime : Long = 0
 
-    lateinit var callTimer: CountDownTimer
 
     //网络异常计数
     private var netErrCount : Int = 0
@@ -119,9 +122,13 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
     //对方设备id
     var fromId: Int = -1
 
+    private var tid: String = ""
+
     private var sosList = ArrayList<SosItem>()
     private var outCallList = ArrayList<String>()
     private var nursingRooms = ArrayList<String>()
+    //呼叫成功的设备集合
+    private var callingList = ArrayList<CallingItem>()
 
     private var serverIp = ""
 
@@ -146,14 +153,9 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
     }
 
     override fun init() {
-        //开始ping网络,30秒ping一次
-        //NetHelper.startNetCheck()
-
         //获取mac地址
         Constant.LOCAL_MAC = NetHelper.getInstance().macAddress
 
-        //presenter.loadTcpServerHost()
-
         //注册广播
         regReceiver()
 
@@ -179,8 +181,6 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
         //val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
         //alarmManager.setTimeZone("Asia/Shanghai")
 
-        //initCallTimer()
-
         //rs485
         /*Thread {
             startRs485()
@@ -512,7 +512,6 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
 
 
     override fun destory() {
-        //callTimer.cancel()
         unRegReceiver()
         EventBus.getDefault().unregister(this)
         Constant.CALL_STATE = Constant.CALL_STANDBY
@@ -528,7 +527,6 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
 
     //数据加载错误
     override fun onError(message: String, type: Int) {
-        //errorLog("error",message)
         showMessage(message)
     }
 
@@ -718,34 +716,6 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
         }, 5000)*/
     }
 
-    private fun initCallTimer() {
-        var overTime = SettingConfig.getSipOverTime(activity)
-        if (overTime <= 0) {
-            overTime = 30
-        }
-
-        callTimer = object: CountDownTimer(overTime*1000L, 1000) {
-            override fun onTick(millisUntilFinished: Long) {
-                //
-            }
-
-            override fun onFinish() {
-                //呼叫超时,退出呼叫界面
-                showMessage(R.string.no_response)
-                updateCallText(getString(R.string.call_standby))
-                if (curInteractionVO != null) {
-                    VoiceUtil.cancelAudioCall(curInteractionVO!!.fromDeviceId)
-                    SerialPortHelper.closeSoundChannel(curInteractionVO!!.fromEthMac.toUpperCase(Locale.ROOT))
-                    val doorAddr = getDoorLightAddr(curInteractionVO!!.fromEthMac.toUpperCase(Locale.ROOT))
-                    if (!TextUtils.isEmpty(doorAddr)) {
-                        SerialPortHelper.closeDoorLight(doorAddr)
-                    }
-                }
-                Constant.CALL_STATE = Constant.CALL_STANDBY
-            }
-        }
-    }
-
 
     override fun setPartSettings(partSetting: PartSettingDO) {
         //设置白昼起止时间
@@ -817,7 +787,7 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
             tcpConnect = true
             Thread(Runnable {
                 TcpClient.getInstance().init(Constant.TCP_SERVER_URL, Constant.TCP_PORT, Constant.TCP_HEART_BEAT)
-                TcpClient.getInstance().setTcpCallback {
+                TcpClient.getInstance().setTcpSendCallback {
                     updateSendTcpInfo(it)
                 }
             }).start()
@@ -873,15 +843,10 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                         } else if (Constant.CALL_STATE == Constant.CALL_INCOMING && addr.equals(curDeviceUart, true)) {
                             //分机接听
                             RingPlayHelper.stopRingTone()
-                            VoiceUtil.acceptAudioCall(curDeviceId, fromId, curInteractionVO!!.id)
+                            VoiceUtil.acceptAudioCall(tid, curDeviceId, fromId, curInteractionVO!!.id)
                             showCallFragment()
                         } else {
                             //分机呼叫
-                            /*if (curInteractionVO == null) {
-                                startCall(addr)
-                            } else if (!addr.equals(curInteractionVO!!.fromEthMac, true) && !addr.equals(curInteractionVO!!.toEthMac, true)) {
-                                startCall(addr)
-                            }*/
                             startCall(addr)
                         }
                     }
@@ -1038,10 +1003,34 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                         if (frameDevice.device.deviceType == DeviceTypeEnum.SIMULATE_BED_DEVICE.value()) {
                             curDeviceId = frameDevice.device.id
                             updateCallText(getString(R.string.call_in_calling) + "$uart, $curDeviceId")
-                            VoiceUtil.startAudioCall(curDeviceId)
-                            if (Constant.CALL_STATE == Constant.CALL_STANDBY) {
-                                Constant.CALL_STATE = Constant.CALL_OUTGOING
+
+                            //VoiceUtil.startAudioCall(curDeviceId)
+                            val tcpModel = VoiceUtil.voiceCall(curDeviceId, RoleTypeEnum.NURSE.name)
+                            val transaction: TcpCallback = object : TcpCallback(tcpModel.tid, uart) {
+                                override fun onSuccess(jsonObject: JSONObject) {
+                                    if (Constant.CALL_STATE == Constant.CALL_STANDBY) {
+                                        Constant.CALL_STATE = Constant.CALL_OUTGOING
+                                    }
+                                }
+
+                                override fun onFailed(jsonObject: JSONObject) {
+                                    // 这里写发送失败的方法
+                                    val callbackString = jsonObject.getString(CALLBACK)
+                                    Log.e(TAG, "call fail: $callbackString, mac: $mac")
+                                    //呼叫失败,关闭对应分机
+                                    if (!TextUtils.isEmpty(mac)) {
+                                        Constant.CALL_STATE = Constant.CALL_STANDBY
+                                        updateCallText(getString(R.string.call_standby))
+                                        //关闭分机呼叫和门灯
+                                        SerialPortHelper.closeSoundChannel(mac)
+                                        val doorAddr = getDoorLightAddr(mac)
+                                        if (!TextUtils.isEmpty(doorAddr)) {
+                                            SerialPortHelper.closeDoorLight(doorAddr)
+                                        }
+                                    }
+                                }
                             }
+                            TcpClient.getInstance().sendTcp(tcpModel, false, transaction)
                         }
                         break
                     }
@@ -1054,12 +1043,23 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
     }
 
     private fun cancelOutCall(uart: String, resetCall: Boolean) {
-        //callTimer.cancel()
         if (FrameHelper.frameDeviceList.size > 0 ) {
             for (frameDevice in FrameHelper.frameDeviceList) {
                 if (frameDevice.device != null && uart.equals(frameDevice.device.ethMac, true)) {
                     updateCallText(getString(R.string.call_standby))
-                    VoiceUtil.cancelAudioCall(frameDevice.device.id)
+
+                    var findDevice = false
+                    for (item in callingList) {
+                        if (item.deviceId == frameDevice.device.id) {
+                            findDevice = true
+                            val callTcp = VoiceUtil.voiceCancel(item.tid, frameDevice.device.id, item.interactionId)
+                            TcpClient.getInstance().sendMsg(callTcp.toJson())
+                        }
+                    }
+                    if (!findDevice) {
+                        VoiceUtil.cancelAudioCall2(frameDevice.device.id)
+                    }
+
                     if (resetCall) {
                         Constant.CALL_STATE = Constant.CALL_STANDBY
                     }
@@ -1076,18 +1076,9 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
         }
     }
 
-    private fun cancelOutCall() {
-        //callTimer.cancel()
-        if (curInteractionVO != null) {
-            VoiceUtil.cancelAudioCall(curInteractionVO!!.fromDeviceId)
-        }
-        updateCallText(getString(R.string.call_standby))
-        Constant.CALL_STATE = Constant.CALL_STANDBY
-    }
-
     private fun rejectCall() {
         updateCallText(getString(R.string.call_standby))
-        VoiceUtil.rejectAudioCall(curDeviceId, fromId, curInteractionVO!!.id)
+        VoiceUtil.rejectAudioCall(tid, curDeviceId, fromId, curInteractionVO!!.id)
         Constant.CALL_STATE = Constant.CALL_STANDBY
     }
 
@@ -1096,17 +1087,32 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
             for (frameDevice in FrameHelper.frameDeviceList) {
                 if (frameDevice.device != null && uart.equals(frameDevice.device.ethMac, true)) {
                     updateCallText("SOS-->$uart, ${frameDevice.device.id}")
-                    OtherUtil.sendSosCall(frameDevice.device.id)
+                    //OtherUtil.sendSosCall(frameDevice.device.id)
+
+                    val tcpModel = OtherUtil.SOSCall(frameDevice.device.id, 0)
+                    val transaction: TcpCallback = object : TcpCallback(tcpModel.tid, uart) {
+                        override fun onSuccess(jsonObject: JSONObject) {
+                            //开启门灯
+                            Log.d(TAG, "sos call success, mac: $mac")
+                            val doorAddr = getDoorLightAddrBySos(mac)
+                            if (!TextUtils.isEmpty(doorAddr)) {
+                                SerialPortHelper.openDoorLightRed(doorAddr)
+                            }
+                        }
+
+                        override fun onFailed(jsonObject: JSONObject) {
+                            // 这里写发送失败的方法
+                            val callbackString = jsonObject.getString(CALLBACK)
+                            Log.e(TAG, "sos call fail: $callbackString, mac: $mac")
+                            //关闭紧急按钮
+                            SerialPortHelper.closeEmergency(mac)
+                        }
+                    }
+                    TcpClient.getInstance().sendTcp(tcpModel, false, transaction)
                     break
                 }
             }
         }
-
-        //开启门灯
-        val doorAddr = getDoorLightAddrBySos(uart)
-        if (!TextUtils.isEmpty(doorAddr)) {
-            SerialPortHelper.openDoorLightRed(doorAddr)
-        }
     }
 
     private fun cancelSosCall(uart: String) {
@@ -1140,36 +1146,6 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
     }
 
 
-    //创建会话
-    /*private fun startSipCall(targetId: String): Boolean {
-        val room = UUID.randomUUID().toString() + System.currentTimeMillis()
-        val b = gEngineKit!!.startOutCall(activity, room, targetId, true)
-        if (b) {
-            val session = gEngineKit!!.currentSession
-            if (session == null) {
-                return false
-            } else {
-                session.setSessionCallback(this)
-                session.toggleSpeaker(false)
-            }
-        }
-        return b
-    }*/
-
-    //语音接通
-    /*private fun joinAudioCall() {
-        val session = gEngineKit?.getCurrentSession()
-        if (session != null) {
-            Log.e("dds", "audio call session state: " + session.state)
-            if (session.state == EnumType.CallState.Incoming) {
-                session.joinHome(session.roomId)
-                session.toggleSpeaker(false)
-            } else if (session.state == EnumType.CallState.Idle) {
-                callEnd()
-            }
-        }
-    }*/
-
     //通过床位分机查找所在房间的门灯
     private fun getDoorLightAddr(bedAddr: String) : String? {
         //首先找到分机所在床位层级,然后通过该bed frame的父级id(即房间层级)找到对应room frame,获得门灯设备信息
@@ -1298,7 +1274,18 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
             if (frameDevice.frame != null && frameDevice.frame.parentId == roomId) {
                 if (frameDevice.device != null && frameDevice.device.deviceType == DeviceTypeEnum.SIMULATE_BED_DEVICE.value()) {
                     //取消分机呼叫
-                    VoiceUtil.cancelAudioCall(frameDevice.device.id)
+                    var findDevice = false
+                    for (item in callingList) {
+                        if (item.deviceId == frameDevice.device.id) {
+                            findDevice = true
+                            val callTcp = VoiceUtil.voiceCancel(item.tid, frameDevice.device.id, item.interactionId)
+                            TcpClient.getInstance().sendMsg(callTcp.toJson())
+                        }
+                    }
+                    if (!findDevice) {
+                        VoiceUtil.cancelAudioCall2(frameDevice.device.id)
+                    }
+
                     Log.d("tcp", "cancel bed call: " + frameDevice.frame.fullName)
                     bedMac = frameDevice.device.ethMac.toUpperCase(Locale.ROOT)
                     SerialPortHelper.closeSoundChannel(bedMac)
@@ -1419,8 +1406,9 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                         if (tcpModel.action == TcpAction.VoiceAction.CALL) {
                             //收到通话请求
                             val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                            tid = tcpModel.tid
                             fromId = tcpModel.fromId
-                            curDeviceUart = interactionVO.toEthMac.toUpperCase(Locale.ROOT)
+                            curDeviceUart = interactionVO.toEthIp.toUpperCase(Locale.ROOT)
                             curInteractionVO = interactionVO
                             curDeviceId = interactionVO.toDeviceId
                             targetSip = interactionVO.fromSipId
@@ -1435,7 +1423,7 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                             Constant.CALL_STATE = Constant.CALL_INCOMING
                             if (Constant.autoAnswer) {
                                 //自动接听
-                                VoiceUtil.acceptAudioCall(tcpModel.toId, fromId, interactionVO?.id)
+                                VoiceUtil.acceptAudioCall(tid, tcpModel.toId, fromId, interactionVO?.id)
                                 showCallFragment()
                             } else {
                                 //响铃并手动接听
@@ -1444,17 +1432,17 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                             }
                         } else if (tcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
                             //我方呼出,对方接受
-                            //callTimer.cancel()
                             val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                             fromId = tcpModel.fromId
-                            curDeviceUart = interactionVO.fromEthMac.toUpperCase(Locale.ROOT)
+                            tid = tcpModel.tid
+                            curDeviceUart = interactionVO.fromEthIp.toUpperCase(Locale.ROOT)
                             curInteractionVO = interactionVO
                             curDeviceId = interactionVO.fromDeviceId
                             targetSip = interactionVO.toSipId
                             showCallFragment()
 
                             //从呼叫列表移除
-                            outCallList.remove(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                            outCallList.remove(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
 
                             //关闭门灯
                             val doorAddr = getDoorLightAddr(curDeviceUart)
@@ -1463,24 +1451,43 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                             }
                         } else if (tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) {
                             //呼叫成功
-                            //curDeviceUart = interactionVO.fromEthMac
                             val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                             if (Constant.CALL_STATE == Constant.CALL_OUTGOING) {
                                 Constant.CALL_STATE = Constant.CALL_OUT_SUCCESS
                                 curInteractionVO = interactionVO
-                                updateCallText(getString(R.string.call_success) + "-->" + interactionVO.fromEthMac.toUpperCase(Locale.ROOT) + ", " + interactionVO.fromDeviceId)
-                                //callTimer.start()
+                                updateCallText(getString(R.string.call_success) + "-->" + interactionVO.fromEthIp.toUpperCase(Locale.ROOT) + ", " + interactionVO.fromDeviceId)
                             } /*else if (Constant.CALL_STATE == Constant.CALL_INCOMING || Constant.CALL_STATE == Constant.CALL_CALLING) {
                                 //因为一个转换盒同时只能存在一个通话,如果当前已经有来电或正在通话中则取消该呼叫,但是门灯不灭掉,表示当前房间刚才存在呼叫等待处理
-                                SerialPortHelper.closeSoundChannel(interactionVO.fromEthMac)
-                                cancelOutCall(interactionVO.fromEthMac, false)
+                                SerialPortHelper.closeSoundChannel(interactionVO.fromEthIp)
+                                cancelOutCall(interactionVO.fromEthIp, false)
                             }*/
 
                             //呼叫成功,添加到呼叫列表
-                            outCallList.add(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                            outCallList.add(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
+
+                            /*if (callingList.size > 0) {
+                                for ((index, e) in callingList.withIndex()) {
+                                    if (e.uart.equals(interactionVO.fromEthIp, true)) {
+                                        var item = CallingItem(tcpModel.tid, interactionVO.fromDeviceId, interactionVO.fromEthIp, System.currentTimeMillis(), interactionVO.id)
+                                        callingList.set(index, item)
+                                    }
+                                }
+                            }*/
+
+                            if (callingList.size > 0) {
+                                val iterator = callingList.iterator()
+                                while (iterator.hasNext()) {
+                                    val it = iterator.next()
+                                    if (interactionVO.fromDeviceId == it.deviceId) {
+                                        iterator.remove()
+                                    }
+                                }
+                            }
+                            val item = CallingItem(tcpModel.tid, interactionVO.fromDeviceId, interactionVO.fromEthIp, System.currentTimeMillis(), interactionVO.id)
+                            callingList.add(item)
 
                             //开启门灯
-                            val doorAddr = getDoorLightAddr(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                            val doorAddr = getDoorLightAddr(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
                             if (!TextUtils.isEmpty(doorAddr)) {
                                 if (Constant.colorLight && !Constant.nursingSign) {
                                     SerialPortHelper.openDoorLightGreen(doorAddr)
@@ -1492,16 +1499,15 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                             //我方呼出,对方拒绝
                             val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                             //从呼叫列表移除
-                            outCallList.remove(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                            outCallList.remove(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
 
                             //if (curInteractionVO!!.id == interactionVO.id) {
-                                //callTimer.cancel()
                                 Constant.CALL_STATE = Constant.CALL_STANDBY
                                 updateCallText(getString(R.string.call_standby))
                                 showMessage(R.string.call_reject)
                                 //关闭分机呼叫和门灯
-                                SerialPortHelper.closeSoundChannel(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
-                                val doorAddr = getDoorLightAddr(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                                SerialPortHelper.closeSoundChannel(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
+                                val doorAddr = getDoorLightAddr(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
                                 if (!TextUtils.isEmpty(doorAddr)) {
                                     SerialPortHelper.closeDoorLight(doorAddr)
                                 }
@@ -1510,58 +1516,57 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                             //我方呼出,对方通话中
                             val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                             //if (curInteractionVO!!.id == interactionVO.id) {
-                                //callTimer.cancel()
                                 Constant.CALL_STATE = Constant.CALL_STANDBY
                                 updateCallText(getString(R.string.call_standby))
                             //}
 
                             //从呼叫列表移除
-                            outCallList.remove(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                            outCallList.remove(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
 
                             showMessage(R.string.call_busy)
                             //关闭分机呼叫和门灯
-                            SerialPortHelper.closeSoundChannel(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
-                            val doorAddr = getDoorLightAddr(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                            SerialPortHelper.closeSoundChannel(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
+                            val doorAddr = getDoorLightAddr(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
                             if (!TextUtils.isEmpty(doorAddr)) {
                                 SerialPortHelper.closeDoorLight(doorAddr)
                             }
                         } else if (tcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
                             //我方呼出,对方不在线,设备离线或其它错误
-                            //callTimer.cancel()
-                            val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
-                            Constant.CALL_STATE = Constant.CALL_STANDBY
-                            updateCallText(getString(R.string.call_standby))
-                            showMessage(R.string.call_failed)
-                            //关闭分机呼叫和门灯
-                            SerialPortHelper.closeSoundChannel(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
-                            val doorAddr = getDoorLightAddr(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
-                            if (!TextUtils.isEmpty(doorAddr)) {
-                                SerialPortHelper.closeDoorLight(doorAddr)
+                            if (tcpModel.data != null) {
+                                val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                                Constant.CALL_STATE = Constant.CALL_STANDBY
+                                updateCallText(getString(R.string.call_standby))
+                                showMessage(R.string.call_failed)
+                                //关闭分机呼叫和门灯
+                                SerialPortHelper.closeSoundChannel(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
+                                val doorAddr = getDoorLightAddr(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
+                                if (!TextUtils.isEmpty(doorAddr)) {
+                                    SerialPortHelper.closeDoorLight(doorAddr)
+                                }
                             }
                         } else if (tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) {
                             //对方挂断,不论我方呼出或呼入
                             val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                             //从呼叫列表移除
-                            outCallList.remove(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                            outCallList.remove(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
 
                             //if (curInteractionVO!!.id == interactionVO.id) {
-                                //callTimer.cancel()
                                 Constant.CALL_STATE = Constant.CALL_STANDBY
                                 updateCallText(getString(R.string.call_standby))
                                 showMessage(R.string.call_end)
                                 //关闭分机呼叫和门灯
                                 if (interactionVO.fromDeviceType == DeviceTypeEnum.SIMULATE_BED_DEVICE.value()) {
-                                    if (!TextUtils.isEmpty(interactionVO.fromEthMac)) {
-                                        SerialPortHelper.closeSoundChannel(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
-                                        val doorAddr = getDoorLightAddr(interactionVO.fromEthMac.toUpperCase(Locale.ROOT))
+                                    if (!TextUtils.isEmpty(interactionVO.fromEthIp)) {
+                                        SerialPortHelper.closeSoundChannel(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
+                                        val doorAddr = getDoorLightAddr(interactionVO.fromEthIp.toUpperCase(Locale.ROOT))
                                         if (!TextUtils.isEmpty(doorAddr)) {
                                             SerialPortHelper.closeDoorLight(doorAddr)
                                         }
                                     }
                                 } else {
-                                    if (!TextUtils.isEmpty(interactionVO.toEthMac)) {
-                                        SerialPortHelper.closeSoundChannel(interactionVO.toEthMac.toUpperCase(Locale.ROOT))
-                                        //val doorAddr = getDoorLightAddr(interactionVO.toEthMac)
+                                    if (!TextUtils.isEmpty(interactionVO.toEthIp)) {
+                                        SerialPortHelper.closeSoundChannel(interactionVO.toEthIp.toUpperCase(Locale.ROOT))
+                                        //val doorAddr = getDoorLightAddr(interactionVO.toEthIp)
                                         //if (!TextUtils.isEmpty(doorAddr)) {
                                         //    SerialPortHelper.closeDoorLight(doorAddr)
                                         //}
@@ -1580,7 +1585,7 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                                 updateCallText(getString(R.string.call_standby))
                                 showMessage("Call cancel")
                                 RingPlayHelper.stopRingTone()
-                                SerialPortHelper.closeSoundChannel(interactionVO.toEthMac.toUpperCase(Locale.ROOT))
+                                SerialPortHelper.closeSoundChannel(interactionVO.toEthIp.toUpperCase(Locale.ROOT))
                             //}
                         }
                         //485分机操作
@@ -1613,7 +1618,7 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                                 outCallList.remove(ethMac.toUpperCase(Locale.ROOT))
                             }
                             RingPlayHelper.stopRingTone()
-                            VoiceUtil.acceptAudioCall(curDeviceId, fromId, curInteractionVO!!.id)
+                            VoiceUtil.acceptAudioCall(tid, curDeviceId, fromId, curInteractionVO!!.id)
                             showCallFragment()
                         } else if (tcpModel.action == TcpAction.VoiceAction.RS485HANDOFF) {
                             /*EventBus.getDefault().post(MessageEvent("handoff", Constant.EVENT_END_CALL))
@@ -1652,7 +1657,7 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                         val sosInteractionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                         if (tcpModel.action == TcpAction.SOSAction.CANCEL) {
                             //紧急呼叫已处理
-                            val addr = sosInteractionVO.fromEthMac.toUpperCase(Locale.ROOT)
+                            val addr = sosInteractionVO.fromEthIp.toUpperCase(Locale.ROOT)
                             cancelSosCall(addr)
                             //删除sosList中对应item
                             var iterator = sosList.iterator()
@@ -1827,7 +1832,7 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
 
     fun handoffCall() {
         EventBus.getDefault().post(MessageEvent("handoff", Constant.EVENT_END_CALL))
-        VoiceUtil.handoffAudioCall(curDeviceId, fromId, curInteractionVO!!.id)
+        VoiceUtil.handoffAudioCall(tid, curDeviceId, fromId, curInteractionVO!!.id)
         Constant.CALL_STATE = Constant.CALL_STANDBY
     }
 

+ 61 - 0
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/bean/CallingItem.java

@@ -0,0 +1,61 @@
+package com.wdkl.app.ncs.conversion_box.bean;
+
+public class CallingItem {
+    private String tid;
+    private Integer deviceId;
+    private String uart;
+    private long time;
+    private Integer interactionId;
+
+    public CallingItem() {
+
+    }
+
+    public CallingItem(String tid, Integer deviceId, String uart, long time, Integer interactionId) {
+        this.tid = tid;
+        this.deviceId = deviceId;
+        this.uart = uart;
+        this.time = time;
+        this.interactionId = interactionId;
+    }
+
+    public String getUart() {
+        return uart;
+    }
+
+    public void setUart(String uart) {
+        this.uart = uart;
+    }
+
+    public long getTime() {
+        return time;
+    }
+
+    public void setTime(long time) {
+        this.time = time;
+    }
+
+    public String getTid() {
+        return tid;
+    }
+
+    public void setTid(String tid) {
+        this.tid = tid;
+    }
+
+    public Integer getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(Integer deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public Integer getInteractionId() {
+        return interactionId;
+    }
+
+    public void setInteractionId(Integer interactionId) {
+        this.interactionId = interactionId;
+    }
+}

+ 12 - 4
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/AsyncPlayer.java

@@ -4,12 +4,10 @@ import android.content.Context;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
-import android.net.Uri;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.util.Log;
 
-import java.io.IOException;
 import java.util.LinkedList;
 
 /**
@@ -46,9 +44,15 @@ public class AsyncPlayer {
             player.setVolume(1.0f, 1.0f);
             //player.prepare();
             player.start();
-            /*if (mPlayer != null) {
+            player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                @Override
+                public void onCompletion(MediaPlayer mp) {
+                    mState = STOP;
+                }
+            });
+            if (mPlayer != null) {
                 mPlayer.release();
-            }*/
+            }
             mPlayer = player;
             Log.w(mTag, "start sound " + cmd.resId);
         } catch (Exception e) {
@@ -140,6 +144,10 @@ public class AsyncPlayer {
         }
     }
 
+    public boolean isPlay() {
+        return mState == PLAY;
+    }
+
     private void enqueueLocked(Command cmd) {
         mCmdQueue.add(cmd);
         if (mThread == null) {

+ 18 - 1
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/RingPlayHelper.java

@@ -2,19 +2,36 @@ package com.wdkl.app.ncs.conversion_box.helper;
 
 import android.content.Context;
 import android.media.AudioManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 
 public class RingPlayHelper {
 
     private static AsyncPlayer ringPlayer;
+    private final static Handler handler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            if (ringPlayer != null) {
+                ringPlayer.stop();
+            }
+        }
+    };
 
     public static void playRingTone(Context context, int res, boolean loop) {
         if (ringPlayer == null) {
             ringPlayer = new AsyncPlayer(null);
         }
-        ringPlayer.play(context, res, loop, AudioManager.STREAM_MUSIC);
+
+        if (!ringPlayer.isPlay()) {
+            ringPlayer.play(context, res, loop, AudioManager.STREAM_MUSIC);
+            handler.removeCallbacksAndMessages(null);
+            handler.sendEmptyMessageDelayed(10, 60000);
+        }
     }
 
     public static void stopRingTone() {
+        handler.removeCallbacksAndMessages(null);
         if (ringPlayer != null) {
             ringPlayer.stop();
         }

+ 0 - 2
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/SerialPortHelper.java

@@ -78,8 +78,6 @@ public class SerialPortHelper {
 
     //关闭指定门灯
     public static void closeDoorLight(String address) {
-        //门灯关闭发送2次
-        SerialPortUtil.getInstance().send(SerialPortUtil.C_HEARD + "8" + SerialPortUtil.C_SEPARATE + address + "F" + SerialPortUtil.C_END);
         SerialPortUtil.getInstance().send(SerialPortUtil.C_HEARD + "8" + SerialPortUtil.C_SEPARATE + address + "F" + SerialPortUtil.C_END);
 
         //if (Constant.nursingSign) {

BIN
conversion_box/src/main/res/raw/ring_tone.mp3


BIN
conversion_box/src/main/res/raw/ring_tone.ogg


+ 2 - 0
middleware/build.gradle

@@ -81,4 +81,6 @@ dependencies {
 
     compile 'com.fasterxml.jackson.core:jackson-databind:2.9.5'
     compile 'io.swagger:swagger-annotations:1.5.14'
+
+    implementation 'org.mongodb:bson:3.6.3'
 }

+ 588 - 30
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/dos/InteractionDO.java

@@ -1,13 +1,17 @@
 package com.wdkl.ncs.android.middleware.model.dos;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
 import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import com.google.common.base.Strings;
 import com.wdkl.ncs.android.middleware.model.annotation.Column;
 import com.wdkl.ncs.android.middleware.model.annotation.Id;
 import com.wdkl.ncs.android.middleware.model.annotation.PrimaryKeyField;
 import com.wdkl.ncs.android.middleware.model.annotation.Table;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -43,6 +47,12 @@ public class InteractionDO implements Serializable {
     @ApiModelProperty(value = "科室Id", required = false)
     private Integer partId;
     /**
+     * 科室名称
+     */
+    @Column(name = "part_name")
+    @ApiModelProperty(value = "科室名称", required = false)
+    private String partName;
+    /**
      * 交互类型(1:语音通话,2:视频通话,3:卫生间紧急呼叫,4:语音留言,5:文本消息,6:按键事件)
      */
     @Column(name = "action_type")
@@ -54,6 +64,8 @@ public class InteractionDO implements Serializable {
     @Column(name = "action_result")
     @ApiModelProperty(value = "0失败,1成功", required = false)
     private Integer actionResult;
+
+
     /**
      * 交互发起设备Id
      */
@@ -61,23 +73,63 @@ public class InteractionDO implements Serializable {
     @ApiModelProperty(value = "交互发起设备Id", required = false)
     private Integer fromDeviceId;
     /**
-     * 交互对象设备Id
+     * null
      */
-    @Column(name = "to_device_id")
-    @ApiModelProperty(value = "交互对象设备Id", required = false)
-    private Integer toDeviceId;
+    @Column(name = "from_eth_mac")
+    @ApiModelProperty(value = "发起端设备的有线以太网卡MAC地址", required = false)
+    private String fromEthMac;
+
+    @Column(name = "from_eth_ip")
+    @ApiModelProperty(value = "发起端设备的有线以太网卡分配的IP地址", required = false)
+    private String fromEthIp;
+    /**
+     * null
+     */
+    @Column(name = "from_sip_id")
+    @ApiModelProperty(value = "发起端设备的SIP账号", required = false)
+    private String fromSipId;
+    /**
+     * null
+     */
+    @Column(name = "from_device_type")
+    @ApiModelProperty(value = "发起端设备的设备类型", required = false)
+    private Integer fromDeviceType;
+
+    @Column(name = "from_device_name")
+    @ApiModelProperty(value = "发起端设备的名称", required = false)
+    private String fromDeviceName;
+    /**
+     * null
+     */
+    @Column(name = "from_device_phone_number")
+    @ApiModelProperty(value = "发起端设备号码", required = false)
+    private String fromDevicePhoneNumber;
     /**
      * 发起设备所在空间结构
      */
     @Column(name = "from_device_frame_id")
     @ApiModelProperty(value = "发起设备所在空间结构", required = false)
     private Integer fromDeviceFrameId;
+
+    @Column(name = "from_frame_type")
+    @ApiModelProperty(value = "发起端空间结构类型:0楼、1病房、2床位", required = false)
+    private Integer fromFrameType;
     /**
-     * 交互对象所在空间结果
+     * null
      */
-    @Column(name = "to_device_frame_id")
-    @ApiModelProperty(value = "交互对象所在空间结果", required = false)
-    private Integer toDeviceFrameId;
+    @Column(name = "from_frame_name")
+    @ApiModelProperty(value = "发起端空间结构名称", required = false)
+    private String fromFrameName;
+    /**
+     * null
+     */
+    @Column(name = "from_frame_full_name")
+    @ApiModelProperty(value = "发起端空间结构全名", required = false)
+    private String fromFrameFullName;
+
+    @Column(name = "from_frame_parent_id")
+    @ApiModelProperty(value = "发起端空间父级id", required = false)
+    private Integer fromFrameParentId;
     /**
      * 交互发起设备使用者member_id
      */
@@ -85,11 +137,132 @@ public class InteractionDO implements Serializable {
     @ApiModelProperty(value = "交互发起设备使用者member_id", required = false)
     private Integer fromDeviceMemberId;
     /**
+     * 用户
+     **/
+    @Column(name = "from_customer_id")
+    @ApiModelProperty(value = "发起端用户CustomId", required = false)
+    private Integer fromCustomerId;
+    /**
+     * null
+     */
+    @Column(name = "from_member_name")
+    @ApiModelProperty(value = "发起端用户姓名", required = false)
+    private String fromMemberName;
+    /**
+     * null
+     */
+    @Column(name = "from_member_face")
+    @ApiModelProperty(value = "发起端用户头像", required = false)
+    private String fromMemberFace;
+
+    @Column(name = "from_clerk_id")
+    @ApiModelProperty(value = "发起端用户成员Id,发起端为护士主机,腕表,医生机时才有", required = false)
+    private Integer fromClerkId;
+    /**
+     * null
+     */
+    @Column(name = "from_role_name")
+    @ApiModelProperty(value = "发起端用户角色名(医生、护士、护工)", required = false)
+    private String fromRoleName;
+
+
+    /**
+     * 交互对象设备Id
+     */
+    @Column(name = "to_device_id")
+    @ApiModelProperty(value = "交互对象设备Id", required = false)
+    private Integer toDeviceId;
+    /**
+     * null
+     */
+    @Column(name = "to_eth_mac")
+    @ApiModelProperty(value = "目的端设备的有线以太网卡MAC地址", required = false)
+    private String toEthMac;
+
+    @Column(name = "to_eth_ip")
+    @ApiModelProperty(value = "目的端设备的有线以太网卡分配的IP地址", required = false)
+    private String toEthIp;
+    /**
+     * null
+     */
+    @Column(name = "to_sip_id")
+    @ApiModelProperty(value = "目的端设备的SIP账号", required = false)
+    private String toSipId;
+    /**
+     * null
+     */
+    @Column(name = "to_device_type")
+    @ApiModelProperty(value = "目的端设备的设备类型", required = false)
+    private Integer toDeviceType;
+
+    @Column(name = "to_device_name")
+    @ApiModelProperty(value = "目的端设备的名称", required = false)
+    private String toDeviceName;
+    /**
+     * null
+     */
+    @Column(name = "to_device_phone_number")
+    @ApiModelProperty(value = "目的端设备号码", required = false)
+    private String toDevicePhoneNumber;
+    /**
+     * 交互对象所在空间结果
+     */
+    @Column(name = "to_device_frame_id")
+    @ApiModelProperty(value = "交互对象所在空间结果", required = false)
+    private Integer toDeviceFrameId;
+
+    @Column(name = "to_frame_type")
+    @ApiModelProperty(value = "目的端空间结构类型:0楼、1病房、2床位", required = false)
+    private Integer toFrameType;
+    /**
+     * null
+     */
+    @Column(name = "to_frame_name")
+    @ApiModelProperty(value = "目的端空间结构名称", required = false)
+    private String toFrameName;
+    /**
+     * null
+     */
+    @Column(name = "to_frame_full_name")
+    @ApiModelProperty(value = "目的端空间结构全名", required = false)
+    private String toFrameFullName;
+
+    @Column(name = "to_frame_parent_id")
+    @ApiModelProperty(value = "目的端空间父级id", required = false)
+    private Integer toFrameParentId;
+    /**
      * 交互对象设备使用者member_id
      */
     @Column(name = "to_device_member_id")
     @ApiModelProperty(value = "交互对象设备使用者member_id", required = false)
     private Integer toDeviceMemberId;
+
+    @Column(name = "to_customer_id")
+    @ApiModelProperty(value = "目的端用户CustomId", required = false)
+    private Integer toCustomerId;
+
+    /**
+     * null
+     */
+    @Column(name = "to_member_name")
+    @ApiModelProperty(value = "目的端用户姓名", required = false)
+    private String toMemberName;
+    /**
+     * null
+     */
+    @Column(name = "to_member_face")
+    @ApiModelProperty(value = "目的端用户头像", required = false)
+    private String toMemberFace;
+
+    @Column(name = "to_clerk_id")
+    @ApiModelProperty(value = "目的端用户成员Id,目的端为护士主机,腕表,医生机时才有", required = false)
+    private Integer toClerkId;
+    /**
+     * null
+     */
+    @Column(name = "to_role_name")
+    @ApiModelProperty(value = "目的端用户角色名(医生、护士、护工)", required = false)
+    private String toRoleName;
     /**
      * 是否为呼叫系统内部交互(如果是外部电话呼叫为true,否则为false)
      */
@@ -133,6 +306,24 @@ public class InteractionDO implements Serializable {
     @ApiModelProperty(value = "交互结束时间(通话时指通话挂断时间,通话挂断后要更新此字段)。事件响应时间、语音已读时间", required = false)
     private Long actionEnd;
     /**
+     * 交互处理者的member_id
+     */
+    @Column(name = "action_end_member_id")
+    @ApiModelProperty(value = "交互处理者的member_id", required = false)
+    private Integer actionEndMemberId;
+    /**
+     * null
+     */
+    @Column(name = "action_end_member_name")
+    @ApiModelProperty(value = "交互处理者的用户姓名", required = false)
+    private String actionEndMemberName;
+    /**
+     * 交互完成时间
+     */
+    @Column(name = "action_completed")
+    @ApiModelProperty(value = "交互完成时间", required = false)
+    private Long actionCompleted;
+    /**
      * 交互数据(类型1,2,3为空)(类型4为音频文件地址)(5为文本内容)(类型6为具体按键类型)
      */
     @Column(name = "data")
@@ -152,9 +343,51 @@ public class InteractionDO implements Serializable {
     private String errorMessage;
 
     @Column(name = "action_direction_type")
-    @ApiModelProperty(value = "交互方向类型(1:分机到主机、腕表、医生机,2:主机、腕表、医生机到分机,3主机、腕表、医生机之间互通,4:分机到分机)",required = false)
+    @ApiModelProperty(value = "交互方向类型(1:分机到主机、腕表、医生机,2:主机、腕表、医生机到分机,3主机、腕表、医生机之间互通,4:分机到分机)", required = false)
     private Integer actionDirectionType;
 
+    @Column(name = "action_status")
+    @ApiModelProperty(value = "事件类型的交互状态(包括:发出,响应,取消,完成)", required = false)
+    private String actionStatus;
+    /**
+     * null
+     */
+    @Column(name = "relative_id")
+    @ApiModelProperty(value = "亲属id,外部来电时,from_member亲属的memberId", required = false)
+    private Integer relativeId;
+    /**
+     * null
+     */
+    @Column(name = "relative_name")
+    @ApiModelProperty(value = "外部来电时,from_member亲属关系", required = false)
+    private String relativeName;
+
+
+    /**
+     *交互接收者的顺序,一个JS数组,一个交互可能同时给同一角色的多个用户,用JS数组可以个分多个层级
+     */
+    @Column(name = "spread_member_path")
+    @ApiModelProperty(value = "交互接收者的顺序", required = false)
+    private String spreadMemberPath;
+
+
+    /**
+     *交互接收的memberId,用逗号隔开,可以不考虑顺序,方便查询用户交互记录,把spread_member_path字段打散后用逗号连接
+     */
+    @Column(name = "spread_member_ids")
+    @ApiModelProperty(value = "交互接收的memberId,", required = false)
+    private String spreadMemberIds;
+
+
+    /**
+     *交互已触达的memberIds,交互被多人接收后,有任意一人处理,需要通知其他已触达的用户取消处理
+     */
+    @Column(name = "reached_member_ids")
+    @ApiModelProperty(value = "交互已触达的memberIds", required = false)
+    private String reachedMemberIds;
+
+    private ArrayList<Integer[]> spreadMemberPathArray;
+
 
     @PrimaryKeyField
     public Integer getId() {
@@ -165,7 +398,6 @@ public class InteractionDO implements Serializable {
         this.id = id;
     }
 
-
     public String getUnionId() {
         return unionId;
     }
@@ -174,7 +406,6 @@ public class InteractionDO implements Serializable {
         this.unionId = unionId;
     }
 
-
     public Long getCreateDate() {
         return createDate;
     }
@@ -183,7 +414,6 @@ public class InteractionDO implements Serializable {
         this.createDate = createDate;
     }
 
-
     public Integer getPartId() {
         return partId;
     }
@@ -192,6 +422,13 @@ public class InteractionDO implements Serializable {
         this.partId = partId;
     }
 
+    public String getPartName() {
+        return partName;
+    }
+
+    public void setPartName(String partName) {
+        this.partName = partName;
+    }
 
     public String getActionType() {
         return actionType;
@@ -201,7 +438,6 @@ public class InteractionDO implements Serializable {
         this.actionType = actionType;
     }
 
-
     public Integer getActionResult() {
         return actionResult;
     }
@@ -210,7 +446,6 @@ public class InteractionDO implements Serializable {
         this.actionResult = actionResult;
     }
 
-
     public Integer getFromDeviceId() {
         return fromDeviceId;
     }
@@ -219,15 +454,53 @@ public class InteractionDO implements Serializable {
         this.fromDeviceId = fromDeviceId;
     }
 
+    public String getFromEthMac() {
+        return fromEthMac;
+    }
 
-    public Integer getToDeviceId() {
-        return toDeviceId;
+    public void setFromEthMac(String fromEthMac) {
+        this.fromEthMac = fromEthMac;
     }
 
-    public void setToDeviceId(Integer toDeviceId) {
-        this.toDeviceId = toDeviceId;
+    public String getFromEthIp() {
+        return fromEthIp;
+    }
+
+    public void setFromEthIp(String fromEthIp) {
+        this.fromEthIp = fromEthIp;
+    }
+
+    public String getFromSipId() {
+        return fromSipId;
+    }
+
+    public void setFromSipId(String fromSipId) {
+        this.fromSipId = fromSipId;
+    }
+
+    public Integer getFromDeviceType() {
+        return fromDeviceType;
     }
 
+    public void setFromDeviceType(Integer fromDeviceType) {
+        this.fromDeviceType = fromDeviceType;
+    }
+
+    public String getFromDeviceName() {
+        return fromDeviceName;
+    }
+
+    public void setFromDeviceName(String fromDeviceName) {
+        this.fromDeviceName = fromDeviceName;
+    }
+
+    public String getFromDevicePhoneNumber() {
+        return fromDevicePhoneNumber;
+    }
+
+    public void setFromDevicePhoneNumber(String fromDevicePhoneNumber) {
+        this.fromDevicePhoneNumber = fromDevicePhoneNumber;
+    }
 
     public Integer getFromDeviceFrameId() {
         return fromDeviceFrameId;
@@ -237,15 +510,37 @@ public class InteractionDO implements Serializable {
         this.fromDeviceFrameId = fromDeviceFrameId;
     }
 
+    public Integer getFromFrameType() {
+        return fromFrameType;
+    }
 
-    public Integer getToDeviceFrameId() {
-        return toDeviceFrameId;
+    public void setFromFrameType(Integer fromFrameType) {
+        this.fromFrameType = fromFrameType;
     }
 
-    public void setToDeviceFrameId(Integer toDeviceFrameId) {
-        this.toDeviceFrameId = toDeviceFrameId;
+    public String getFromFrameName() {
+        return fromFrameName;
+    }
+
+    public void setFromFrameName(String fromFrameName) {
+        this.fromFrameName = fromFrameName;
     }
 
+    public String getFromFrameFullName() {
+        return fromFrameFullName;
+    }
+
+    public void setFromFrameFullName(String fromFrameFullName) {
+        this.fromFrameFullName = fromFrameFullName;
+    }
+
+    public Integer getFromFrameParentId() {
+        return fromFrameParentId;
+    }
+
+    public void setFromFrameParentId(Integer fromFrameParentId) {
+        this.fromFrameParentId = fromFrameParentId;
+    }
 
     public Integer getFromDeviceMemberId() {
         return fromDeviceMemberId;
@@ -255,6 +550,141 @@ public class InteractionDO implements Serializable {
         this.fromDeviceMemberId = fromDeviceMemberId;
     }
 
+    public Integer getFromCustomerId() {
+        return fromCustomerId;
+    }
+
+    public void setFromCustomerId(Integer fromCustomerId) {
+        this.fromCustomerId = fromCustomerId;
+    }
+
+    public String getFromMemberName() {
+        return fromMemberName;
+    }
+
+    public void setFromMemberName(String fromMemberName) {
+        this.fromMemberName = fromMemberName;
+    }
+
+    public String getFromMemberFace() {
+        return fromMemberFace;
+    }
+
+    public void setFromMemberFace(String fromMemberFace) {
+        this.fromMemberFace = fromMemberFace;
+    }
+
+    public Integer getFromClerkId() {
+        return fromClerkId;
+    }
+
+    public void setFromClerkId(Integer fromClerkId) {
+        this.fromClerkId = fromClerkId;
+    }
+
+    public String getFromRoleName() {
+        return fromRoleName;
+    }
+
+    public void setFromRoleName(String fromRoleName) {
+        this.fromRoleName = fromRoleName;
+    }
+
+    public Integer getToDeviceId() {
+        return toDeviceId;
+    }
+
+    public void setToDeviceId(Integer toDeviceId) {
+        this.toDeviceId = toDeviceId;
+    }
+
+    public String getToEthMac() {
+        return toEthMac;
+    }
+
+    public void setToEthMac(String toEthMac) {
+        this.toEthMac = toEthMac;
+    }
+
+    public String getToEthIp() {
+        return toEthIp;
+    }
+
+    public void setToEthIp(String toEthIp) {
+        this.toEthIp = toEthIp;
+    }
+
+    public String getToSipId() {
+        return toSipId;
+    }
+
+    public void setToSipId(String toSipId) {
+        this.toSipId = toSipId;
+    }
+
+    public Integer getToDeviceType() {
+        return toDeviceType;
+    }
+
+    public void setToDeviceType(Integer toDeviceType) {
+        this.toDeviceType = toDeviceType;
+    }
+
+    public String getToDeviceName() {
+        return toDeviceName;
+    }
+
+    public void setToDeviceName(String toDeviceName) {
+        this.toDeviceName = toDeviceName;
+    }
+
+    public String getToDevicePhoneNumber() {
+        return toDevicePhoneNumber;
+    }
+
+    public void setToDevicePhoneNumber(String toDevicePhoneNumber) {
+        this.toDevicePhoneNumber = toDevicePhoneNumber;
+    }
+
+    public Integer getToDeviceFrameId() {
+        return toDeviceFrameId;
+    }
+
+    public void setToDeviceFrameId(Integer toDeviceFrameId) {
+        this.toDeviceFrameId = toDeviceFrameId;
+    }
+
+    public Integer getToFrameType() {
+        return toFrameType;
+    }
+
+    public void setToFrameType(Integer toFrameType) {
+        this.toFrameType = toFrameType;
+    }
+
+    public String getToFrameName() {
+        return toFrameName;
+    }
+
+    public void setToFrameName(String toFrameName) {
+        this.toFrameName = toFrameName;
+    }
+
+    public String getToFrameFullName() {
+        return toFrameFullName;
+    }
+
+    public void setToFrameFullName(String toFrameFullName) {
+        this.toFrameFullName = toFrameFullName;
+    }
+
+    public Integer getToFrameParentId() {
+        return toFrameParentId;
+    }
+
+    public void setToFrameParentId(Integer toFrameParentId) {
+        this.toFrameParentId = toFrameParentId;
+    }
 
     public Integer getToDeviceMemberId() {
         return toDeviceMemberId;
@@ -264,6 +694,45 @@ public class InteractionDO implements Serializable {
         this.toDeviceMemberId = toDeviceMemberId;
     }
 
+    public Integer getToCustomerId() {
+        return toCustomerId;
+    }
+
+    public void setToCustomerId(Integer toCustomerId) {
+        this.toCustomerId = toCustomerId;
+    }
+
+    public String getToMemberName() {
+        return toMemberName;
+    }
+
+    public void setToMemberName(String toMemberName) {
+        this.toMemberName = toMemberName;
+    }
+
+    public String getToMemberFace() {
+        return toMemberFace;
+    }
+
+    public void setToMemberFace(String toMemberFace) {
+        this.toMemberFace = toMemberFace;
+    }
+
+    public Integer getToClerkId() {
+        return toClerkId;
+    }
+
+    public void setToClerkId(Integer toClerkId) {
+        this.toClerkId = toClerkId;
+    }
+
+    public String getToRoleName() {
+        return toRoleName;
+    }
+
+    public void setToRoleName(String toRoleName) {
+        this.toRoleName = toRoleName;
+    }
 
     public Boolean getOuteriorAction() {
         return outeriorAction;
@@ -273,7 +742,6 @@ public class InteractionDO implements Serializable {
         this.outeriorAction = outeriorAction;
     }
 
-
     public String getOuteriorActionNumber() {
         return outeriorActionNumber;
     }
@@ -282,7 +750,6 @@ public class InteractionDO implements Serializable {
         this.outeriorActionNumber = outeriorActionNumber;
     }
 
-
     public Long getOuteriorActionMemberId() {
         return outeriorActionMemberId;
     }
@@ -291,7 +758,6 @@ public class InteractionDO implements Serializable {
         this.outeriorActionMemberId = outeriorActionMemberId;
     }
 
-
     public Integer getRemarkId() {
         return remarkId;
     }
@@ -300,7 +766,6 @@ public class InteractionDO implements Serializable {
         this.remarkId = remarkId;
     }
 
-
     public Long getActionStart() {
         return actionStart;
     }
@@ -309,7 +774,6 @@ public class InteractionDO implements Serializable {
         this.actionStart = actionStart;
     }
 
-
     public Long getActionAccept() {
         return actionAccept;
     }
@@ -318,7 +782,6 @@ public class InteractionDO implements Serializable {
         this.actionAccept = actionAccept;
     }
 
-
     public Long getActionEnd() {
         return actionEnd;
     }
@@ -327,6 +790,29 @@ public class InteractionDO implements Serializable {
         this.actionEnd = actionEnd;
     }
 
+    public Integer getActionEndMemberId() {
+        return actionEndMemberId;
+    }
+
+    public void setActionEndMemberId(Integer actionEndMemberId) {
+        this.actionEndMemberId = actionEndMemberId;
+    }
+
+    public String getActionEndMemberName() {
+        return actionEndMemberName;
+    }
+
+    public void setActionEndMemberName(String actionEndMemberName) {
+        this.actionEndMemberName = actionEndMemberName;
+    }
+
+    public Long getActionCompleted() {
+        return actionCompleted;
+    }
+
+    public void setActionCompleted(Long actionCompleted) {
+        this.actionCompleted = actionCompleted;
+    }
 
     public String getData() {
         return data;
@@ -336,7 +822,6 @@ public class InteractionDO implements Serializable {
         this.data = data;
     }
 
-
     public String getErrorCode() {
         return errorCode;
     }
@@ -345,7 +830,6 @@ public class InteractionDO implements Serializable {
         this.errorCode = errorCode;
     }
 
-
     public String getErrorMessage() {
         return errorMessage;
     }
@@ -361,4 +845,78 @@ public class InteractionDO implements Serializable {
     public void setActionDirectionType(Integer actionDirectionType) {
         this.actionDirectionType = actionDirectionType;
     }
+
+    public String getActionStatus() {
+        return actionStatus;
+    }
+
+    public void setActionStatus(String actionStatus) {
+        this.actionStatus = actionStatus;
+    }
+
+    public Integer getRelativeId() {
+        return relativeId;
+    }
+
+    public void setRelativeId(Integer relativeId) {
+        this.relativeId = relativeId;
+    }
+
+    public String getRelativeName() {
+        return relativeName;
+    }
+
+    public void setRelativeName(String relativeName) {
+        this.relativeName = relativeName;
+    }
+
+
+    public String getSpreadMemberPath() {
+        return spreadMemberPath;
+    }
+
+    public void setSpreadMemberPath(String spreadMemberPath) {
+        this.spreadMemberPath = spreadMemberPath;
+    }
+
+    public String getSpreadMemberIds() {
+        return spreadMemberIds;
+    }
+
+    public void setSpreadMemberIds(String spreadMemberIds) {
+        this.spreadMemberIds = spreadMemberIds;
+    }
+
+    public String getReachedMemberIds() {
+        return reachedMemberIds;
+    }
+
+    public void setReachedMemberIds(String reachedMemberIds) {
+        this.reachedMemberIds = reachedMemberIds;
+    }
+
+    public ArrayList<Integer[]> getSpreadMemberPathArray() {
+        if (spreadMemberPathArray != null) {
+            return spreadMemberPathArray;
+        }
+        if (!Strings.isNullOrEmpty(spreadMemberPath)){
+            ArrayList<Integer[]> arrayList = new ArrayList<>();
+            JSONArray jsonArray = JSON.parseArray(spreadMemberPath);
+            for(Object item:jsonArray){
+                JSONArray itemJsonArray = (JSONArray)item;
+                Integer[] itemArray = new Integer[itemJsonArray.size()];
+                for(int i=0;i<itemJsonArray.size();i++){
+                    itemArray[i] = itemJsonArray.getInteger(i);
+                }
+                arrayList.add(itemArray);
+            }
+            spreadMemberPathArray = arrayList;
+            return spreadMemberPathArray;
+        }
+        return null;
+    }
+
+    public void setSpreadMemberPathArray(ArrayList<Integer[]> spreadMemberPathArray) {
+        this.spreadMemberPathArray = spreadMemberPathArray;
+    }
 }

+ 49 - 3
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/dos/RoleDO.java

@@ -2,8 +2,6 @@ package com.wdkl.ncs.android.middleware.model.dos;
 
 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
 import com.fasterxml.jackson.databind.annotation.JsonNaming;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
 import com.wdkl.ncs.android.middleware.model.annotation.Column;
 import com.wdkl.ncs.android.middleware.model.annotation.Id;
 import com.wdkl.ncs.android.middleware.model.annotation.PrimaryKeyField;
@@ -11,6 +9,9 @@ import com.wdkl.ncs.android.middleware.model.annotation.Table;
 
 import java.io.Serializable;
 
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
 @Table(name = "ncs_role")
 @ApiModel
 @JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class)
@@ -19,7 +20,7 @@ public class RoleDO implements Serializable {
     /**
      * 角色主键
      */
-    @Column(name = "role_id")
+    @Id(name = "role_id")
     @ApiModelProperty(value = "角色主键", required = false)
     private Integer roleId;
     /**
@@ -58,6 +59,27 @@ public class RoleDO implements Serializable {
     @Column(name = "hidden_in_seller")
     @ApiModelProperty(value = "是否为隐藏角色,不可见,管理端可见", required = false)
     private Boolean hiddenInSeller;
+    /**
+     * 是否处理呼叫,唯一,相斥
+     */
+    @Column(name = "bool_main")
+    @ApiModelProperty(value = "是否处理呼叫,唯一,相斥", required = false)
+    private Boolean boolMain;
+
+    /**
+     * 上级领导角色Id
+     */
+    @Column(name = "leader_role_id",allowNullUpdate = true)
+    @ApiModelProperty(value = "上级领导角色Id", required = false)
+    private Integer leaderRoleId;
+
+
+    /**
+     * 角色类型,内置逻辑设置 RoleTypeEnum 名称
+     */
+    @Column(name = "role_type")
+    @ApiModelProperty(value = "角色描述", required = false)
+    private String roleType;
 
 
     @PrimaryKeyField
@@ -123,4 +145,28 @@ public class RoleDO implements Serializable {
         this.hiddenInSeller = hiddenInSeller;
     }
 
+    public Boolean getBoolMain() {
+        return boolMain;
+    }
+
+    public void setBoolMain(Boolean boolMain) {
+        this.boolMain = boolMain;
+    }
+
+
+    public Integer getLeaderRoleId() {
+        return leaderRoleId;
+    }
+
+    public void setLeaderRoleId(Integer leaderRoleId) {
+        this.leaderRoleId = leaderRoleId;
+    }
+
+    public String getRoleType() {
+        return roleType;
+    }
+
+    public void setRoleType(String roleType) {
+        this.roleType = roleType;
+    }
 }

+ 45 - 669
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/InteractionVO.java

@@ -1,13 +1,10 @@
 package com.wdkl.ncs.android.middleware.model.vo;
 
+import android.support.annotation.Nullable;
+
 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
 import com.fasterxml.jackson.databind.annotation.JsonNaming;
-import com.wdkl.ncs.android.middleware.model.annotation.Column;
-import com.wdkl.ncs.android.middleware.model.annotation.Id;
-
-import io.swagger.annotations.ApiModelProperty;
-
-import java.io.Serializable;
+import com.wdkl.ncs.android.middleware.model.dos.InteractionDO;
 
 /**
  * @program nc
@@ -16,668 +13,47 @@ import java.io.Serializable;
  * @create: 2021/04/02 15:27
  */
 @JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class)
-public class InteractionVO implements Serializable {
-
-    /**
-     * id
-     */
-    @Column(name = "id")
-    @ApiModelProperty(value = "id", required = false)
-    @Id(name = "id")
-    private Integer id;
-    /**
-     * 创建时间
-     */
-    @Column(name = "create_date")
-    @ApiModelProperty(value = "创建时间", required = false)
-    private Long createDate;
-    /**
-     * 科室Id
-     */
-    @Column(name = "part_id")
-    @ApiModelProperty(value = "科室Id", required = false)
-    private Integer partId;
-    /**
-     * 交互类型(1:语音通话,2:视频通话,3:卫生间紧急呼叫,4:语音留言,5:文本消息,6:按键事件)
-     */
-    @Column(name = "action_type")
-    @ApiModelProperty(value = "交互类型(1:语音通话,2:视频通话,3:卫生间紧急呼叫,4:语音留言,5:文本消息,6:按键事件)", required = false)
-    private String actionType;
-    /**
-     * SUCCESS、FAILED
-     */
-    @Column(name = "action_result")
-    @ApiModelProperty(value = "0失败,1成功", required = false)
-    private Integer actionResult;
-    /**
-     * 交互发起设备Id
-     */
-    @Column(name = "from_device_id")
-    @ApiModelProperty(value = "交互发起设备Id", required = false)
-    private Integer fromDeviceId;
-    /**
-     * 交互对象设备Id
-     */
-    @Column(name = "to_device_id")
-    @ApiModelProperty(value = "交互对象设备Id", required = false)
-    private Integer toDeviceId;
-    /**
-     * 发起设备所在空间结构
-     */
-    @Column(name = "from_device_frame_id")
-    @ApiModelProperty(value = "发起设备所在空间结构", required = false)
-    private Integer fromDeviceFrameId;
-    /**
-     * 交互对象所在空间结果
-     */
-    @Column(name = "to_device_frame_id")
-    @ApiModelProperty(value = "交互对象所在空间结果", required = false)
-    private Integer toDeviceFrameId;
-    /**
-     * 交互发起设备使用者member_id
-     */
-    @Column(name = "from_device_member_id")
-    @ApiModelProperty(value = "交互发起设备使用者member_id", required = false)
-    private Integer fromDeviceMemberId;
-    /**
-     * 交互对象设备使用者member_id
-     */
-    @Column(name = "to_device_member_id")
-    @ApiModelProperty(value = "交互对象设备使用者member_id", required = false)
-    private Integer toDeviceMemberId;
-    /**
-     * 交互开始时间(通话时指通话接通时间,与create_time相同)
-     */
-    @Column(name = "action_start")
-    @ApiModelProperty(value = "交互开始时间(通话时指通话接通时间,与create_time相同)", required = false)
-    private Long actionStart;
-    /**
-     * 应答时间
-     */
-    @Column(name = "action_accept")
-    @ApiModelProperty(value = "应答时间", required = false)
-    private Long actionAccept;
-    /**
-     * 交互结束时间(通话时指通话挂断时间,通话挂断后要更新此字段)。事件响应时间、语音已读时间
-     */
-    @Column(name = "action_end")
-    @ApiModelProperty(value = "交互结束时间(通话时指通话挂断时间,通话挂断后要更新此字段)。事件响应时间、语音已读时间", required = false)
-    private Long actionEnd;
-    /**
-     * 交互处理者的member_id
-     */
-    @Column(name = "action_end_member_id")
-    @ApiModelProperty(value = "交互处理者的member_id", required = false)
-    private Integer actionEndMemberId;
-    /**
-     * 交互数据(类型1,2,3为空)(类型4为音频文件地址)(5为文本内容)(类型6为具体按键类型)
-     */
-    @Column(name = "data")
-    @ApiModelProperty(value = "交互数据(类型1,2,3为空)(类型4为音频文件地址)(5为文本内容)(类型6为具体按键类型)", required = false)
-    private String data;
-
-    @Column(name = "action_direction_type")
-    @ApiModelProperty(value = "交互方向类型(1:分机到主机、腕表、医生机,2:主机、腕表、医生机到分机,3主机、腕表、医生机之间互通,4:分机到分机)",required = false)
-    private Integer actionDirectionType;
-
-    /** 用户 **/
-    @Column(name = "from_customer_id")
-    @ApiModelProperty(value = "发起端用户CustomId", required = false)
-    private Integer fromCustomerId;
-
-    @Column(name = "from_member_name")
-    @ApiModelProperty(value = "发起端用户姓名", required = false)
-    private String fromMemberName;
-
-    @Column(name = "from_member_face")
-    @ApiModelProperty(value = "发起端用户头像", required = false)
-    private String fromMemberFace;
-
-    @Column(name = "from_clerk_id")
-    @ApiModelProperty(value = "发起端用户成员Id,发起端为护士主机,腕表,医生机时才有", required = false)
-    private Integer fromClerkId;
-
-    @Column(name = "from_role_name")
-    @ApiModelProperty(value = "发起端用户角色名(医生、护士、护工)", required = false)
-    private String fromRoleName;
-
-
-    @Column(name = "to_customer_id")
-    @ApiModelProperty(value = "目的端用户CustomId", required = false)
-    private Integer toCustomerId;
-
-    @Column(name = "to_member_name")
-    @ApiModelProperty(value = "目的端用户姓名", required = false)
-    private String toMemberName;
-
-    @Column(name = "to_member_face")
-    @ApiModelProperty(value = "目的端用户头像", required = false)
-    private String toMemberFace;
-
-    @Column(name = "to_clerk_id")
-    @ApiModelProperty(value = "目的端用户成员Id,目的端为护士主机,腕表,医生机时才有", required = false)
-    private Integer toClerkId;
-
-    @Column(name = "to_role_name")
-    @ApiModelProperty(value = "目的端用户角色名(医生、护士、护工)", required = false)
-    private String toRoleName;
-
-
-    /** 设备 **/
-    @Column(name = "from_eth_mac")
-    @ApiModelProperty(value = "发起端设备的有线以太网卡MAC地址", required = false)
-    private String fromEthMac;
-
-    @Column(name = "from_eth_ip")
-    @ApiModelProperty(value = "发起端设备的有线以太网卡分配的IP地址", required = false)
-    private String fromEthIp;
-
-
-    @Column(name = "from_sip_id")
-    @ApiModelProperty(value = "发起端设备的SIP账号", required = false)
-    private String fromSipId;
-
-    @Column(name = "from_device_type")
-    @ApiModelProperty(value = "发起端设备的设备类型", required = false)
-    private Integer fromDeviceType;
-
-    @Column(name = "from_device_name")
-    @ApiModelProperty(value = "发起端设备的名称", required = false)
-    private String fromDeviceName;
-
-    @Column(name = "from_device_phone_number" )
-    @ApiModelProperty(value="发起端设备号码",required=false)
-    private String fromDevicePhoneNumber;
-
-
-    @Column(name = "to_eth_mac")
-    @ApiModelProperty(value = "目的端设备的有线以太网卡MAC地址", required = false)
-    private String toEthMac;
-
-    @Column(name = "to_eth_ip")
-    @ApiModelProperty(value = "目的端设备的有线以太网卡分配的IP地址", required = false)
-    private String toEthIp;
-
-
-    @Column(name = "to_sip_id")
-    @ApiModelProperty(value = "目的端设备的SIP账号", required = false)
-    private String toSipId;
-
-    @Column(name = "to_device_type")
-    @ApiModelProperty(value = "目的端设备的设备类型", required = false)
-    private Integer toDeviceType;
-
-    @Column(name = "to_device_name")
-    @ApiModelProperty(value = "目的端设备的名称", required = false)
-    private String toDeviceName;
-
-    @Column(name = "to_device_phone_number" )
-    @ApiModelProperty(value="目的端设备号码",required=false)
-    private String toDevicePhoneNumber;
-
-
-    /** 空间结构 **/
-
-    @Column(name = "from_frame_type")
-    @ApiModelProperty(value = "发起端空间结构类型:0楼、1病房、2床位", required = false)
-    private Integer fromFrameType;
-
-
-    @Column(name = "from_frame_name")
-    @ApiModelProperty(value = "发起端空间结构名称", required = false)
-    private String fromFrameName;
-
-    @Column(name = "from_frame_full_name")
-    @ApiModelProperty(value = "发起端空间结构全名", required = false)
-    private String fromFrameFullName;
-
-    @Column(name = "from_frame_parent_id")
-    @ApiModelProperty(value = "发起端空间父级id", required = false)
-    private String fromFrameParentId;
-
-
-    @Column(name = "to_frame_type")
-    @ApiModelProperty(value = "目的端空间结构类型:0楼、1病房、2床位", required = false)
-    private Integer toFrameType;
-
-
-    @Column(name = "to_frame_name")
-    @ApiModelProperty(value = "目的端空间结构名称", required = false)
-    private String toFrameName;
-
-    @Column(name = "to_frame_full_name")
-    @ApiModelProperty(value = "目的端空间结构全名", required = false)
-    private String toFrameFullName;
-
-    @Column(name = "to_frame_parent_id")
-    @ApiModelProperty(value = "目的端空间父级id", required = false)
-    private String toFrameParentId;
-
-
-    @Column(name = "relative_id")
-    @ApiModelProperty(value = "亲属id,外部来电时,from_member亲属的memberId", required = false)
-    private Integer relativeId;
-
-    @Column(name = "relative_name")
-    @ApiModelProperty(value = "外部来电时,from_member亲属关系", required = false)
-    private String relativeName;
-
-    @Column(name = "action_end_member_name")
-    @ApiModelProperty(value = "交互处理者的用户姓名", required = false)
-    private String actionEndMemberName;
-
-
-    @Column(name = "action_status")
-    @ApiModelProperty(value = "事件类型的交互状态(包括:发出,响应,取消,完成)",required = false)
-    private String actionStatus;
-
-
-
-    public Integer getId() {
-        return id;
-    }
-
-    public void setId(Integer id) {
-        this.id = id;
-    }
-
-    public Long getCreateDate() {
-        return createDate;
-    }
-
-    public void setCreateDate(Long createDate) {
-        this.createDate = createDate;
-    }
-
-    public Integer getPartId() {
-        return partId;
-    }
-
-    public void setPartId(Integer partId) {
-        this.partId = partId;
-    }
-
-    public String getActionType() {
-        return actionType;
-    }
-
-    public void setActionType(String actionType) {
-        this.actionType = actionType;
-    }
-
-    public Integer getActionResult() {
-        return actionResult;
-    }
-
-    public void setActionResult(Integer actionResult) {
-        this.actionResult = actionResult;
-    }
-
-    public Integer getFromDeviceId() {
-        return fromDeviceId;
-    }
-
-    public void setFromDeviceId(Integer fromDeviceId) {
-        this.fromDeviceId = fromDeviceId;
-    }
-
-    public Integer getToDeviceId() {
-        return toDeviceId;
-    }
-
-    public void setToDeviceId(Integer toDeviceId) {
-        this.toDeviceId = toDeviceId;
-    }
-
-    public Integer getFromDeviceFrameId() {
-        return fromDeviceFrameId;
-    }
-
-    public void setFromDeviceFrameId(Integer fromDeviceFrameId) {
-        this.fromDeviceFrameId = fromDeviceFrameId;
-    }
-
-    public Integer getToDeviceFrameId() {
-        return toDeviceFrameId;
-    }
-
-    public void setToDeviceFrameId(Integer toDeviceFrameId) {
-        this.toDeviceFrameId = toDeviceFrameId;
-    }
-
-    public Integer getFromDeviceMemberId() {
-        return fromDeviceMemberId;
-    }
-
-    public void setFromDeviceMemberId(Integer fromDeviceMemberId) {
-        this.fromDeviceMemberId = fromDeviceMemberId;
-    }
-
-    public Integer getToDeviceMemberId() {
-        return toDeviceMemberId;
-    }
-
-    public void setToDeviceMemberId(Integer toDeviceMemberId) {
-        this.toDeviceMemberId = toDeviceMemberId;
-    }
-
-    public Long getActionStart() {
-        return actionStart;
-    }
-
-    public void setActionStart(Long actionStart) {
-        this.actionStart = actionStart;
-    }
-
-    public Long getActionAccept() {
-        return actionAccept;
-    }
-
-    public void setActionAccept(Long actionAccept) {
-        this.actionAccept = actionAccept;
-    }
-
-    public Long getActionEnd() {
-        return actionEnd;
-    }
-
-    public void setActionEnd(Long actionEnd) {
-        this.actionEnd = actionEnd;
-    }
-
-    public Integer getActionEndMemberId() {
-        return actionEndMemberId;
-    }
-
-    public void setActionEndMemberId(Integer actionEndMemberId) {
-        this.actionEndMemberId = actionEndMemberId;
-    }
-
-    public String getData() {
-        return data;
-    }
-
-    public void setData(String data) {
-        this.data = data;
-    }
-
-    public Integer getActionDirectionType() {
-        return actionDirectionType;
-    }
-
-    public void setActionDirectionType(Integer actionDirectionType) {
-        this.actionDirectionType = actionDirectionType;
-    }
-
-    public Integer getFromCustomerId() {
-        return fromCustomerId;
-    }
-
-    public void setFromCustomerId(Integer fromCustomerId) {
-        this.fromCustomerId = fromCustomerId;
-    }
-
-    public String getFromMemberName() {
-        return fromMemberName;
-    }
-
-    public void setFromMemberName(String fromMemberName) {
-        this.fromMemberName = fromMemberName;
-    }
-
-    public String getFromMemberFace() {
-        return fromMemberFace;
-    }
-
-    public void setFromMemberFace(String fromMemberFace) {
-        this.fromMemberFace = fromMemberFace;
-    }
-
-    public Integer getFromClerkId() {
-        return fromClerkId;
-    }
-
-    public void setFromClerkId(Integer fromClerkId) {
-        this.fromClerkId = fromClerkId;
-    }
-
-    public String getFromRoleName() {
-        return fromRoleName;
-    }
-
-    public void setFromRoleName(String fromRoleName) {
-        this.fromRoleName = fromRoleName;
-    }
-
-    public Integer getToCustomerId() {
-        return toCustomerId;
-    }
-
-    public void setToCustomerId(Integer toCustomerId) {
-        this.toCustomerId = toCustomerId;
-    }
-
-    public String getToMemberName() {
-        return toMemberName;
-    }
-
-    public void setToMemberName(String toMemberName) {
-        this.toMemberName = toMemberName;
-    }
-
-    public String getToMemberFace() {
-        return toMemberFace;
-    }
-
-    public void setToMemberFace(String toMemberFace) {
-        this.toMemberFace = toMemberFace;
-    }
-
-    public Integer getToClerkId() {
-        return toClerkId;
-    }
-
-    public void setToClerkId(Integer toClerkId) {
-        this.toClerkId = toClerkId;
-    }
-
-    public String getToRoleName() {
-        return toRoleName;
-    }
-
-    public void setToRoleName(String toRoleName) {
-        this.toRoleName = toRoleName;
-    }
-
-    public String getFromEthMac() {
-        return fromEthMac;
-    }
-
-    public void setFromEthMac(String fromEthMac) {
-        this.fromEthMac = fromEthMac;
-    }
-
-    public String getFromEthIp() {
-        return fromEthIp;
-    }
-
-    public void setFromEthIp(String fromEthIp) {
-        this.fromEthIp = fromEthIp;
-    }
-
-    public String getFromSipId() {
-        return fromSipId;
-    }
-
-    public void setFromSipId(String fromSipId) {
-        this.fromSipId = fromSipId;
-    }
-
-    public Integer getFromDeviceType() {
-        return fromDeviceType;
-    }
-
-    public void setFromDeviceType(Integer fromDeviceType) {
-        this.fromDeviceType = fromDeviceType;
-    }
-
-    public String getFromDeviceName() {
-        return fromDeviceName;
-    }
-
-    public void setFromDeviceName(String fromDeviceName) {
-        this.fromDeviceName = fromDeviceName;
-    }
-
-    public String getFromDevicePhoneNumber() {
-        return fromDevicePhoneNumber;
-    }
-
-    public void setFromDevicePhoneNumber(String fromDevicePhoneNumber) {
-        this.fromDevicePhoneNumber = fromDevicePhoneNumber;
-    }
-
-    public String getToEthMac() {
-        return toEthMac;
-    }
-
-    public void setToEthMac(String toEthMac) {
-        this.toEthMac = toEthMac;
-    }
-
-    public String getToEthIp() {
-        return toEthIp;
-    }
-
-    public void setToEthIp(String toEthIp) {
-        this.toEthIp = toEthIp;
-    }
-
-    public String getToSipId() {
-        return toSipId;
-    }
-
-    public void setToSipId(String toSipId) {
-        this.toSipId = toSipId;
-    }
-
-    public Integer getToDeviceType() {
-        return toDeviceType;
-    }
-
-    public void setToDeviceType(Integer toDeviceType) {
-        this.toDeviceType = toDeviceType;
-    }
-
-    public String getToDeviceName() {
-        return toDeviceName;
-    }
-
-    public void setToDeviceName(String toDeviceName) {
-        this.toDeviceName = toDeviceName;
-    }
-
-    public String getToDevicePhoneNumber() {
-        return toDevicePhoneNumber;
-    }
-
-    public void setToDevicePhoneNumber(String toDevicePhoneNumber) {
-        this.toDevicePhoneNumber = toDevicePhoneNumber;
-    }
-
-    public Integer getFromFrameType() {
-        return fromFrameType;
-    }
-
-    public void setFromFrameType(Integer fromFrameType) {
-        this.fromFrameType = fromFrameType;
-    }
-
-    public String getFromFrameName() {
-        return fromFrameName;
-    }
-
-    public void setFromFrameName(String fromFrameName) {
-        this.fromFrameName = fromFrameName;
-    }
-
-    public String getFromFrameFullName() {
-        return fromFrameFullName;
-    }
-
-    public void setFromFrameFullName(String fromFrameFullName) {
-        this.fromFrameFullName = fromFrameFullName;
-    }
-
-    public String getFromFrameParentId() {
-        return fromFrameParentId;
-    }
-
-    public void setFromFrameParentId(String fromFrameParentId) {
-        this.fromFrameParentId = fromFrameParentId;
-    }
-
-    public Integer getToFrameType() {
-        return toFrameType;
-    }
-
-    public void setToFrameType(Integer toFrameType) {
-        this.toFrameType = toFrameType;
-    }
-
-    public String getToFrameName() {
-        return toFrameName;
-    }
-
-    public void setToFrameName(String toFrameName) {
-        this.toFrameName = toFrameName;
-    }
-
-    public String getToFrameFullName() {
-        return toFrameFullName;
-    }
-
-    public void setToFrameFullName(String toFrameFullName) {
-        this.toFrameFullName = toFrameFullName;
-    }
-
-    public String getToFrameParentId() {
-        return toFrameParentId;
-    }
-
-    public void setToFrameParentId(String toFrameParentId) {
-        this.toFrameParentId = toFrameParentId;
-    }
-
-    public Integer getRelativeId() {
-        return relativeId;
-    }
-
-    public void setRelativeId(Integer relativeId) {
-        this.relativeId = relativeId;
-    }
-
-    public String getRelativeName() {
-        return relativeName;
-    }
-
-    public void setRelativeName(String relativeName) {
-        this.relativeName = relativeName;
-    }
-
-    public String getActionEndMemberName() {
-        return actionEndMemberName;
-    }
-
-    public void setActionEndMemberName(String actionEndMemberName) {
-        this.actionEndMemberName = actionEndMemberName;
-    }
-
-    public String getActionStatus() {
-        return actionStatus;
-    }
-
-    public void setActionStatus(String actionStatus) {
-        this.actionStatus = actionStatus;
+public class InteractionVO extends InteractionDO {
+
+    @Override
+    public String toString() {
+        return "InteractionVO{" +
+                "fromCustomerId=" + getFromCustomerId() +
+                ", fromMemberName='" + getFromDeviceType() + '\'' +
+                ", fromMemberFace='" + getFromMemberFace() + '\'' +
+                ", fromClerkId=" + getFromClerkId() +
+                ", fromRoleName='" + getFromRoleName() + '\'' +
+                ", toCustomerId=" + getToCustomerId() +
+                ", toMemberName='" + getToMemberName() + '\'' +
+                ", toMemberFace='" + getToMemberFace() + '\'' +
+                ", toClerkId=" + getToClerkId() +
+                ", toRoleName='" + getToRoleName() + '\'' +
+                ", fromEthMac='" + getFromEthMac() + '\'' +
+                ", fromEthIp='" + getFromEthIp() + '\'' +
+                ", fromSipId='" + getFromSipId() + '\'' +
+                ", fromDeviceType=" + getFromDeviceType() +
+                ", toEthMac='" + getToEthMac() + '\'' +
+                ", toEthIp='" + getToEthIp() + '\'' +
+                ", toSipId='" + getToSipId() + '\'' +
+                ", toDeviceType=" + getToDeviceType() +
+                ", fromFrameType=" + getFromFrameType() +
+                ", fromFrameName='" + getFromFrameName() + '\'' +
+                ", fromFrameFullName='" + getFromFrameFullName() + '\'' +
+                ", toFrameType=" + getToFrameType() +
+                ", toFrameName='" + getToFrameName() + '\'' +
+                ", toFrameFullName='" + getToFrameFullName() + '\'' +
+                ", relativeId=" + getRelativeId() +
+                ", relativeName='" + getRelativeName() + '\'' +
+                ", actionStatus='" + getActionStatus() + '\'' +
+                ", actionEndMemberId=" + getActionEndMemberId() +
+                ", actionEndMemberName='" + getActionEndMemberName() + '\'' +
+                '}';
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        InteractionVO other= (InteractionVO)obj;
+        return this.getId().equals(other.getId());
     }
 }
+

+ 245 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TaskSchedule.java

@@ -0,0 +1,245 @@
+package com.wdkl.ncs.android.middleware.tcp;
+
+import com.wdkl.ncs.android.middleware.tcp.util.DateUtil;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 描述
+ *
+ * @author allen
+ * 2022-11-28 10:15
+ */
+public class TaskSchedule {
+    private final String TAG = getClass().getSimpleName();
+
+    private ScheduledThreadPoolExecutor pool;
+    //任务池
+    private final ConcurrentHashMap<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<>();
+
+    private TaskSchedule() {
+        pool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(5);
+        pool.setRemoveOnCancelPolicy(true);
+    }
+
+    //单例
+    private static class TaskScheduleHolder {
+        private static final TaskSchedule instance = new TaskSchedule();
+    }
+
+    public static TaskSchedule getInstance() {
+        return TaskScheduleHolder.instance;
+    }
+
+    /**
+     * Submits a one-shot task that becomes enabled after the given delay.
+     *
+     * @param command the task to execute
+     * @param delay   the time from now to delay execution
+     * @param unit    the time unit of the delay parameter
+     * @return a ScheduledFuture representing pending completion of
+     * the task and whose {@code get()} method will return
+     * {@code null} upon completion
+     * @throws RejectedExecutionException if the task cannot be
+     *                                    scheduled for execution
+     * @throws NullPointerException       if command or unit is null
+     */
+    public ScheduledFuture<?> runDelay(Runnable command,
+                                       long delay, TimeUnit unit) {
+        if (pool != null) {
+            return pool.schedule(command, delay, unit);
+        }
+        return null;
+    }
+
+    /**
+     * Submits a periodic action that becomes enabled first after the
+     * given initial delay, and subsequently with the given period;
+     * that is, executions will commence after
+     * {@code initialDelay}, then {@code initialDelay + period}, then
+     * {@code initialDelay + 2 * period}, and so on.
+     *
+     * <p>The sequence of task executions continues indefinitely until
+     * one of the following exceptional completions occur:
+     * <ul>
+     * <li>The task is {@linkplain Future#cancel explicitly cancelled}
+     * via the returned future.
+     * <li>The executor terminates, also resulting in task cancellation.
+     * <li>An execution of the task throws an exception.  In this case
+     * calling {@link Future#get() get} on the returned future will throw
+     * {@link ExecutionException}, holding the exception as its cause.
+     * </ul>
+     * Subsequent executions are suppressed.  Subsequent calls to
+     * {@link Future#isDone isDone()} on the returned future will
+     * return {@code true}.
+     *
+     * <p>If any execution of this task takes longer than its period, then
+     * subsequent executions may start late, but will not concurrently
+     * execute.
+     *
+     * @param command      the task to execute
+     * @param initialDelay the time to delay first execution
+     * @param period       the period between successive executions
+     * @param unit         the time unit of the initialDelay and period parameters
+     * @return a ScheduledFuture representing pending completion of
+     * the series of repeated tasks.  The future's {@link
+     * Future#get() get()} method will never return normally,
+     * and will throw an exception upon task cancellation or
+     * abnormal termination of a task execution.
+     * @throws RejectedExecutionException if the task cannot be
+     *                                    scheduled for execution
+     * @throws NullPointerException       if command or unit is null
+     * @throws IllegalArgumentException   if period less than or equal to zero
+     */
+    public ScheduledFuture<?> runAtFixedRate(Runnable command,
+                                             long initialDelay,
+                                             long period,
+                                             TimeUnit unit) {
+        if (pool != null) {
+            return pool.scheduleAtFixedRate(command, initialDelay, period, unit);
+        }
+        return null;
+    }
+
+    public ScheduledFuture<?> scheduleTask(@NotNull String taskName, @NotNull Runnable task, @NotNull Long startTime, Long period) {
+        if (pool != null) {
+            ScheduledFuture<?> future = pool.scheduleAtFixedRate(task, startTime, period, TimeUnit.MILLISECONDS);
+            cancelTask(taskName);
+            this.taskFutures.put(taskName, future);
+            return future;
+        }
+        return null;
+    }
+
+    /**
+     * 取消任务
+     *
+     * @param taskName
+     */
+    public void cancelTask(String taskName) {
+        if (pool != null) {
+            ScheduledFuture<?> future = this.taskFutures.get(taskName);
+            if (future != null) {
+                future.cancel(true);
+            }
+            this.taskFutures.remove(taskName);
+        }
+    }
+
+    /**
+     * Submits a periodic action that becomes enabled first after the
+     * given initial delay, and subsequently with the given delay
+     * between the termination of one execution and the commencement of
+     * the next.
+     *
+     * <p>The sequence of task executions continues indefinitely until
+     * one of the following exceptional completions occur:
+     * <ul>
+     * <li>The task is {@linkplain Future#cancel explicitly cancelled}
+     * via the returned future.
+     * <li>The executor terminates, also resulting in task cancellation.
+     * <li>An execution of the task throws an exception.  In this case
+     * calling {@link Future#get() get} on the returned future will throw
+     * {@link ExecutionException}, holding the exception as its cause.
+     * </ul>
+     * Subsequent executions are suppressed.  Subsequent calls to
+     * {@link Future#isDone isDone()} on the returned future will
+     * return {@code true}.
+     *
+     * @param command      the task to execute
+     * @param initialDelay the time to delay first execution
+     * @param delay        the delay between the termination of one
+     *                     execution and the commencement of the next
+     * @param unit         the time unit of the initialDelay and delay parameters
+     * @return a ScheduledFuture representing pending completion of
+     * the series of repeated tasks.  The future's {@link
+     * Future#get() get()} method will never return normally,
+     * and will throw an exception upon task cancellation or
+     * abnormal termination of a task execution.
+     * @throws RejectedExecutionException if the task cannot be
+     *                                    scheduled for execution
+     * @throws NullPointerException       if command or unit is null
+     * @throws IllegalArgumentException   if delay less than or equal to zero
+     */
+    public ScheduledFuture<?> runWithFixedDelay(Runnable command,
+                                                long initialDelay,
+                                                long delay,
+                                                TimeUnit unit) {
+        if (pool != null) {
+            return pool.scheduleWithFixedDelay(command, initialDelay, delay, unit);
+        }
+        return null;
+    }
+
+    public void shutDown(Integer seconds) {
+        if (pool != null) {
+            try {
+                pool.awaitTermination(seconds, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            pool.shutdown();
+        }
+    }
+
+    //测试
+    public static void main(String[] args) {
+        System.out.println(DateUtil.getDateline() + "     main " + Thread.currentThread().getId());
+       final   Runnable task1 = new Runnable() {
+            @Override
+            public void run() {
+                System.out.println(DateUtil.getDateline() + "     1 " + Thread.currentThread().getId());
+            }
+        };
+
+        Runnable task2 = new Runnable() {
+            @Override
+            public void run() {
+                System.out.println(DateUtil.getDateline() + "     2 " + Thread.currentThread().getId());
+            }
+        };
+
+        Runnable task3 = new Runnable() {
+            @Override
+            public void run() {
+                System.out.println(DateUtil.getDateline() + "     3 " + Thread.currentThread().getId());
+            }
+        };
+
+        final ScheduledFuture<?> task1Result = TaskSchedule.getInstance().runAtFixedRate(task1, 0, 1000, TimeUnit.MILLISECONDS);
+        final ScheduledFuture<?> task2Result = TaskSchedule.getInstance().runAtFixedRate(task2, 0, 1000, TimeUnit.MILLISECONDS);
+        final ScheduledFuture<?> task3Result = TaskSchedule.getInstance().runAtFixedRate(task3, 0, 1000, TimeUnit.MILLISECONDS);
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(1000);
+                    task1Result.cancel(true);
+
+                    Thread.sleep(2000);
+                    task2Result.cancel(true);
+
+                    Thread.sleep(3000);
+                    task3Result.cancel(true);
+
+                    TaskSchedule.getInstance().runAtFixedRate(task1, 0, 1000, TimeUnit.MILLISECONDS);
+
+                    Thread.currentThread().interrupt();
+                } catch (Exception ex) {
+                }
+            }
+        }).start();
+
+        TaskSchedule.getInstance().shutDown(10);
+    }
+}

+ 39 - 7
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TcpClient.java

@@ -1,7 +1,12 @@
 package com.wdkl.ncs.android.middleware.tcp;
 
+import com.alibaba.fastjson.JSONObject;
 import com.wdkl.ncs.android.middleware.common.Constant;
 import com.wdkl.ncs.android.middleware.common.MessageEvent;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpCallback;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpSendModel;
+import com.wdkl.ncs.android.middleware.tcp.util.DateUtil;
 
 import org.greenrobot.eventbus.EventBus;
 
@@ -44,7 +49,7 @@ public class TcpClient {
     //tcp是否完成初始化
     private boolean inited = false;
 
-    private TcpCallBack tcpCallBack;
+    private TcpSendCallBack tcpSendCallBack;
     private boolean connecting = false;
 
 
@@ -152,11 +157,38 @@ public class TcpClient {
     }
 
     //发送消息,线程安全
-    public synchronized void sendMsg(String content){
+    public void sendTcp(TcpModel tcpModel, Boolean reSend, TcpCallback transaction) {
+        String tcpString = JSONObject.toJSONString(tcpModel);
+
+        if (reSend){
+            TcpSendModel model = new TcpSendModel();
+            model.setMsg(tcpString);
+            model.setTid(tcpModel.getTid());
+            model.setTs(DateUtil.getDateline());
+            //启动定时任务
+            tcpClientHandler.handleReSend(model);
+        }
+        sendMsg(tcpString, transaction);
+    }
+
+    //发送消息,线程安全
+    public synchronized void sendMsg(String content, TcpCallback transaction) {
+        //System.out.println("send tcp msg = [" + content + "]");
+        if (tcpClientHandler != null) {
+            tcpClientHandler.sendMsg(content, transaction);
+            if (tcpSendCallBack != null) {
+                tcpSendCallBack.tcpMsg(content);
+            }
+        }
+    }
+
+    //发送消息,线程安全
+    public synchronized void sendMsg(String content) {
+        //Log.e(TAG, "发送的数据 " + content);
         if (tcpClientHandler != null) {
             tcpClientHandler.sendMsg(content);
-            if (tcpCallBack != null) {
-                tcpCallBack.tcpMsg(content);
+            if (tcpSendCallBack != null) {
+                tcpSendCallBack.tcpMsg(content);
             }
         }
     }
@@ -166,11 +198,11 @@ public class TcpClient {
         return channel;
     }
 
-    public void setTcpCallback(TcpCallBack callback) {
-        tcpCallBack = callback;
+    public void setTcpSendCallback(TcpSendCallBack callback) {
+        tcpSendCallBack = callback;
     }
 
-    public interface TcpCallBack {
+    public interface TcpSendCallBack {
         void tcpMsg(String msg);
     }
 }

+ 121 - 47
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TcpClientHandler.java

@@ -2,14 +2,21 @@ package com.wdkl.ncs.android.middleware.tcp;
 
 import android.util.Log;
 
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.base.Strings;
 import com.wdkl.ncs.android.middleware.common.Constant;
 import com.wdkl.ncs.android.middleware.common.MessageEvent;
 import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel;
 import com.wdkl.ncs.android.middleware.tcp.channel.DeviceUtil;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpCallback;
 import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpSendModel;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+import com.wdkl.ncs.android.middleware.tcp.util.DateUtil;
 
 import org.greenrobot.eventbus.EventBus;
 
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 
 import io.netty.buffer.Unpooled;
@@ -34,6 +41,13 @@ public class TcpClientHandler extends SimpleChannelInboundHandler<String> {
     //是否连接成功
     private static Boolean connected = false;
 
+    private Integer stopSendSeconds = 20;
+
+    //存储回调 callback, key: tid
+    private static ConcurrentHashMap<String, TcpCallback> trantCache = new ConcurrentHashMap<>();
+    //key: tid。用于重发
+    static ConcurrentHashMap<String, TcpSendModel> tcpSendCache = new ConcurrentHashMap<>();
+
     //连接成功执行的方法
     @Override
     public void channelActive(ChannelHandlerContext ctx) throws Exception {
@@ -64,17 +78,66 @@ public class TcpClientHandler extends SimpleChannelInboundHandler<String> {
     @Override
     protected void channelRead0(ChannelHandlerContext ctx, String source) throws Exception {
         //System.out.println("channelRead0: read callback from server ===> " + source);
-        if(source.equals("1")){
-            Log.i(TAG,"收到服务器返回的心跳 "+source);
-        }else {
-            TcpModel tcpModel = TcpModel.getModelByJson(source);
-
-            TcpModel responseTcpModel = DeviceChannel.handleTcpReceived(tcpModel);
-            if (responseTcpModel!=null){
-                ctx.writeAndFlush(responseTcpModel.toJson());
+        try {
+            System.out.println("TcpClientHandler from server ===> " + source);
+            if (source.equals("1")) {
+                Log.e(TAG, "收到服务器返回的心跳" + "source " + source);
             } else {
-                ReferenceCountUtil.release(source);
+                TcpModel tcpModel = TcpModel.getModelByJson(source);
+
+                if (tcpModel != null) {
+                    //Log.i(TAG, "received type==" + tcpModel.getType() + ", action==" + tcpModel.getAction() + ", tid===" + tcpModel.getTid());
+                    switch (tcpModel.getType()) {
+                        case CALLBACK:
+                            //收到ack说明消息已到达服务器,可以取消重发,收到success说明消息正常处理
+                            if (tcpModel.getAction() == TcpAction.CallbackAction.ACK) {
+                                //服务器已收到tcp消息
+                                if (!Strings.isNullOrEmpty(tcpModel.getTid())) {
+                                    // 取消定时任务
+                                    tcpSendCache.remove(tcpModel.getTid());
+                                }
+                            } else if (tcpModel.getAction() == TcpAction.CallbackAction.SUCCESS) {
+                                //回调成功
+                                if (!Strings.isNullOrEmpty(tcpModel.getTid())) {
+                                    TcpCallback transaction = trantCache.get(tcpModel.getTid());
+                                    if (transaction != null) {
+                                        JSONObject jsonObject = new JSONObject();
+                                        jsonObject.put(TcpCallback.CALLBACK, TcpCallback.CALLBACK_SUCCESS);
+                                        transaction.onSuccess(jsonObject);
+                                        trantCache.remove(tcpModel.getTid());
+                                    }
+                                }
+                            } else if (tcpModel.getAction() == TcpAction.CallbackAction.FAILED) {
+                                //回调失败
+                                if (!Strings.isNullOrEmpty(tcpModel.getTid())) {
+                                    TcpCallback transaction = trantCache.get(tcpModel.getTid());
+                                    if (transaction != null) {
+                                        JSONObject jsonObject = new JSONObject();
+                                        if (tcpModel.getData() != null) {
+                                            jsonObject.put(TcpCallback.CALLBACK, tcpModel.getData().toString());
+                                        } else {
+                                            jsonObject.put(TcpCallback.CALLBACK, TcpCallback.CALLBACK_FAIL);
+                                        }
+                                        transaction.onFailed(jsonObject);
+                                        trantCache.remove(tcpModel.getTid());
+                                    }
+                                }
+                            }
+                            break;
+
+                        default:
+                            TcpModel responseTcpModel = DeviceChannel.handleTcpReceived(tcpModel);
+                            if (responseTcpModel != null) {
+                                ctx.writeAndFlush(responseTcpModel.toJson());
+                            } else {
+                                ReferenceCountUtil.release(source);
+                            }
+                    }
+                }
             }
+
+        } catch (Exception e) {
+            e.printStackTrace();
         }
 
     }
@@ -87,10 +150,8 @@ public class TcpClientHandler extends SimpleChannelInboundHandler<String> {
             if (event.state() == IdleState.WRITER_IDLE) { //超时未执行写操作,在指定的超时时间内未有写操作,要发送心跳包,告诉服务器连接还存活。服务器收到心跳立马回应,正常客户端收到后执行读操作,
                 ctx.writeAndFlush("0");                //这种情况下不会引发READER_IDLE事件。如果服务器因为网络或其他原因导致回应的心跳,客户端没有收到,在超过写超时时间2个周期后依然没有收到,
             } else if (event.state() == IdleState.READER_IDLE) { //认为服务不可用,主动断开连接
-//                ctx.channel().close();
                 Log.i(TAG, "TcpClientHandler ===> READER_IDLE");
                 ctx.close();
-//                ctx.close();
             } else if (event.state() == IdleState.ALL_IDLE) {
                 Log.i(TAG, "TcpClientHandler ===> ALL_IDLE");
 
@@ -112,50 +173,63 @@ public class TcpClientHandler extends SimpleChannelInboundHandler<String> {
     public void sendMsg(String msg){
         if (ctx==null){
             System.out.println("ctx is null");
-            /*try {
-                Thread.sleep(1000);
-                sendMsg(msg);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }*/
             return;
         }
-        System.out.println("wzlll: send tcp msg====" + msg);
+        Log.i(TAG, "sendMsg ==> " + msg);
         ctx.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
     }
 
-    //已经连接上,中途失去连接时的处理
-    private void reConnect(final ChannelHandlerContext ctx){
-        if (totalRetryTimes>100){
-            //todo: 存储数据库,并告警
-        }
-        if (connected && (TcpClient.getInstance().channel != null && TcpClient.getInstance().channel.isActive())){
-            return;
+    //使用带callback和tid方式发送消息
+    public synchronized boolean sendMsg(String msg, TcpCallback transaction){
+        if (ctx == null){
+            System.out.println("ctx is null");
+            return false;
         }
-        totalRetryTimes++;
-        System.out.println("TcpClientHandler 总计连接次数:"+totalRetryTimes);
-        retryTimes++;
-        if (retryTimes > 30) { //超时30次,10分钟后再试
-            retryTimes = 0;
-            ctx.channel().eventLoop().schedule(new Runnable() {
-                @Override
-                public void run() {
-                    System.out.println("TcpClientHandler 重新连接,第" + retryTimes + "次");
-                    TcpClient.getInstance().doConnect();
-                    reConnect(ctx);
-                }
-            }, 10*60, TimeUnit.SECONDS);
 
+        Log.i(TAG, "sendMsg ==> " + msg + ", transaction ==> " + transaction);
+        //ctx.writeAndFlush(msg);
+        ctx.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
+        if (transaction!=null) {
+            trantCache.put(transaction.getTid(), transaction);
+        }
+        return true;
+    }
 
-        }else {
-            ctx.channel().eventLoop().schedule(new Runnable() {
-                @Override
-                public void run() {
-                    System.out.println("TcpClientHandler 重新连接,第" + retryTimes + "次");
-                    TcpClient.getInstance().doConnect();
-                    reConnect(ctx);
+    //发送消息时缓存定时任务
+    public void handleReSend(final TcpSendModel model){
+        tcpSendCache.put(model.getTid(), model);
+        TaskSchedule.getInstance().scheduleTask("TCP_MESSAGE_REPEAT" + model.getTid(), new Runnable() {
+            @Override
+            public void run() {
+                TcpSendModel m = tcpSendCache.get(model.getTid());
+                if (m != null){
+                    System.out.println("定时任务:" + m.getTid() + "...........");
+                    long time = DateUtil.getDateline() - m.getTs();
+                    //比对时间
+                    if (time < stopSendSeconds){
+                        //因为是重发,所以不需要再缓存 callback
+                        System.out.println("重发消息");
+                        sendMsg(model.getMsg(), null);
+                    } else { //超时处理
+                        //关闭定时任务,提示掉线?
+                        System.out.println("取消定时任务,提示掉线");
+                        TaskSchedule.getInstance().cancelTask("TCP_MESSAGE_REPEAT" + model.getTid());
+                        //这里可以执行失败的方法
+                        TcpCallback transaction = trantCache.get(model.getTid());
+                        if (transaction!=null){
+                            JSONObject jsonObject = new JSONObject();
+                            jsonObject.put(TcpCallback.CALLBACK, TcpCallback.CALLBACK_FAIL);
+                            transaction.onFailed(jsonObject);
+                        }
+                        trantCache.remove(model.getTid());
+                        tcpSendCache.remove(model.getTid());
+                    }
+                } else {    //缓存不存在,取消定时
+                    System.out.println("缓存不存在,取消定时");
+                    TaskSchedule.getInstance().cancelTask("TCP_MESSAGE_REPEAT" + model.getTid());
                 }
-            }, retrySeconds * retryTimes, TimeUnit.SECONDS);
-        }
+            }
+        },5000L, 5000L);
+
     }
 }

+ 5 - 24
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/DeviceChannel.java

@@ -22,51 +22,32 @@ public class DeviceChannel {
     public static TcpModel handleTcpReceived(TcpModel tcpModel){
         TcpModel responseTcpModel = null;
 
-        Log.d("tcpModel", "received tcp model: " + tcpModel.toJson() + ", call state: " + Constant.CALL_STATE);
+        //Log.d("tcpModel", "received tcp model: " + tcpModel.toJson() + ", call state: " + Constant.CALL_STATE);
         switch (tcpModel.getType()){
-            case CALLBACK:
-                if(tcpModel.getAction()== TcpAction.CallbackAction.SUCCESS){
-                    //todo: 刷新呼叫记录列表
-                    EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
-                }else if(tcpModel.getAction()== TcpAction.CallbackAction.FAILED){
-                    EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
-                }
-                break;
             case VOICE:
                 if (tcpModel.getAction()==TcpAction.VoiceAction.CALL) { //语音呼入
-                    //todo: 判断当前是否通话
                     // 当前正在通话中或有新的来电或正在呼叫,直接返回该状态给服务器
                     if (Constant.CALL_STATE == Constant.CALL_CALLING || Constant.CALL_STATE == Constant.CALL_INCOMING || Constant.CALL_STATE == Constant.CALL_OUT_SUCCESS) {
                         InteractionVO interactionVO = new Gson().fromJson(tcpModel.getData().toString(), InteractionVO.class);
-                        responseTcpModel = VoiceUtil.voiceCalling(Constant.DEVICE_ID, tcpModel.getFromId(), interactionVO.getId());
+                        responseTcpModel = VoiceUtil.voiceCalling(tcpModel.getTid(), Constant.DEVICE_ID, tcpModel.getFromId(), interactionVO.getId());
                         return responseTcpModel;
                     } else{
-                        //todo: 通话界面展现,data中服务器传过来呼入名称;从接口重新获取左侧数据
                         // 当前待机状态,返回呼叫成功,并切换到呼叫界面
+                        responseTcpModel = VoiceUtil.voiceSuccess(tcpModel.getTid(), tcpModel.getToId(), tcpModel.getFromId());
                         Constant.CALL_STATE = Constant.CALL_INCOMING;
                         EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
+                        return responseTcpModel;
                     }
                 } else if (tcpModel.getAction()==TcpAction.VoiceAction.ACCEPT) {
                     //对方接听呼叫,但是当前转换盒在通话中或来电,直接回复通话中
                     if (Constant.CALL_STATE == Constant.CALL_CALLING || Constant.CALL_STATE == Constant.CALL_INCOMING) {
                         InteractionVO interactionVO = new Gson().fromJson(tcpModel.getData().toString(), InteractionVO.class);
-                        responseTcpModel = VoiceUtil.voiceCalling(Constant.DEVICE_ID, tcpModel.getFromId(), interactionVO.getId());
+                        responseTcpModel = VoiceUtil.voiceCalling(tcpModel.getTid(), Constant.DEVICE_ID, tcpModel.getFromId(), interactionVO.getId());
                         return responseTcpModel;
                     } else {
                         EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
                     }
                 } else {
-                    //tcpModel.getAction()==TcpAction.VoiceAction.ACCEPT
-                    //todo: 通话中界面更新;建立数据通话
-                    //tcpModel.getAction()==TcpAction.VoiceAction.REJECT
-                    //todo: 清掉呼出的TcpModel,通话中界面更新 --- 显示对方拒绝并停留3秒,结束至正常界面;更新左侧
-                    //tcpModel.getAction()==TcpAction.VoiceAction.CALLING
-                    //todo: 清掉呼出的TcpModel,通话中界面更新 --- 显示对方占线并停留3秒,结束至正常界面;更新左侧
-                    //tcpModel.getAction()==TcpAction.VoiceAction.FAILED
-                    //todo: 清掉呼出的TcpModel,通话中界面更新 --- 显示对方离线并停留3秒,结束至正常界面;更新左侧
-                    //tcpModel.getAction()==TcpAction.VoiceAction.HANDOFF
-                    //todo: 清掉呼出的TcpModel,通话中界面更新 --- 显示对方已挂断并停留3秒,结束至正常界面;更新左侧
-
                     //交由后续处理
                     EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
                 }

+ 55 - 3
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/EventUtil.java

@@ -1,5 +1,8 @@
 package com.wdkl.ncs.android.middleware.tcp.channel;
 
+import com.google.common.base.Strings;
+import com.wdkl.ncs.android.middleware.common.Constant;
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
 import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpType;
@@ -9,11 +12,11 @@ import java.util.Map;
 
 public class EventUtil {
     //分机发送按键事件无需toId
-    public static TcpModel eventKeyClick(Integer fromId, Integer keyId){
+    public static TcpModel eventKeyClick(Integer fromId, Integer toId, Integer keyId){
         Map map = new HashMap();
         map.put("id", keyId);
 
-        TcpModel tcpModel = new TcpModel();
+        TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.EVENT);
         tcpModel.setAction(TcpAction.EventAction.KEY_CLICK);
         tcpModel.setFromId(fromId);
@@ -22,7 +25,7 @@ public class EventUtil {
     }
 
     public static TcpModel eventResponse(Integer fromId, Integer toId, Integer eventId){
-        TcpModel tcpModel = new TcpModel();
+        TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.EVENT);
         tcpModel.setAction(TcpAction.EventAction.RESPONSE);
         tcpModel.setFromId(fromId);
@@ -30,4 +33,53 @@ public class EventUtil {
         tcpModel.setData(eventId);
         return tcpModel;
     }
+
+    public static TcpModel eventReceived(TcpModel model){
+        //回复收到需要使用同一个tid
+        TcpModel tcpModel = new TcpModel();
+        if (!Strings.isNullOrEmpty(model.getTid())) {
+            tcpModel.setTid(model.getTid());
+        }
+        tcpModel.setType(TcpType.EVENT);
+        tcpModel.setAction(TcpAction.EventAction.RECEIVED);
+        tcpModel.setFromId(Constant.DEVICE_ID);
+        tcpModel.setToId(model.getFromId());
+        tcpModel.setData(model.getData());
+        return tcpModel;
+    }
+
+
+    public static TcpModel cancelEvent(InteractionVO interactionVO){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.EVENT);
+        tcpModel.setAction(TcpAction.EventAction.CANCEL);
+        tcpModel.setFromId(interactionVO.getFromDeviceId());
+        //tcpModel.setToId(model.getToId());
+        tcpModel.setData(interactionVO);
+        return tcpModel;
+    }
+
+    public static TcpModel eventCompleted(InteractionVO interactionVO){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.EVENT);
+        tcpModel.setAction(TcpAction.EventAction.COMPLETED);
+        tcpModel.setFromId(interactionVO.getFromDeviceId());
+        tcpModel.setToId(interactionVO.getToDeviceId());
+        tcpModel.setData(interactionVO);
+        return tcpModel;
+    }
+
+    public static TcpModel eventCancelReceived(TcpModel model) {
+        //回复收到需要使用同一个tid
+        TcpModel tcpModel = new TcpModel();
+        if (!Strings.isNullOrEmpty(model.getTid())) {
+            tcpModel.setTid(model.getTid());
+        }
+        tcpModel.setType(TcpType.EVENT);
+        tcpModel.setAction(TcpAction.EventAction.CANCEL_CONFIRM);
+        tcpModel.setFromId(Constant.DEVICE_ID);
+        tcpModel.setToId(model.getFromId());
+        tcpModel.setData(model.getData());
+        return tcpModel;
+    }
 }

+ 31 - 6
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/ImUtil.java

@@ -1,26 +1,51 @@
 package com.wdkl.ncs.android.middleware.tcp.channel;
 
+import com.google.common.base.Strings;
+import com.wdkl.ncs.android.middleware.common.Constant;
 import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpType;
 
+import java.util.HashMap;
+import java.util.Map;
+
 public class ImUtil {
-    public static TcpModel imMsg(Integer fromId, String msg){
-        TcpModel tcpModel = new TcpModel();
+    //tid1,fromId,data:{roleType:NURSE,filePath:pathUrl}
+    //带toId指定了目标设备的不用传roleType
+    public static TcpModel imMsg(Integer fromId, Integer toId, String filePath){
+        Map map = new HashMap();
+        map.put("filePath", filePath);
+
+        TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.IM);
         tcpModel.setAction(TcpAction.IMAction.MSG);
         tcpModel.setFromId(fromId);
-        tcpModel.setData(msg);
+        tcpModel.setToId(toId);
+        tcpModel.setData(map);
         return tcpModel;
     }
 
-    public static TcpModel imRead(Integer fromId, Integer toId, Integer msg){
-        TcpModel tcpModel = new TcpModel();
+    public static TcpModel imRead(Integer fromId, Integer toId, Integer msgId){
+        TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.IM);
         tcpModel.setAction(TcpAction.IMAction.MSG_READ);
         tcpModel.setFromId(fromId);
         tcpModel.setToId(toId);
-        tcpModel.setData(msg);
+        tcpModel.setData(msgId);
+        return tcpModel;
+    }
+
+    public static TcpModel imReceived(TcpModel model){
+        //回复收到需要使用同一个tid
+        TcpModel tcpModel = new TcpModel();
+        if (!Strings.isNullOrEmpty(model.getTid())) {
+            tcpModel.setTid(model.getTid());
+        }
+        tcpModel.setType(TcpType.IM);
+        tcpModel.setAction(TcpAction.IMAction.RECEIVED);
+        tcpModel.setFromId(Constant.DEVICE_ID);
+        tcpModel.setToId(model.getFromId());
+        tcpModel.setData(model.getData());
         return tcpModel;
     }
 }

+ 2 - 2
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/OtherUtil.java

@@ -10,7 +10,7 @@ public class OtherUtil {
 
     //分机SOS呼叫,不需要传toId
     public static TcpModel SOSCall(Integer fromId, int type){
-        TcpModel tcpModel = new TcpModel();
+        TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.SOS);
         tcpModel.setAction(TcpAction.SOSAction.CALL);
 
@@ -43,7 +43,7 @@ public class OtherUtil {
     }
 
     public static TcpModel SOSCancel(Integer fromId, Integer toId){
-        TcpModel tcpModel = new TcpModel();
+        TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.SOS);
         tcpModel.setAction(TcpAction.SOSAction.CANCEL);
         tcpModel.setFromId(fromId);

+ 51 - 21
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/VoiceUtil.java

@@ -9,9 +9,23 @@ import java.util.HashMap;
 import java.util.Map;
 
 public class VoiceUtil {
+    //tid1,fromId,data:{roleType:NURSE}
+    //带toId指定了目标设备的不用传roleType,否则需要
+    public static TcpModel voiceCall(Integer fromId, String roleType){
+        Map map = new HashMap();
+        map.put("roleType", roleType);
+
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.VOICE);
+        tcpModel.setAction(TcpAction.VoiceAction.CALL);
+        tcpModel.setFromId(fromId);
+        tcpModel.setData(map);
+        return tcpModel;
+    }
+
     //分机呼叫,不需要传toId
     public static TcpModel voiceCall(Integer fromId){
-        TcpModel tcpModel = new TcpModel();
+        TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.VOICE);
         tcpModel.setAction(TcpAction.VoiceAction.CALL);
         tcpModel.setFromId(fromId);
@@ -19,7 +33,7 @@ public class VoiceUtil {
     }
 
     public static TcpModel voiceCall2(Integer fromId, String uart){
-        TcpModel tcpModel = new TcpModel();
+        TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.VOICE);
         tcpModel.setAction(TcpAction.VoiceAction.CALL);
         tcpModel.setFromId(fromId);
@@ -27,8 +41,8 @@ public class VoiceUtil {
         return tcpModel;
     }
 
-    public static TcpModel voiceAccept(Integer fromId, Integer toId, Integer interactionId){
-        TcpModel tcpModel = new TcpModel();
+    public static TcpModel voiceAccept(String tid, Integer fromId, Integer toId, Integer interactionId){
+        TcpModel tcpModel = new TcpModel(tid);
         tcpModel.setType(TcpType.VOICE);
         tcpModel.setAction(TcpAction.VoiceAction.ACCEPT);
         tcpModel.setFromId(fromId);
@@ -39,8 +53,8 @@ public class VoiceUtil {
         return tcpModel;
     }
 
-    public static TcpModel voiceReject(Integer fromId, Integer toId, Integer interactionId){
-        TcpModel tcpModel = new TcpModel();
+    public static TcpModel voiceReject(String tid, Integer fromId, Integer toId, Integer interactionId){
+        TcpModel tcpModel = new TcpModel(tid);
         tcpModel.setType(TcpType.VOICE);
         tcpModel.setAction(TcpAction.VoiceAction.REJECT);
         tcpModel.setFromId(fromId);
@@ -51,8 +65,8 @@ public class VoiceUtil {
         return tcpModel;
     }
 
-    public static TcpModel voiceCalling(Integer fromId, Integer toId, Integer interactionId){
-        TcpModel tcpModel = new TcpModel();
+    public static TcpModel voiceCalling(String tid, Integer fromId, Integer toId, Integer interactionId){
+        TcpModel tcpModel = new TcpModel(tid);
         tcpModel.setType(TcpType.VOICE);
         tcpModel.setAction(TcpAction.VoiceAction.CALLING);
         tcpModel.setFromId(fromId);
@@ -64,17 +78,28 @@ public class VoiceUtil {
     }
 
     //分机自己取消呼叫,没有toId
-    public static TcpModel voiceCancel(Integer fromId){
-        TcpModel tcpModel = new TcpModel();
+    public static TcpModel voiceCancel(String tid, Integer fromId, Integer interactionId){
+        TcpModel tcpModel = new TcpModel(tid);
         tcpModel.setType(TcpType.VOICE);
         tcpModel.setAction(TcpAction.VoiceAction.CANCEL);
         tcpModel.setFromId(fromId);
+        if (interactionId != -1) {
+            tcpModel.setData(interactionId);
+        }
         return tcpModel;
     }
 
-    public static TcpModel voiceHandoff(Integer fromId, Integer toId, Integer interactionId){
+    public static TcpModel voiceCancel2(Integer fromId){
         TcpModel tcpModel = new TcpModel();
         tcpModel.setType(TcpType.VOICE);
+        tcpModel.setAction(TcpAction.VoiceAction.CANCEL);
+        tcpModel.setFromId(fromId);
+        return tcpModel;
+    }
+
+    public static TcpModel voiceHandoff(String tid, Integer fromId, Integer toId, Integer interactionId){
+        TcpModel tcpModel = new TcpModel(tid);
+        tcpModel.setType(TcpType.VOICE);
         tcpModel.setAction(TcpAction.VoiceAction.HANDOFF);
         tcpModel.setFromId(fromId);
         if (toId != -1) {
@@ -86,8 +111,8 @@ public class VoiceUtil {
         return tcpModel;
     }
 
-    public static TcpModel voiceSuccess(Integer fromId, Integer toId){
-        TcpModel tcpModel = new TcpModel();
+    public static TcpModel voiceSuccess(String tid, Integer fromId, Integer toId){
+        TcpModel tcpModel = new TcpModel(tid);
         tcpModel.setType(TcpType.VOICE);
         tcpModel.setAction(TcpAction.VoiceAction.SUCCESS);
         tcpModel.setFromId(fromId);
@@ -99,7 +124,7 @@ public class VoiceUtil {
      ************************* 发送呼叫相关TCP *********************************
     ***************************************************************************/
     //语音呼叫
-    public static void startAudioCall(Integer fromId) {
+    /*public static void startAudioCall(Integer fromId) {
         TcpModel tcpModel = VoiceUtil.voiceCall(fromId);
         TcpClient.getInstance().sendMsg(tcpModel.toJson());
     }
@@ -114,23 +139,28 @@ public class VoiceUtil {
     public static void cancelAudioCall(Integer fromId) {
         TcpModel tcpModel = VoiceUtil.voiceCancel(fromId);
         TcpClient.getInstance().sendMsg(tcpModel.toJson());
-    }
+    }*/
 
     //接受通话
-    public static void acceptAudioCall(Integer fromId, Integer toId, Integer interactionId) {
-        TcpModel tcpModel = VoiceUtil.voiceAccept(fromId, toId, interactionId);
+    public static void acceptAudioCall(String tid, Integer fromId, Integer toId, Integer interactionId) {
+        TcpModel tcpModel = VoiceUtil.voiceAccept(tid, fromId, toId, interactionId);
         TcpClient.getInstance().sendMsg(tcpModel.toJson());
     }
 
     //挂断通话
-    public static void handoffAudioCall(Integer fromId, Integer toId, Integer interactionId) {
-        TcpModel tcpModel = VoiceUtil.voiceHandoff(fromId, toId, interactionId);
+    public static void handoffAudioCall(String tid, Integer fromId, Integer toId, Integer interactionId) {
+        TcpModel tcpModel = VoiceUtil.voiceHandoff(tid, fromId, toId, interactionId);
         TcpClient.getInstance().sendMsg(tcpModel.toJson());
     }
 
     //拒绝通话
-    public static void rejectAudioCall(Integer fromId, Integer toId, Integer interactionId) {
-        TcpModel tcpModel = VoiceUtil.voiceReject(fromId, toId, interactionId);
+    public static void rejectAudioCall(String tid, Integer fromId, Integer toId, Integer interactionId) {
+        TcpModel tcpModel = VoiceUtil.voiceReject(tid, fromId, toId, interactionId);
+        TcpClient.getInstance().sendMsg(tcpModel.toJson());
+    }
+
+    public static void cancelAudioCall2(Integer fromId) {
+        TcpModel tcpModel = VoiceUtil.voiceCancel2(fromId);
         TcpClient.getInstance().sendMsg(tcpModel.toJson());
     }
 }

+ 43 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/dto/TcpCallback.java

@@ -0,0 +1,43 @@
+package com.wdkl.ncs.android.middleware.tcp.dto;
+
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * 描述
+ *
+ * @author allen
+ * 2022-11-01 12:10
+ */
+public class TcpCallback {
+    public static final String CALLBACK = "CALLBACK";
+    public static final String CALLBACK_SUCCESS = "send success";
+    public static final String CALLBACK_FAIL = "send fail";
+
+    private String tid;
+    private String mac;
+
+    public TcpCallback(String tid){
+        this.tid = tid;
+    }
+
+    public TcpCallback(String tid, String mac){
+        this.tid = tid;
+        this.mac = mac;
+    }
+
+    public void onSuccess(JSONObject jsonObject){
+
+    }
+
+    public void onFailed(JSONObject jsonObject){
+
+    }
+
+    public String getTid() {
+        return tid;
+    }
+
+    public String getMac() {
+        return mac;
+    }
+}

+ 31 - 44
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/dto/TcpModel.java

@@ -3,15 +3,12 @@ package com.wdkl.ncs.android.middleware.tcp.dto;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.google.common.base.Strings;
-import com.wdkl.ncs.android.middleware.model.dos.DeviceDO;
-import com.wdkl.ncs.android.middleware.model.dos.FrameDO;
-import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpType;
 
+import org.bson.types.ObjectId;
+
 import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * tcp传输对象
@@ -20,6 +17,9 @@ import java.util.Map;
  * 2021-03-30 11:49
  */
 public class TcpModel implements Serializable {
+
+    private String tid;
+
     /**
      * TCP传输对象的类型
      */
@@ -43,6 +43,17 @@ public class TcpModel implements Serializable {
      */
     private Object data;
 
+    public TcpModel() {
+    }
+
+    public TcpModel(String tid) {
+        if (Strings.isNullOrEmpty(tid)) {
+            this.tid = new ObjectId().toString();
+        } else {
+            this.tid = tid;
+        }
+    }
+
     public TcpType getType() {
         return type;
     }
@@ -75,7 +86,7 @@ public class TcpModel implements Serializable {
         this.toId = toId;
     }
 
-    public Object getData() {
+    public Object  getData() {
         return data;
     }
 
@@ -94,6 +105,7 @@ public class TcpModel implements Serializable {
         TcpModel tcpModel = new TcpModel();
 
         JSONObject jsonObject = JSON.parseObject(tcpModelJsonString);
+        String tid = jsonObject.getString("tid");
         String type = jsonObject.getString("type");
         String action = jsonObject.getString("action");
         Integer fromId = jsonObject.getInteger("fromId");
@@ -133,11 +145,17 @@ public class TcpModel implements Serializable {
             case SOS:
                 tcpAction = TcpAction.SOSAction.fromString(action);
                 break;
+            case ENTRACEGUARD:
+                tcpAction = TcpAction.EntraceGuardAction.fromString(action);
+                break;
+            case REINFORCE:
+                tcpAction = TcpAction.ReinforceAction.fromString(action);
+                break;
             case SIDE:
-                tcpAction=TcpAction.SideAction.fromString(action);
+                tcpAction = TcpAction.SideAction.fromString(action);
                 break;
         }
-
+        tcpModel.setTid(tid);
         tcpModel.setType(tcpType);
         tcpModel.setAction(tcpAction);
         tcpModel.setToId(toId);
@@ -176,42 +194,11 @@ public class TcpModel implements Serializable {
         return tcpModel.toJson();
     }
 
-    /**
-     * 示例
-     * @param args
-     */
-    public static void main(String[] args) {
-        //创建对象示例
-        TcpModel tcpModel = new TcpModel();
-        tcpModel.type = TcpType.VOICE;
-        tcpModel.action = TcpAction.VoiceAction.CALL;
-
-        Map map = new HashMap();
-
-        DeviceDO deviceDO = new DeviceDO();
-        deviceDO.setCode("123");
-        deviceDO.setEthMac("abcd");
-        map.put("device",deviceDO);
-
-        FrameDO frameDO = new FrameDO();
-        frameDO.setName("ok");
-        map.put("frame",frameDO);
-
-        tcpModel.setData(map);
-
-        System.out.println("enum show === " + tcpModel.action.getDescription() + " -- " + tcpModel.action.getName());
-
-        //要传输的json字符串
-        String jsonStr = tcpModel.toJson();
-        System.out.println("json show === " + jsonStr);
-
-        //获利传输的JSON字符串,转换为对象
-        TcpModel dTcpModel = TcpModel.getModelByJson(jsonStr);
-        //数据反转
-        String dataString = dTcpModel.getData().toString();
-        JSONObject dataJSON = JSON.parseObject(dataString);
-        DeviceDO dDeviceDO = JSON.parseObject(dataJSON.getString("device"), DeviceDO.class);
+    public String getTid() {
+        return tid;
+    }
 
-        System.out.println("json de show === " + dTcpModel.action + " -- " + dDeviceDO.getEthMac());
+    public void setTid(String tid) {
+        this.tid = tid;
     }
 }

+ 49 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/dto/TcpSendModel.java

@@ -0,0 +1,49 @@
+package com.wdkl.ncs.android.middleware.tcp.dto;
+
+import java.io.Serializable;
+
+/**
+ * 描述
+ *
+ * @author allen
+ * 2022-11-28 11:23
+ */
+public class TcpSendModel implements Serializable {
+    private String tid;
+    private String msg;
+    //第一次发出的时间戳,比对一定时间后清除缓存
+    private Long ts;
+
+    @Override
+    public String toString() {
+        return "TcpSendModel{" +
+                "tid='" + tid + '\'' +
+                ", msg='" + msg + '\'' +
+                ", ts=" + ts +
+                '}';
+    }
+
+    public String getTid() {
+        return tid;
+    }
+
+    public void setTid(String tid) {
+        this.tid = tid;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public Long getTs() {
+        return ts;
+    }
+
+    public void setTs(Long ts) {
+        this.ts = ts;
+    }
+}

+ 106 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/RoleTypeEnum.java

@@ -0,0 +1,106 @@
+package com.wdkl.ncs.android.middleware.tcp.enums;
+
+import com.alibaba.fastjson.JSONObject;
+import com.wdkl.ncs.android.middleware.model.dos.RoleDO;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author
+ * @title: Vothin
+ * @projectName nc
+ * @date 2021/5/1910:29
+ */
+public enum RoleTypeEnum {
+    ADMINISTRATORS(1, "管理员"),
+    DOCTOR(2, "医生"),
+    NURSE(3, "护士"),
+    WORKER(4, "护工"),
+    NURSE_SUPERVISOR(5, "护士主管"),
+    DIETITIAN(6, "营养师"),
+    HOUSEKEEPER(7, "管家"),
+    NURSE_HEAD(8, "护士组长"),
+    LIFE_ASSISTANT(9, "生活助理");
+    private final int value;
+    private final String typeName;
+
+
+    RoleTypeEnum(int value, String typeName) {
+        this.value = value;
+        this.typeName = typeName;
+    }
+
+    public int value() {
+        return this.value;
+    }
+
+    public String typeName() {
+        return this.typeName;
+    }
+
+    /**
+     * 通过typeName 转换成枚举
+     *
+     * @param typeName
+     * @return
+     */
+    public static RoleTypeEnum parse(String typeName) {
+        RoleTypeEnum[] values = RoleTypeEnum.values();
+        for (RoleTypeEnum value : values) {
+            if (value.typeName().equals(typeName)) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 通过value值转换成枚举
+     *
+     * @param value
+     * @return
+     */
+    public static RoleTypeEnum parse(int value) {
+        RoleTypeEnum[] values = RoleTypeEnum.values();
+        for (RoleTypeEnum roleTypeEnum : values) {
+            if (roleTypeEnum.value() == value) {
+                return roleTypeEnum;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * 把角色DO转换为 RoleTypeEnum
+     * @param roleDO
+     * @return
+     */
+
+    public static RoleTypeEnum parse(RoleDO roleDO) {
+        RoleTypeEnum[] values = RoleTypeEnum.values();
+        for (RoleTypeEnum roleTypeEnum : values) {
+            if (roleTypeEnum.name().equals(roleDO.getRoleType())) {
+                return roleTypeEnum;
+            }
+        }
+        return null;
+    }
+
+
+    public static List getNames(){
+        RoleTypeEnum[] values = RoleTypeEnum.values();
+
+        return Arrays.stream(values).map(p-> {
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("key",p.name());
+            jsonObject.put("typeName",p.typeName());
+            return jsonObject;
+        }).collect(Collectors.toList());
+    }
+
+
+
+}

+ 17 - 5
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/TcpAction.java

@@ -14,6 +14,7 @@ public interface TcpAction {
     String getDescription();
 
     enum CallbackAction implements TcpAction {
+        ACK("反馈"),
         SUCCESS("同步"),
         FAILED("失败"),
         NO_MATCH("没有匹配");
@@ -88,7 +89,7 @@ public interface TcpAction {
         HANDOFF("挂断"),
         CANCEL("取消"),
         PCALLING("已经通话中"),
-        VOICE_OFF("通话被接听"),
+        VOICE_OFF("通话被其他设备处理"),
         RS485CALL("485界面发起呼叫"),
         RS485CANCEL("485界面呼叫取消"),
         RS485CANCEL_BY_DOOR("485门口机取消房间内的呼叫"),
@@ -104,6 +105,7 @@ public interface TcpAction {
         GAINED("呼叫被应答"),
         HCALL("手柄呼叫"),
         HRESPONSE("响应手柄"),
+        HRESPONSED("响应过手柄了"),
         NO_MATCH("没有匹配");
 
 
@@ -174,8 +176,9 @@ public interface TcpAction {
 
 
     enum SOSAction implements TcpAction {
-        CALL("紧急呼叫"),
-        CANCEL("取消"),
+        CALL("紧急呼叫"),   //1
+        CANCEL("取消"),   //1
+        CANCELED("已取消"),    //1
         TRANSFER("转接"),
         ALARM_TEST("测试报警"),
         ALARM_INTRUSION("侵入报警"),
@@ -196,6 +199,12 @@ public interface TcpAction {
         ALARM_VITAL("体征报警"),
         AlARM_BUTTON("紧急按钮"),
         ALARM_FALL_RADAR("跌到报警"),
+        ALARM_INFUSION_RADAR("输液报警"),
+        ROOM_CALL("房间紧急呼叫"),    //1
+        OTHER_CALL("其他紧急呼叫"),   //1
+        RECEIVED("客户端收到确认"),
+        // 当设备在科室级时用这个PART_USER_CALL,比如:用户手环
+        PART_USER_CALL("用户紧急呼叫"),   //
         NO_MATCH("没有匹配");
 
         private final String description;
@@ -320,6 +329,8 @@ public interface TcpAction {
 
     enum DeviceAction implements TcpAction {
         RESTART("重启"),
+        REBOOT("重启机器"),
+        OPEN_DEBUG("打开网络调试"),
         CONNECT("连接"),
         APP_UPDATE("APP更新"),
         DEVICE_REFRESH("设备刷新"),
@@ -355,13 +366,14 @@ public interface TcpAction {
 
     enum EventAction implements TcpAction {
         KEY_CLICK("按键事件"),
-        RESPONSE("已响应"),
+        RESPONSE("响应"),
+        RESPONSED("已响应"),
         RECEIVED("接收端收到确认"),
         CANCEL("取消"),
         CANCEL_CONFIRM("接收端确认收到取消"),
+        NO_TARGET("没有目标设备"),
         COMPLETED("完成"),
         NO_MATCH("没有匹配");
-
         private final String description;
         EventAction(String description){
             this.description = description;

+ 483 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/util/DateUtil.java

@@ -0,0 +1,483 @@
+package com.wdkl.ncs.android.middleware.tcp.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * 日期相关的操作
+ *
+ * @author Dawei
+ */
+
+@SuppressWarnings({"AlibabaCollectionInitShouldAssignCapacity", "AlibabaUndefineMagicConstant"})
+public class DateUtil {
+    /**
+     * 一天的秒数
+     */
+    public static final long ONE_DAY = 86400;
+
+    /**
+     * 获取当前格式化后的时间
+     * @return
+     */
+    public static String formatNow() {
+        return DateUtil.toString(new Date(), "yyyy-MM-dd HH:mm:ss");
+    }
+    /**
+     * 当天的开始时间
+     *
+     * @return
+     */
+    public static long startOfTodDay() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        Date date = calendar.getTime();
+        return date.getTime() / 1000;
+    }
+
+    /**
+     * 将 Sun Apr 15 04:12:39 CST 2018 格式的时间转换为时间戳
+     *
+     * @param dateFormat Sun Apr 15 04:12:39 CST 2018 格式的时间
+     * @return 时间戳
+     */
+    public static long getFormatDate(String dateFormat) {
+        try {
+            SimpleDateFormat sdf1 = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
+
+            return sdf1.parse(dateFormat).getTime() / 1000;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return 0;
+    }
+
+    public static long startOfDay(String s) {
+        Date date = null;
+        Calendar calendar= Calendar.getInstance();
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        try {
+            date = simpleDateFormat.parse(s);
+            calendar.setTime(date);
+        } catch (Exception e) {
+        }
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        long start = calendar.getTime().getTime()/1000;
+        return start;
+    }
+
+    public static long endOfDay(String s) {
+        Date date = null;
+        Calendar calendar= Calendar.getInstance();
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        try {
+            date = simpleDateFormat.parse(s);
+            calendar.setTime(date);
+        } catch (Exception e) {
+        }
+        calendar.set(Calendar.HOUR_OF_DAY, 23);
+        calendar.set(Calendar.MINUTE, 59);
+        calendar.set(Calendar.SECOND, 59);
+        calendar.set(Calendar.MILLISECOND, 999);
+        long start = calendar.getTime().getTime()/1000;
+        return start;
+    }
+
+
+
+    /**
+     * 当天的结束时间
+     *
+     * @return
+     */
+    public static long endOfTodDay() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 23);
+        calendar.set(Calendar.MINUTE, 59);
+        calendar.set(Calendar.SECOND, 59);
+        calendar.set(Calendar.MILLISECOND, 999);
+        Date date = calendar.getTime();
+        return date.getTime() / 1000;
+    }
+
+    /**
+     * 昨天的开始时间
+     *
+     * @return
+     */
+    public static long startOfyesterday() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.add(Calendar.DATE, -1);
+        calendar.set(Calendar.MILLISECOND, 0);
+        Date date = calendar.getTime();
+        return date.getTime() / 1000;
+    }
+
+    /**
+     * 昨天的结束时间
+     *
+     * @return
+     */
+    public static long endOfyesterday() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 23);
+        calendar.set(Calendar.MINUTE, 59);
+        calendar.set(Calendar.SECOND, 59);
+        calendar.set(Calendar.MILLISECOND, 999);
+        calendar.add(Calendar.DATE, -1);
+        Date date = calendar.getTime();
+        return date.getTime() / 1000;
+    }
+
+    /**
+     * 某天的开始时间
+     *
+     * @param dayUntilNow 距今多少天以前
+     * @return 时间戳
+     */
+    public static long startOfSomeDay(int dayUntilNow) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        calendar.add(Calendar.DATE, -dayUntilNow);
+        Date date = calendar.getTime();
+        return date.getTime() / 1000;
+    }
+
+    /**
+     * 某天的开始时间
+     *
+     * @param dayUntilNow 距今多少天以后
+     * @return 时间戳
+     */
+    public static long endOfSomeDay(int dayUntilNow) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        calendar.add(Calendar.DATE, +dayUntilNow);
+        Date date = calendar.getTime();
+        return date.getTime() / 1000;
+    }
+
+    /**
+     * 某天的年月日
+     *
+     * @param dayUntilNow 距今多少天以前
+     * @return 年月日map key为 year month day
+     */
+    public static Map<String, Object> getYearMonthAndDay(int dayUntilNow) {
+
+        Map<String, Object> map = new HashMap<String, Object>(3);
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        calendar.add(Calendar.DATE, -dayUntilNow);
+        map.put("year", calendar.get(Calendar.YEAR));
+        map.put("month", calendar.get(Calendar.MONTH) + 1);
+        map.put("day", calendar.get(Calendar.DAY_OF_MONTH));
+        return map;
+    }
+
+    /**
+     * 将一个字符串转换成日期格式
+     *
+     * @param date
+     * @param pattern
+     * @return
+     */
+    public static Date toDate(String date, String pattern) {
+        if ("".equals("" + date)) {
+            return null;
+        }
+        if (pattern == null) {
+            pattern = "yyyy-MM-dd";
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+        Date newDate = new Date();
+        try {
+            newDate = sdf.parse(date);
+
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+        return newDate;
+    }
+
+    /**
+     * 获取上个月的开始结束时间
+     *
+     * @return
+     */
+    public static Long[] getLastMonth() {
+        // 取得系统当前时间
+        Calendar cal = Calendar.getInstance();
+        int year = cal.get(Calendar.YEAR);
+        int month = cal.get(Calendar.MONTH) + 1;
+
+        // 取得系统当前时间所在月第一天时间对象
+        cal.set(Calendar.DAY_OF_MONTH, 1);
+
+        // 日期减一,取得上月最后一天时间对象
+        cal.add(Calendar.DAY_OF_MONTH, -1);
+
+        // 输出上月最后一天日期
+        int day = cal.get(Calendar.DAY_OF_MONTH);
+
+        String months = "";
+        String days = "";
+
+        if (month > 1) {
+            month--;
+        } else {
+            year--;
+            month = 12;
+        }
+        if (!(String.valueOf(month).length() > 1)) {
+            months = "0" + month;
+        } else {
+            months = String.valueOf(month);
+        }
+        if (!(String.valueOf(day).length() > 1)) {
+            days = "0" + day;
+        } else {
+            days = String.valueOf(day);
+        }
+        String firstDay = "" + year + "-" + months + "-01";
+        String lastDay = "" + year + "-" + months + "-" + days + " 23:59:59";
+
+        Long[] lastMonth = new Long[2];
+        lastMonth[0] = DateUtil.getDateline(firstDay);
+        lastMonth[1] = DateUtil.getDateline(lastDay, "yyyy-MM-dd HH:mm:ss");
+
+        return lastMonth;
+    }
+
+    public static Long[] getMonthStartEnd(String year, String month){
+        Integer monthInt = Integer.parseInt(month);
+        if (monthInt<0 || monthInt>12){
+            return null;
+        }
+
+        Date date = null;
+        Calendar cal= Calendar.getInstance();
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM");
+        try {
+            date = simpleDateFormat.parse(year+'-'+month);
+            cal.setTime(date);
+        } catch (Exception e) {
+        }
+
+        // 输出下月第一天日期
+        int notMonth = monthInt + 1;
+        // 取得系统当前时间所在月第一天时间对象
+        cal.set(Calendar.DAY_OF_MONTH, 1);
+
+        // 日期减一,取得上月最后一天时间对象
+        cal.add(Calendar.DAY_OF_MONTH, -1);
+        cal.set(Calendar.MILLISECOND, 0);
+
+        //long start = cal.getTime().getTime()/1000;
+
+        String months = "";
+        String nextMonths = "";
+
+        if (!(String.valueOf(monthInt).length() > 1)) {
+            months = "0" + monthInt;
+        } else {
+            months = String.valueOf(monthInt);
+        }
+        if (!(String.valueOf(notMonth).length() > 1)) {
+            nextMonths = "0" + notMonth;
+        } else {
+            nextMonths = String.valueOf(notMonth);
+        }
+        String firstDay = "" + year + "-" + months + "-01 00:00:00";
+        String lastDay = "" + year + "-" + nextMonths + "-01 00:00:00";
+        Long[] currentMonth = new Long[2];
+        currentMonth[0] = DateUtil.getDateline(firstDay,"yyyy-MM-dd HH:mm:ss");
+        currentMonth[1] = DateUtil.getDateline(lastDay,"yyyy-MM-dd HH:mm:ss");
+
+        return currentMonth;
+    }
+
+    /**
+     * 获取当月的开始结束时间
+     *
+     * @return
+     */
+    public static Long[] getCurrentMonth() {
+        // 取得系统当前时间
+        Calendar cal = Calendar.getInstance();
+        int year = cal.get(Calendar.YEAR);
+        int month = cal.get(Calendar.MONTH) + 1;
+        // 输出下月第一天日期
+        int notMonth = cal.get(Calendar.MONTH) + 2;
+        // 取得系统当前时间所在月第一天时间对象
+        cal.set(Calendar.DAY_OF_MONTH, 1);
+
+        // 日期减一,取得上月最后一天时间对象
+        cal.add(Calendar.DAY_OF_MONTH, -1);
+
+        String months = "";
+        String nextMonths = "";
+
+        if (!(String.valueOf(month).length() > 1)) {
+            months = "0" + month;
+        } else {
+            months = String.valueOf(month);
+        }
+        if (!(String.valueOf(notMonth).length() > 1)) {
+            nextMonths = "0" + notMonth;
+        } else {
+            nextMonths = String.valueOf(notMonth);
+        }
+        String firstDay = "" + year + "-" + months + "-01";
+        String lastDay = "" + year + "-" + nextMonths + "-01";
+        Long[] currentMonth = new Long[2];
+        currentMonth[0] = DateUtil.getDateline(firstDay);
+        currentMonth[1] = DateUtil.getDateline(lastDay);
+
+        return currentMonth;
+    }
+
+    /**
+     * 获取某年开始结束时间
+     *
+     * @return
+     */
+    public static Long[] getYearTime(Integer year) {
+
+
+        Calendar firstCal = Calendar.getInstance();
+        firstCal.set(Calendar.YEAR, year - 1);
+        firstCal.set(Calendar.MONTH, Calendar.DECEMBER);
+        firstCal.set(Calendar.DATE, 31);
+
+        Calendar lastCal = Calendar.getInstance();
+        lastCal.set(Calendar.YEAR, year);
+        lastCal.set(Calendar.MONTH, Calendar.DECEMBER);
+        lastCal.set(Calendar.DATE, 31);
+
+        Long[] yearTime = new Long[2];
+        yearTime[0] = firstCal.getTime().getTime() / 1000;
+        yearTime[1] = lastCal.getTime().getTime() / 1000;
+
+        return yearTime;
+    }
+
+    /**
+     * 把日期转换成字符串型
+     *
+     * @param date
+     * @param pattern
+     * @return
+     */
+    public static String toString(Date date, String pattern) {
+        if (date == null) {
+            return "";
+        }
+        if (pattern == null) {
+            pattern = "yyyy-MM-dd";
+        }
+        String dateString = "";
+        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+        try {
+            dateString = sdf.format(date);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+        return dateString;
+    }
+
+    public static String toString(Long time, String pattern) {
+        if (time > 0) {
+            if (time.toString().length() == 10) {
+                time = time * 1000;
+            }
+            Date date = new Date(time);
+            String str = DateUtil.toString(date, pattern);
+            return str;
+        }
+        return "";
+    }
+
+    /**
+     * 为了方便mock 设置此属性
+     * 如果设置了此属性,则回直接返回设置的值
+     */
+    public static Long mockDate;
+
+    public static long getDateline() {
+        if (mockDate != null) {
+            return mockDate;
+        }
+        return System.currentTimeMillis() / 1000;
+    }
+
+    /**
+     * 判断当前时间是否在某个时间范围
+     *
+     * @param start 开始时间,以秒为单位的时间戳
+     * @param end   结束时间,以秒为单位的时间戳
+     * @return 是否在范围内
+     */
+    public static boolean inRangeOf(long start, long end) {
+        long now = getDateline();
+        return start <= now && end >= now;
+    }
+
+    public static long getDateline(String date) {
+        return toDate(date, "yyyy-MM-dd").getTime() / 1000;
+    }
+
+    public static long getDateHaveHour(String date) {
+        return toDate(date, "yyyy-MM-dd HH").getTime() / 1000;
+    }
+
+    public static long getDateline(String date, String pattern) {
+        return toDate(date, pattern).getTime() / 1000;
+    }
+
+    /**
+     * 查当前日期是一周中的星期几
+     * @return 1=Sunday,,,7=Saturday
+     */
+    public static long getDayOfWeek(Date date){
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
+        return cal.get(Calendar.DAY_OF_WEEK);// 1=Sunday,,,7=Saturday
+    }
+
+    public static long getDayOfWeekForMonBegin(Date date){
+        long week=getDayOfWeek(date);
+         if(week== Calendar.SUNDAY)return 7;
+         else return week-1;
+    }
+
+    public static String getNowStrForRs485(){
+        Date date = new Date();
+        String nowString = DateUtil.toString(date, "yyyy-MM-dd HH:mm:ss");
+        nowString += " " + getDayOfWeekForMonBegin(date);
+        return nowString;
+    }
+
+}