From 0f58218badea86a5209ae7f1ccd60b7cb4b26eee Mon Sep 17 00:00:00 2001
From: panlinlin <648540858@qq.com>
Date: 星期四, 25 二月 2021 18:10:02 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into wvp-28181-2.0

---
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java |    8 
 src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java                                          |   68 +
 src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java                           |    7 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java    |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java                            |  166 ++
 src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java                |   33 
 web_src/package-lock.json                                                                       |   80 +
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java                     |   95 +
 web_src/index.html                                                                              |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java                           |    1 
 web_src/src/components/Login.vue                                                                |   41 
 web_src/src/main.js                                                                             |   10 
 src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java                                |   19 
 src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java          |    8 
 src/main/resources/application-dev.yml                                                          |    6 
 web_src/src/components/UiHeader.vue                                                             |    4 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java                                |   90 +
 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java                           |  120 +
 src/main/java/com/genersoft/iot/vmp/web/AuthController.java                                     |    3 
 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java                            |   87 +
 src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java                                         |   18 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java               |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java   |  384 +++++
 src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java                          |    3 
 web_src/package.json                                                                            |    1 
 src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java                                  |    3 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java                                |    2 
 src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java                 |   58 
 src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java                   |   11 
 README.md                                                                                       |   53 
 src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java                           |    2 
 src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java                             |   70 
 src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java                     |   13 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java                   |   26 
 web_src/src/router/index.js                                                                     |    6 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java                       |   18 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java  |  515 +++++--
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java                 |  516 ++++++
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java                              |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java                         |    5 
 web_src/src/components/devicePosition.vue                                                       |  388 +++++
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java                         |   20 
 src/main/java/com/genersoft/iot/vmp/web/ApiController.java                                      |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java          |   47 
 src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java                       |   13 
 src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java                    |    7 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java     |   78 +
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java |    2 
 web_src/src/components/GeoConvertTools.js                                                       |  250 +++
 src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java                              |   41 
 src/main/java/com/genersoft/iot/vmp/vmanager/MobilePosition/MobilePositionController.java       |  118 +
 src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java                                    |    2 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java                                    |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java                                  |  119 +
 src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java                             |    8 
 src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java                                       |    1 
 web_src/src/components/videoList.vue                                                            |   47 
 src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java                   |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/BaiduPoint.java                                |   24 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java         |   18 
 src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java                         |   29 
 src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java                                |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java                                       |    2 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java                          |   29 
 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java                          |  238 +++
 src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java                           |    6 
 src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java                               |    3 
 web_src/.postcssrc.js                                                                           |    2 
 68 files changed, 3,578 insertions(+), 494 deletions(-)

diff --git a/README.md b/README.md
index 929e85e..8912045 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,12 @@
 # gitee鍚屾浠撳簱
 https://gitee.com/18010473990/wvp-GB28181.git
 
+# 鎴浘
+![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_1.png)
+![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_2.png)
+![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_151459.png)
+![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_152643.png)
+![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_151606.png)
 
 # 1.0 鏀寔鐗规��  
 1. 瑙嗛棰勮;  
@@ -36,8 +42,32 @@
 19. 鏀寔鎾斁h265, g.711鏍煎紡鐨勬祦(闇�瑕佸皢closeWaitRTPInfo璁句负false)
 20. 鎶ヨ淇℃伅澶勭悊锛屾敮鎸佸悜鍓嶇鎺ㄩ�佹姤璀︿俊鎭�
 
-# 2.0 鏀寔鐗规�� 
-- [ ] 鍥芥爣閫氶亾鍚戜笂绾ц仈  
+# 鏂版敮鎸佺壒鎬�  
+1. 闆嗘垚web鐣岄潰, 涓嶉渶瑕佸崟鐙儴缃插墠绔湇鍔�, 鐩存帴鍒╃敤wvp鍐呯疆鏂囦欢鏈嶅姟閮ㄧ讲, 闅弚vp涓�璧烽儴缃�;   
+2. 鏀寔骞冲彴鎺ュ叆, 閽堝澶у钩鍙板ぇ閲忚澶囩殑鎯呭喌杩涜浼樺寲;  
+3. 鏀寔妫�绱�,閫氶亾绛涢��;  
+4. 鏀寔鑷姩閰嶇疆ZLM濯掍綋鏈嶅姟, 鍑忓皯鍥犻厤缃棶棰樻墍鍑虹幇鐨勯棶棰�;  
+5. 鏀寔鍚敤udp澶氱鍙fā寮�, 鎻愰珮udp妯″紡涓嬪獟浣撲紶杈撴�ц兘;  
+6. 鏀寔閫氶亾鏄惁鍚湁闊抽鐨勮缃�;  
+7. 鏀寔閫氶亾瀛愮洰褰曟煡璇�;  
+8. 鏀寔udp/tcp鍥芥爣娴佷紶杈撴ā寮�;  
+9. 鏀寔鐩存帴杈撳嚭RTSP銆丷TMP銆丠TTP-FLV銆乄ebsocket-FLV銆丠LS澶氱鍗忚娴佸湴鍧�  
+10. 鏀寔鍥芥爣缃戠粶鏍℃椂  
+11. 鏀寔鍏綉閮ㄧ讲, 鏀寔wvp涓巣lm鍒嗗紑閮ㄧ讲   
+12. 鏀寔鎾斁h265, g.711鏍煎紡鐨勬祦   
+13. 鏀寔鍥哄畾娴佸湴鍧�鍜岃嚜鍔ㄧ偣鎾紝鍚屾椂鏀寔鏈偣鎾椂鐩存帴鎾斁娴佸湴鍧�锛屼唬鐮佽嚜鍔ㄥ彂璧风偣鎾�.  ( [鏌ョ湅WIKI](https://github.com/648540858/wvp-GB28181-pro/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E5%9B%BA%E5%AE%9A%E6%92%AD%E6%94%BE%E5%9C%B0%E5%9D%80%E4%B8%8E%E8%87%AA%E5%8A%A8%E7%82%B9%E6%92%AD)锛�
+14. 鎶ヨ淇℃伅澶勭悊锛屾敮鎸佸悜鍓嶇鎺ㄩ�佹姤璀︿俊鎭�
+15. 鏀寔璁㈤槄涓庨�氱煡鏂规硶
+   -  [X] 绉诲姩浣嶇疆璁㈤槄
+   -  [X] 绉诲姩浣嶇疆閫氱煡澶勭悊
+   -  [ ] 鎶ヨ浜嬩欢璁㈤槄
+   -  [X] 鎶ヨ浜嬩欢閫氱煡澶勭悊
+   -  [ ] 璁惧鐩綍璁㈤槄
+   -  [X] 璁惧鐩綍閫氱煡澶勭悊
+16. 绉诲姩浣嶇疆鏌ヨ鍜屾樉绀猴紝鍙�氳繃閰嶇疆鏂囦欢璁剧疆绉诲姩浣嶇疆鍘嗗彶鏄惁瀛樺偍
+
+# 2.0 鏀寔鐗规��
+- [ ] 鍥芥爣閫氶亾鍚戜笂绾ц仈
     - [X] WEB娣诲姞涓婄骇骞冲彴
     - [X] 娉ㄥ唽
     - [X] 蹇冭烦淇濇椿
@@ -45,13 +75,18 @@
     - [X] 閫氶亾鎺ㄩ��
     - [ ] 鐐规挱
     - [ ] 浜戝彴鎺у埗
-- [ ] 娣诲姞RTSP瑙嗛  
-- [ ] 娣诲姞ONVIF鎺㈡祴灞�鍩熺綉鍐呯殑璁惧  
-- [ ] 娣诲姞RTMP瑙嗛  
-- [ ] 娣诲姞绯荤粺閰嶇疆  
-- [ ] 娣诲姞鐢ㄦ埛绠$悊  
- 
- 
+- [ ] 娣诲姞RTSP瑙嗛
+- [ ] 娣诲姞ONVIF鎺㈡祴灞�鍩熺綉鍐呯殑璁惧
+- [ ] 娣诲姞RTMP瑙嗛
+- [ ] 娣诲姞绯荤粺閰嶇疆
+- [ ] 娣诲姞鐢ㄦ埛绠$悊
+
+# 寰呭疄鐜帮細 
+涓婄骇绾ц仈  
+鎺ㄦ祦鍒楄〃  
+鎷夋祦鍒楄〃  
+web鐣岄潰绯荤粺璁剧疆
+浣跨敤mysql浣滀负鏁版嵁搴�  
 
 # 椤圭洰閮ㄧ讲
 鍙傝��:[WIKI](https://github.com/648540858/wvp-GB28181-pro/wiki)
diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
index 990f003..4a92e09 100644
--- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
+++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
@@ -3,7 +3,6 @@
 import java.util.logging.LogManager;
 
 import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 
 @SpringBootApplication
diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
index 6ad8e7e..832bce3 100644
--- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
+++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.common;
 
 /**    
- * @Description:TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜绫荤殑浣滅敤)   
+ * @Description: 瀹氫箟甯搁噺   
  * @author: swwheihei
  * @date:   2019骞�5鏈�30鏃� 涓嬪崍3:04:04   
  *   
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java
new file mode 100644
index 0000000..da587f4
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java
@@ -0,0 +1,18 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration("userSetup")
+public class UserSetup {
+    @Value("${userSettings.savePositionHistory}")
+    boolean savePositionHistory;
+
+    public boolean getSavePositionHistory() {
+        return savePositionHistory;
+    }
+
+    public void setSavePositionHistory(boolean savePositionHistory) {
+        this.savePositionHistory = savePositionHistory;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java
index 4c71b5c..ff9d643 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java
@@ -4,7 +4,7 @@
 import org.springframework.context.annotation.Configuration;
 
 /**    
- * @Description:TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜绫荤殑浣滅敤)   
+ * @Description: 鑾峰彇鏁版嵁搴撻厤缃�   
  * @author: swwheihei
  * @date:   2020骞�5鏈�6鏃� 涓嬪崍2:46:00     
  */
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
index c564edd..7c767ba 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
@@ -149,8 +149,6 @@
 					}
 				}
 			}
-		// } else if (status == Response.TRYING) {
-			// trying涓嶄細鍥炲
 		} else if ((status >= 100) && (status < 200)) {
 			// 澧炲姞鍏跺畠鏃犻渶鍥炲鐨勫搷搴旓紝濡�101銆�180绛�
 		} else {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
index 53282e2..4605ede 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
@@ -91,8 +91,10 @@
         long time = date.getTime();
         Random rand = new Random();
         long pad = rand.nextLong();
-        String nonceString = (new Long(time)).toString()
-                + (new Long(pad)).toString();
+        // String nonceString = (new Long(time)).toString()
+        //         + (new Long(pad)).toString();
+        String nonceString = Long.valueOf(time).toString()
+                + Long.valueOf(pad).toString();
         byte mdbytes[] = messageDigest.digest(nonceString.getBytes());
         // Convert the mdbytes array into a hex string.
         return toHexString(mdbytes);
@@ -191,7 +193,7 @@
 
         // 瀹㈡埛绔殢鏈烘暟锛岃繖鏄竴涓笉閫忔槑鐨勫瓧绗︿覆鍊硷紝鐢卞鎴风鎻愪緵锛屽苟涓斿鎴风鍜屾湇鍔″櫒閮戒細浣跨敤锛屼互閬垮厤鐢ㄦ槑鏂囨枃鏈��
         // 杩欎娇寰楀弻鏂归兘鍙互鏌ラ獙瀵规柟鐨勮韩浠斤紝骞跺娑堟伅鐨勫畬鏁存�ф彁渚涗竴浜涗繚鎶�
-        String cNonce = authHeader.getCNonce();
+        //String cNonce = authHeader.getCNonce();
 
         // nonce璁℃暟鍣紝鏄竴涓�16杩涘埗鐨勬暟鍊硷紝琛ㄧず鍚屼竴nonce涓嬪鎴风鍙戦�佸嚭璇锋眰鐨勬暟閲�
         int nc = authHeader.getNonceCount();
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/BaiduPoint.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/BaiduPoint.java
new file mode 100644
index 0000000..8f33f30
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/BaiduPoint.java
@@ -0,0 +1,24 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class BaiduPoint {
+
+    String bdLng;
+
+    String bdLat;
+
+    public String getBdLng() {
+        return bdLng;
+    }
+
+    public void setBdLng(String bdLng) {
+        this.bdLng = bdLng;
+    }
+
+    public String getBdLat() {
+        return bdLat;
+    }
+
+    public void setBdLat(String bdLat) {
+        this.bdLat = bdLat;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java
new file mode 100644
index 0000000..47535a6
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java
@@ -0,0 +1,166 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+/**
+ * @Description: 绉诲姩浣嶇疆bean
+ * @author: lawrencehj
+ * @date: 2021骞�1鏈�23鏃�
+ */
+
+public class MobilePosition {
+    /**
+     * 璁惧Id
+     */
+    private String deviceId;
+
+    /**
+     * 璁惧鍚嶇О
+     */
+    private String deviceName;
+
+    /**
+     * 閫氱煡鏃堕棿
+     */
+    private String time;
+
+    /**
+     * 缁忓害
+     */
+    private double longitude;
+
+    /**
+     * 绾害
+     */
+    private double latitude;
+
+    /**
+     * 娴锋嫈楂樺害
+     */
+    private double altitude;
+
+    /**
+     * 閫熷害
+     */
+    private double speed;
+
+    /**
+     * 鏂瑰悜
+     */
+    private double direction;
+
+    /**
+     * 浣嶇疆淇℃伅涓婃姤鏉ユ簮锛圡obile Position銆丟PS Alarm锛�
+     */
+    private String reportSource;
+
+    /**
+     * 鍥藉唴鍦扮悊鍧愭爣绯伙紙GCJ-02 / BD-09锛�
+     */
+    private String GeodeticSystem;
+
+    /**
+     * 鍥藉唴鍧愭爣绯伙細缁忓害鍧愭爣
+     */
+    private String cnLng;
+
+    /**
+     * 鍥藉唴鍧愭爣绯伙細绾害鍧愭爣
+     */
+    private String cnLat;
+
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getDeviceName() {
+        return deviceName;
+    }
+
+    public void setDeviceName(String deviceName) {
+        this.deviceName = deviceName;
+    }
+
+    public String getTime() {
+        return time;
+    }
+
+    public void setTime(String time) {
+        this.time = time;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+
+    public void setLongitude(double longitude) {
+        this.longitude = longitude;
+    }
+    
+    public double getLatitude() {
+        return latitude;
+    }
+
+    public void setLatitude(double latitude) {
+        this.latitude = latitude;
+    }
+
+    public double getAltitude() {
+        return altitude;
+    }
+
+    public void setAltitude(double altitude) {
+        this.altitude = altitude;
+    }
+
+    public double getSpeed() {
+        return speed;
+    }
+
+    public void setSpeed(double speed) {
+        this.speed = speed;
+    }
+
+    public double getDirection() {
+        return direction;
+    }
+
+    public void setDirection(double direction) {
+        this.direction = direction;
+    }
+
+    public String getReportSource() {
+        return reportSource;
+    }
+
+    public void setReportSource(String reportSource) {
+        this.reportSource = reportSource;
+    }
+
+    public String getGeodeticSystem() {
+        return GeodeticSystem;
+    }
+
+    public void setGeodeticSystem(String geodeticSystem) {
+        GeodeticSystem = geodeticSystem;
+    }
+
+    public String getCnLng() {
+        return cnLng;
+    }
+
+    public void setCnLng(String cnLng) {
+        this.cnLng = cnLng;
+    }
+
+    public String getCnLat() {
+        return cnLat;
+    }
+
+    public void setCnLat(String cnLat) {
+        this.cnLat = cnLat;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
index 45a6112..4ec182a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
 
-import gov.nist.javax.sip.header.SIPDate;
+//import gov.nist.javax.sip.header.SIPDate;
 
 import java.util.List;
 
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java
index ba1a1c7..f2a256b 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java
@@ -1,14 +1,100 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
+import gov.nist.core.InternalErrorHandler;
 import gov.nist.javax.sip.header.SIPDate;
+
+import java.util.*;
 
 /**
  * 閲嶅啓jain sip鐨凷IPDate瑙e喅涓庡浗鏍囨椂闂存牸寮忎笉涓�鑷寸殑闂
  */
 public class WvpSipDate extends SIPDate {
 
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+    
+    private Calendar javaCal;
+
     public WvpSipDate(long timeMillis) {
-        super(timeMillis);
+        this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
+        Date date = new Date(timeMillis);
+        this.javaCal.setTime(date);
+        this.wkday = this.javaCal.get(7);
+        switch(this.wkday) {
+            case 1:
+                this.sipWkDay = "Sun";
+                break;
+            case 2:
+                this.sipWkDay = "Mon";
+                break;
+            case 3:
+                this.sipWkDay = "Tue";
+                break;
+            case 4:
+                this.sipWkDay = "Wed";
+                break;
+            case 5:
+                this.sipWkDay = "Thu";
+                break;
+            case 6:
+                this.sipWkDay = "Fri";
+                break;
+            case 7:
+                this.sipWkDay = "Sat";
+                break;
+            default:
+                InternalErrorHandler.handleException("No date map for wkday " + this.wkday);
+        }
+
+        this.day = this.javaCal.get(5);
+        this.month = this.javaCal.get(2);
+        switch(this.month) {
+            case 0:
+                this.sipMonth = "Jan";
+                break;
+            case 1:
+                this.sipMonth = "Feb";
+                break;
+            case 2:
+                this.sipMonth = "Mar";
+                break;
+            case 3:
+                this.sipMonth = "Apr";
+                break;
+            case 4:
+                this.sipMonth = "May";
+                break;
+            case 5:
+                this.sipMonth = "Jun";
+                break;
+            case 6:
+                this.sipMonth = "Jul";
+                break;
+            case 7:
+                this.sipMonth = "Aug";
+                break;
+            case 8:
+                this.sipMonth = "Sep";
+                break;
+            case 9:
+                this.sipMonth = "Oct";
+                break;
+            case 10:
+                this.sipMonth = "Nov";
+                break;
+            case 11:
+                this.sipMonth = "Dec";
+                break;
+            default:
+                InternalErrorHandler.handleException("No date map for month " + this.month);
+        }
+
+        this.year = this.javaCal.get(1);
+        this.hour = this.javaCal.get(11);
+        this.minute = this.javaCal.get(12);
+        this.second = this.javaCal.get(13);
     }
 
     @Override
@@ -48,7 +134,7 @@
             var6 = "" + this.second;
         }
 
-        int var8 = this.getJavaCal().get(14);
+        int var8 = this.javaCal.get(14);
         String var7;
         if (var8 < 10) {
             var7 = "00" + var8;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
index c796a4a..58b03dd 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
@@ -65,4 +65,5 @@
 		alarmEvent.setAlarmInfo(deviceAlarm);
 		applicationEventPublisher.publishEvent(alarmEvent);
 	}
+	
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
index 176a435..e00a59f 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
@@ -1,21 +1,13 @@
 package com.genersoft.iot.vmp.gb28181.event;
 
-import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import javax.sip.ResponseEvent;
-import javax.sip.message.Request;
-import java.util.EventObject;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 @Component
 public class SipSubscribe {
-
-    private final static Logger logger = LoggerFactory.getLogger(SipSubscribe.class);
 
     private Map<String, SipSubscribe.Event> errorSubscribes = new ConcurrentHashMap<>();
 
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java
index fecc8b1..9f91bed 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java
@@ -10,6 +10,11 @@
  */
 
 public class AlarmEvent extends ApplicationEvent {
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
     public AlarmEvent(Object source) {
         super(source);
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java
index 5553106..aa45efe 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java
@@ -3,18 +3,17 @@
 import org.springframework.context.ApplicationEvent;
 
 /**    
- * @Description:TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜绫荤殑浣滅敤)   
+ * @Description: 绂荤嚎浜嬩欢绫�   
  * @author: swwheihei
  * @date:   2020骞�5鏈�6鏃� 涓婂崍11:33:13     
  */
 public class OfflineEvent extends ApplicationEvent {
 	
-	/**   
-	 * @Title:  OutlineEvent   
-	 * @Description:    TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜鏂规硶鐨勪綔鐢�)   
-	 * @param:  @param source  
-	 * @throws   
-	 */  
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = 1L;
+
 	public OfflineEvent(Object source) {
 		super(source);
 	}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java
index 86eddc1..e077cb9 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java
@@ -3,18 +3,17 @@
 import org.springframework.context.ApplicationEvent;
 
 /**    
- * @Description:TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜绫荤殑浣滅敤)   
+ * @Description: 鍦ㄧ嚎浜嬩欢绫�   
  * @author: swwheihei
  * @date:   2020骞�5鏈�6鏃� 涓婂崍11:32:56     
  */
 public class OnlineEvent extends ApplicationEvent {
 
-	/**   
-	 * @Title:  OnlineEvent   
-	 * @Description:    TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜鏂规硶鐨勪綔鐢�)   
-	 * @param:  @param source  
-	 * @throws   
-	 */  
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = 1L;
+
 	public OnlineEvent(Object source) {
 		super(source);
 	}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
index d48cae5..9c8129d 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
@@ -19,6 +19,8 @@
 import com.genersoft.iot.vmp.vmanager.service.IPlayService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+// import org.slf4j.Logger;
+// import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Lazy;
@@ -36,6 +38,7 @@
 import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor;
@@ -49,7 +52,7 @@
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 
 /**    
- * @Description:TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜绫荤殑浣滅敤)   
+ * @Description: SIP淇′护澶勭悊鍒嗛厤
  * @author: swwheihei
  * @date:   2020骞�5鏈�3鏃� 涓嬪崍4:24:37     
  */
@@ -78,7 +81,7 @@
 
 	@Autowired
 	private SIPCommanderFroPlatform cmderFroPlatform;
-	
+
 	@Autowired
 	private RedisUtil redis;
 	
@@ -122,6 +125,7 @@
 		Request request = evt.getRequest();
 		String method = request.getMethod();
 //		logger.info("鎺ユ敹鍒版秷鎭細"+request.getMethod());
+//		sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);
 		if (Request.INVITE.equals(method)) {
 			InviteRequestProcessor processor = new InviteRequestProcessor();
 			processor.setRequestEvent(evt);
@@ -161,7 +165,6 @@
 			processor.setRequestEvent(evt);
 			return processor;
 		} else if (Request.MESSAGE.equals(method)) {
-
 			MessageRequestProcessor processor = new MessageRequestProcessor();
 			processor.setRequestEvent(evt);
 			processor.setTcpSipProvider(getTcpSipProvider());
@@ -175,8 +178,23 @@
 			processor.setStorager(storager);
 			processor.setRedisCatchStorage(redisCatchStorage);
 			return processor;
+		} else if (Request.NOTIFY.equalsIgnoreCase(method)) {
+			NotifyRequestProcessor processor = new NotifyRequestProcessor();
+			processor.setRequestEvent(evt);
+			processor.setTcpSipProvider(getTcpSipProvider());
+			processor.setUdpSipProvider(getUdpSipProvider());
+			processor.setPublisher(publisher);
+			processor.setRedis(redis);
+			processor.setDeferredResultHolder(deferredResultHolder);
+			processor.setOffLineDetector(offLineDetector);
+			processor.setCmder(cmder);
+			processor.setStorager(storager);
+			processor.setRedisCatchStorage(redisCatchStorage);
+			return processor;
 		} else {
-			return new OtherRequestProcessor();
+			OtherRequestProcessor processor = new OtherRequestProcessor();
+			processor.setRequestEvent(evt);
+			return processor;
 		}
 	}
 	
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java
new file mode 100644
index 0000000..770edf0
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java
@@ -0,0 +1,78 @@
+package com.genersoft.iot.vmp.gb28181.transmit.callback;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
+import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
+import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+
+import org.slf4j.Logger;
+
+@SuppressWarnings("unchecked")
+public class CheckForAllRecordsThread extends Thread {
+
+    private String key;
+
+    private RecordInfo recordInfo;
+
+    private RedisUtil redis;
+
+    private Logger logger;
+
+    private DeferredResultHolder deferredResultHolder;
+
+    public CheckForAllRecordsThread(String key, RecordInfo recordInfo) {
+        this.key = key;
+        this.recordInfo = recordInfo;
+    }
+
+    public void run() {
+
+        String cacheKey = this.key;
+
+        for (long stop = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); stop > System.nanoTime();) {
+            List<Object> cacheKeys = redis.scan(cacheKey + "_*");
+            List<RecordItem> totalRecordList = new ArrayList<RecordItem>();
+            for (int i = 0; i < cacheKeys.size(); i++) {
+                totalRecordList.addAll((List<RecordItem>) redis.get(cacheKeys.get(i).toString()));
+            }
+            if (totalRecordList.size() < this.recordInfo.getSumNum()) {
+                logger.info("宸茶幏鍙�" + totalRecordList.size() + "椤瑰綍鍍忔暟鎹紝鍏�" + this.recordInfo.getSumNum() + "椤�");
+            } else {
+                logger.info("褰曞儚鏁版嵁宸插叏閮ㄨ幏鍙栵紝鍏�" + this.recordInfo.getSumNum() + "椤�");
+                this.recordInfo.setRecordList(totalRecordList);
+                for (int i = 0; i < cacheKeys.size(); i++) {
+                    redis.del(cacheKeys.get(i).toString());
+                }
+                break;
+            }
+        }
+        // 鑷劧椤哄簭鎺掑簭, 鍏冪礌杩涜鍗囧簭鎺掑垪
+        this.recordInfo.getRecordList().sort(Comparator.naturalOrder());
+        RequestMessage msg = new RequestMessage();
+        String deviceId = recordInfo.getDeviceId();
+        msg.setDeviceId(deviceId);
+        msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
+        msg.setData(recordInfo);
+        deferredResultHolder.invokeResult(msg);
+        logger.info("澶勭悊瀹屾垚锛岃繑鍥炵粨鏋�");
+        MessageRequestProcessor.threadNameList.remove(cacheKey);
+    }
+    
+	public void setRedis(RedisUtil redis) {
+		this.redis = redis;
+	}
+
+	public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) {
+		this.deferredResultHolder = deferredResultHolder;
+    }
+
+    public void setLogger(Logger logger) {
+        this.logger = logger;
+    }
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
index 0759d7f..ab72be5 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
@@ -1,6 +1,5 @@
 package com.genersoft.iot.vmp.gb28181.transmit.callback;
 
-import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -10,14 +9,23 @@
 import org.springframework.web.context.request.async.DeferredResult;
 
 /**    
- * @Description:TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜绫荤殑浣滅敤)   
+ * @Description: 寮傛璇锋眰澶勭悊
  * @author: swwheihei
  * @date:   2020骞�5鏈�8鏃� 涓嬪崍7:59:05     
  */
+@SuppressWarnings(value = {"rawtypes", "unchecked"})
 @Component
 public class DeferredResultHolder {
 	
+	public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS";
+	
 	public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO";
+	
+	public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL";
+	
+	public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG";
+
+	public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD";
 	
 	public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG";
 	
@@ -27,6 +35,12 @@
 
 	public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
 
+	public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
+
+	public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
+
+	public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
+
 	private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>();
 
 	public void put(String key, DeferredResult result) {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java
index fcf7e7a..223092f 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java
@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.transmit.callback;
 
 /**    
- * @Description:TODO(杩欓噷鐢ㄤ竴鍙ヨ瘽鎻忚堪杩欎釜绫荤殑浣滅敤)   
+ * @Description: 璇锋眰淇℃伅瀹氫箟   
  * @author: swwheihei
  * @date:   2020骞�5鏈�8鏃� 涓嬪崍1:09:18     
  */
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
index 67fd996..6fa4eca 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -1,6 +1,5 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 
-import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
@@ -115,24 +114,35 @@
 	/**
 	 * 闊宠棰戝綍鍍忔帶鍒�
 	 * 
-	 * @param device  瑙嗛璁惧
-	 * @param channelId  棰勮閫氶亾
+	 * @param device  		瑙嗛璁惧
+	 * @param channelId  	棰勮閫氶亾
+	 * @param recordCmdStr	褰曞儚鍛戒护锛歊ecord / StopRecord
 	 */
-	boolean recordCmd(Device device,String channelId);
+	boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);
 	
+	/**
+	 * 杩滅▼鍚姩鎺у埗鍛戒护
+	 * 
+	 * @param device	瑙嗛璁惧
+	 */
+	boolean teleBootCmd(Device device);
+
 	/**
 	 * 鎶ヨ甯冮槻/鎾ら槻鍛戒护
 	 * 
-	 * @param device  瑙嗛璁惧
+	 * @param device  	瑙嗛璁惧
+	 * @param setGuard	true: SetGuard, false: ResetGuard
 	 */
-	boolean guardCmd(Device device);
+	boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 鎶ヨ澶嶄綅鍛戒护
 	 * 
-	 * @param device  瑙嗛璁惧
+	 * @param device		瑙嗛璁惧
+	 * @param alarmMethod	鎶ヨ鏂瑰紡锛堝彲閫夛級
+	 * @param alarmType		鎶ヨ绫诲瀷锛堝彲閫夛級
 	 */
-	boolean alarmCmd(Device device);
+	boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 寮哄埗鍏抽敭甯у懡浠�,璁惧鏀跺埌姝ゅ懡浠ゅ簲绔嬪埢鍙戦�佷竴涓狪DR甯�
@@ -140,14 +150,17 @@
 	 * @param device  瑙嗛璁惧
 	 * @param channelId  棰勮閫氶亾
 	 */
-	boolean iFameCmd(Device device,String channelId);
+	boolean iFrameCmd(Device device, String channelId);
 	
 	/**
 	 * 鐪嬪畧浣嶆帶鍒跺懡浠�
 	 * 
-	 * @param device  瑙嗛璁惧
+	 * @param device		瑙嗛璁惧
+	 * @param enabled		鐪嬪畧浣嶄娇鑳斤細1 = 寮�鍚紝0 = 鍏抽棴
+	 * @param resetTime		鑷姩褰掍綅鏃堕棿闂撮殧锛屽紑鍚湅瀹堜綅鏃朵娇鐢紝鍗曚綅:绉�(s)
+	 * @param presetIndex	璋冪敤棰勭疆浣嶇紪鍙凤紝寮�鍚湅瀹堜綅鏃朵娇鐢紝鍙栧�艰寖鍥�0~255
 	 */
-	boolean homePositionCmd(Device device);
+	boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 璁惧閰嶇疆鍛戒护
@@ -156,13 +169,24 @@
 	 */
 	boolean deviceConfigCmd(Device device);
 	
+		/**
+	 * 璁惧閰嶇疆鍛戒护锛歜asicParam
+	 * 
+	 * @param device  			瑙嗛璁惧
+	 * @param channelId			閫氶亾缂栫爜锛堝彲閫夛級
+	 * @param name				璁惧/閫氶亾鍚嶇О锛堝彲閫夛級
+	 * @param expiration		娉ㄥ唽杩囨湡鏃堕棿锛堝彲閫夛級
+	 * @param heartBeatInterval	蹇冭烦闂撮殧鏃堕棿锛堝彲閫夛級
+	 * @param heartBeatCount	蹇冭烦瓒呮椂娆℃暟锛堝彲閫夛級
+	 */  
+	boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 鏌ヨ璁惧鐘舵��
 	 * 
 	 * @param device 瑙嗛璁惧
 	 */
-	boolean deviceStatusQuery(Device device);
+	boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 鏌ヨ璁惧淇℃伅
@@ -191,30 +215,65 @@
 	/**
 	 * 鏌ヨ鎶ヨ淇℃伅
 	 * 
-	 * @param device 瑙嗛璁惧
+	 * @param device		瑙嗛璁惧
+	 * @param startPriority	鎶ヨ璧峰绾у埆锛堝彲閫夛級
+	 * @param endPriority	鎶ヨ缁堟绾у埆锛堝彲閫夛級
+	 * @param alarmMethod	鎶ヨ鏂瑰紡鏉′欢锛堝彲閫夛級
+	 * @param alarmType		鎶ヨ绫诲瀷
+	 * @param startTime		鎶ヨ鍙戠敓璧峰鏃堕棿锛堝彲閫夛級
+	 * @param endTime		鎶ヨ鍙戠敓缁堟鏃堕棿锛堝彲閫夛級
+	 * @return				true = 鍛戒护鍙戦�佹垚鍔�
 	 */
-	boolean alarmInfoQuery(Device device);
+	boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
+							String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);	
 	
 	/**
 	 * 鏌ヨ璁惧閰嶇疆
 	 * 
-	 * @param device 瑙嗛璁惧
+	 * @param device 		瑙嗛璁惧
+	 * @param channelId		閫氶亾缂栫爜锛堝彲閫夛級
+	 * @param configType	閰嶇疆绫诲瀷锛�
 	 */
-	boolean configQuery(Device device);
+	boolean deviceConfigQuery(Device device, String channelId, String configType,  SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 鏌ヨ璁惧棰勭疆浣嶇疆
 	 * 
 	 * @param device 瑙嗛璁惧
 	 */
-	boolean presetQuery(Device device);
+	boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 鏌ヨ绉诲姩璁惧浣嶇疆鏁版嵁
 	 * 
 	 * @param device 瑙嗛璁惧
 	 */
-	boolean mobilePostitionQuery(Device device);
+	boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 璁㈤槄銆佸彇娑堣闃呯Щ鍔ㄤ綅缃�
+	 * 
+	 * @param device	瑙嗛璁惧
+	 * @param expires	璁㈤槄瓒呮椂鏃堕棿锛堝��=0鏃朵负鍙栨秷璁㈤槄锛�
+	 * @param interval	涓婃姤鏃堕棿闂撮殧
+	 * @return			true = 鍛戒护鍙戦�佹垚鍔�
+	 */
+	boolean mobilePositionSubscribe(Device device, int expires, int interval);
+
+	/**
+	 * 璁㈤槄銆佸彇娑堣闃呮姤璀︿俊鎭�
+	 * @param device		瑙嗛璁惧
+	 * @param expires		璁㈤槄杩囨湡鏃堕棿锛�0 = 鍙栨秷璁㈤槄锛�
+	 * @param startPriority	鎶ヨ璧峰绾у埆锛堝彲閫夛級
+	 * @param endPriority	鎶ヨ缁堟绾у埆锛堝彲閫夛級
+	 * @param alarmMethods	鎶ヨ鏂瑰紡鏉′欢锛堝彲閫夛級
+	 * @param alarmType		鎶ヨ绫诲瀷
+	 * @param startTime		鎶ヨ鍙戠敓璧峰鏃堕棿锛堝彲閫夛級
+	 * @param endTime		鎶ヨ鍙戠敓缁堟鏃堕棿锛堝彲閫夛級
+	 * @return				true = 鍛戒护鍙戦�佹垚鍔�
+	 */
+	boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
+
 
 	/**
 	 * 閲婃斁rtpserver
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
index 2829d0e..8d6c202 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
@@ -168,4 +168,51 @@
 		request.setContent(content, contentTypeHeader);
 		return request;
 	}
+
+	public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+		Request request = null;
+		// sipuri
+		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(),
+				device.getTransport(), viaTag);
+		viaHeader.setRPort();
+		viaHeaders.add(viaHeader);
+		// from
+		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),
+				sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
+		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		// to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
+		// callid
+		CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+				: udpSipProvider.getNewCallId();
+		// Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		// ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE);
+
+		request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
+				toHeader, viaHeaders, maxForwards);
+
+
+		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
+		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+
+		// Expires
+		ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires);
+		request.addHeader(expireHeader);
+
+		// Event
+		EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event);
+		request.addHeader(eventHeader);
+
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
+		request.setContent(content, contentTypeHeader);
+		return request;
+	}
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
index 7ff5d14..6bab809 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -1,21 +1,18 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
 
 import java.text.ParseException;
-import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import javax.sip.*;
 import javax.sip.address.SipURI;
 import javax.sip.header.CallIdHeader;
-import javax.sip.header.Header;
 import javax.sip.header.ViaHeader;
 import javax.sip.message.Request;
 
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.MediaServerConfig;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
@@ -34,6 +31,8 @@
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
 import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
+import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 
 /**    
  * @Description:璁惧鑳藉姏鎺ュ彛锛岀敤浜庡畾涔夎澶囩殑鎺у埗銆佹煡璇㈣兘鍔�   
@@ -235,7 +234,8 @@
 			ptzXml.append("</Info>\r\n");
 			ptzXml.append("</Control>\r\n");
 			
-			Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtzTag", null);
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null);
 			
 			transmitRequest(device, request);
 			return true;
@@ -271,7 +271,8 @@
 			ptzXml.append("</Info>\r\n");
 			ptzXml.append("</Control>\r\n");
 			
-			Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtzTag", null);
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null);
 			transmitRequest(device, request);
 			return true;
 		} catch (SipException | ParseException | InvalidArgumentException e) {
@@ -383,7 +384,8 @@
 
 			content.append("y="+ssrc+"\r\n");//ssrc
 
-			Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null, ssrc);
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc);
 
 			ClientTransaction transaction = transmitRequest(device, request, errorEvent);
 			streamSession.put(streamId, transaction);
@@ -482,7 +484,8 @@
 
 	        content.append("y="+ssrc+"\r\n");//ssrc
 	        
-	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "playback", null);
+			String tm = Long.toString(System.currentTimeMillis());
+	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null);
 
 	        ClientTransaction transaction = transmitRequest(device, request, errorEvent);
 	        streamSession.put(streamId, transaction);
@@ -573,24 +576,89 @@
 	/**
 	 * 闊宠棰戝綍鍍忔帶鍒�
 	 * 
-	 * @param device  瑙嗛璁惧
-	 * @param channelId  棰勮閫氶亾
+	 * @param device		瑙嗛璁惧
+	 * @param channelId  	棰勮閫氶亾
+	 * @param recordCmdStr	褰曞儚鍛戒护锛歊ecord / StopRecord
 	 */  
 	@Override
-	public boolean recordCmd(Device device, String channelId) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Control>\r\n");
+			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			if (XmlUtil.isEmpty(channelId)) {
+				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			} else {
+				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+			}
+			cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");
+			cmdXml.append("</Control>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromRecord" + tm, null);
+			transmitRequest(device, request, errorEvent);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
 	}
 
 	/**
+	 * 杩滅▼鍚姩鎺у埗鍛戒护
+	 * 
+	 * @param device	瑙嗛璁惧
+	 */
+	@Override
+	public boolean teleBootCmd(Device device) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Control>\r\n");
+			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");
+			cmdXml.append("</Control>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null);
+			transmitRequest(device, request);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
+	}
+	
+	/**
 	 * 鎶ヨ甯冮槻/鎾ら槻鍛戒护
 	 * 
-	 * @param device  瑙嗛璁惧
-	 */  
+	 * @param device  		瑙嗛璁惧
+	 * @param guardCmdStr	"SetGuard"/"ResetGuard"
+	 */
 	@Override
-	public boolean guardCmd(Device device) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Control>\r\n");
+			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
+			cmdXml.append("</Control>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null);
+			transmitRequest(device, request, errorEvent);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
 	}
 
 	/**
@@ -599,9 +667,37 @@
 	 * @param device  瑙嗛璁惧
 	 */  
 	@Override
-	public boolean alarmCmd(Device device) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Control>\r\n");
+			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");
+			if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) {
+				cmdXml.append("<Info>\r\n");
+			}
+			if (!XmlUtil.isEmpty(alarmMethod)) {
+				cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
+			}
+			if (!XmlUtil.isEmpty(alarmType)) {
+				cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
+			}
+			if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) {
+				cmdXml.append("</Info>\r\n");
+			}
+			cmdXml.append("</Control>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null);
+			transmitRequest(device, request, errorEvent);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
 	}
 
 	/**
@@ -611,20 +707,79 @@
 	 * @param channelId  棰勮閫氶亾
 	 */ 
 	@Override
-	public boolean iFameCmd(Device device, String channelId) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean iFrameCmd(Device device, String channelId) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Control>\r\n");
+			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			if (XmlUtil.isEmpty(channelId)) {
+				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			} else {
+				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+			}
+			cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");
+			cmdXml.append("</Control>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null);
+			transmitRequest(device, request);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
 	}
 
 	/**
 	 * 鐪嬪畧浣嶆帶鍒跺懡浠�
 	 * 
-	 * @param device  瑙嗛璁惧
+	 * @param device		瑙嗛璁惧
+	 * @param enabled		鐪嬪畧浣嶄娇鑳斤細1 = 寮�鍚紝0 = 鍏抽棴
+	 * @param resetTime		鑷姩褰掍綅鏃堕棿闂撮殧锛屽紑鍚湅瀹堜綅鏃朵娇鐢紝鍗曚綅:绉�(s)
+	 * @param presetIndex	璋冪敤棰勭疆浣嶇紪鍙凤紝寮�鍚湅瀹堜綅鏃朵娇鐢紝鍙栧�艰寖鍥�0~255
 	 */  
 	@Override
-	public boolean homePositionCmd(Device device) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Control>\r\n");
+			cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			if (XmlUtil.isEmpty(channelId)) {
+				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			} else {
+				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+			}
+			cmdXml.append("<HomePosition>\r\n");
+			if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
+				cmdXml.append("<Enabled>1</Enabled>\r\n");
+				if (NumericUtil.isInteger(resetTime)) {
+					cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
+				} else {
+					cmdXml.append("<ResetTime>0</ResetTime>\r\n");
+				}
+				if (NumericUtil.isInteger(presetIndex)) {
+					cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
+				} else {
+					cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
+				}
+			} else {
+				cmdXml.append("<Enabled>0</Enabled>\r\n");
+			}
+			cmdXml.append("</HomePosition>\r\n");
+			cmdXml.append("</Control>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null);
+			transmitRequest(device, request, errorEvent);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
 	}
 
 	/**
@@ -639,14 +794,87 @@
 	}
 
 	/**
+	 * 璁惧閰嶇疆鍛戒护锛歜asicParam
+	 * 
+	 * @param device  			瑙嗛璁惧
+	 * @param channelId			閫氶亾缂栫爜锛堝彲閫夛級
+	 * @param name				璁惧/閫氶亾鍚嶇О锛堝彲閫夛級
+	 * @param expiration		娉ㄥ唽杩囨湡鏃堕棿锛堝彲閫夛級
+	 * @param heartBeatInterval	蹇冭烦闂撮殧鏃堕棿锛堝彲閫夛級
+	 * @param heartBeatCount	蹇冭烦瓒呮椂娆℃暟锛堝彲閫夛級
+	 */  
+	@Override
+	public boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, 
+										String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Control>\r\n");
+			cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			if (XmlUtil.isEmpty(channelId)) {
+				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			} else {
+				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+			}
+			cmdXml.append("<BasicParam>\r\n");
+			if (!XmlUtil.isEmpty(name)) {
+				cmdXml.append("<Name>" + name + "</Name>\r\n");
+			}
+			if (NumericUtil.isInteger(expiration)) {
+				if (Integer.valueOf(expiration) > 0) {
+					cmdXml.append("<Expiration>" + expiration + "</Expiration>\r\n");
+				}
+			}
+			if (NumericUtil.isInteger(heartBeatInterval)) {
+				if (Integer.valueOf(heartBeatInterval) > 0) {
+					cmdXml.append("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>\r\n");
+				}
+			}
+			if (NumericUtil.isInteger(heartBeatCount)) {
+				if (Integer.valueOf(heartBeatCount) > 0) {
+					cmdXml.append("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>\r\n");
+				}
+			}
+			cmdXml.append("</BasicParam>\r\n");
+			cmdXml.append("</Control>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null);
+			transmitRequest(device, request, errorEvent);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
+	}
+
+	/**
 	 * 鏌ヨ璁惧鐘舵��
 	 * 
 	 * @param device 瑙嗛璁惧
 	 */  
 	@Override
-	public boolean deviceStatusQuery(Device device) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer catalogXml = new StringBuffer(200);
+			catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
+			catalogXml.append("<Query>\r\n");
+			catalogXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
+			catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			catalogXml.append("</Query>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, "FromStatus" + tm, null);
+
+			transmitRequest(device, request, errorEvent);
+			return true;
+			
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		}
 	}
 
 	/**
@@ -665,7 +893,8 @@
 			catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
 			catalogXml.append("</Query>\r\n");
 			
-			Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaDeviceInfoBranch", "FromDeviceInfoTag", null);
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaDeviceInfoBranch", "FromDev" + tm, null);
 
 			transmitRequest(device, request);
 			
@@ -694,7 +923,8 @@
 			catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
 			catalogXml.append("</Query>\r\n");
 			
-			Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCatalogTag", null);
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCat" + tm, null);
 
 			transmitRequest(device, request, errorEvent);
 		} catch (SipException | ParseException | InvalidArgumentException e) {
@@ -728,7 +958,8 @@
 			recordInfoXml.append("<Type>all</Type>\r\n");
 			recordInfoXml.append("</Query>\r\n");
 			
-			Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "FromRecordInfoTag", null);
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "fromRec" + tm, null);
 
 			transmitRequest(device, request);
 		} catch (SipException | ParseException | InvalidArgumentException e) {
@@ -741,23 +972,86 @@
 	/**
 	 * 鏌ヨ鎶ヨ淇℃伅
 	 * 
-	 * @param device 瑙嗛璁惧
-	 */  
+	 * @param device		瑙嗛璁惧
+	 * @param startPriority	鎶ヨ璧峰绾у埆锛堝彲閫夛級
+	 * @param endPriority	鎶ヨ缁堟绾у埆锛堝彲閫夛級
+	 * @param alarmMethods	鎶ヨ鏂瑰紡鏉′欢锛堝彲閫夛級
+	 * @param alarmType		鎶ヨ绫诲瀷
+	 * @param startTime		鎶ヨ鍙戠敓璧峰鏃堕棿锛堝彲閫夛級
+	 * @param endTime		鎶ヨ鍙戠敓缁堟鏃堕棿锛堝彲閫夛級
+	 * @return				true = 鍛戒护鍙戦�佹垚鍔�
+	 */
 	@Override
-	public boolean alarmInfoQuery(Device device) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType,
+								 String startTime, String endTime, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Query>\r\n");
+			cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			if (!XmlUtil.isEmpty(startPriority)) {
+				cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
+			}
+			if (!XmlUtil.isEmpty(endPriority)) {
+				cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
+			}
+			if (!XmlUtil.isEmpty(alarmMethod)) {
+				cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
+			}
+			if (!XmlUtil.isEmpty(alarmType)) {
+				cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
+			}
+			if (!XmlUtil.isEmpty(startTime)) {
+				cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
+			}
+			if (!XmlUtil.isEmpty(endTime)) {
+				cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
+			}
+			cmdXml.append("</Query>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null);
+			transmitRequest(device, request, errorEvent);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
 	}
 
 	/**
 	 * 鏌ヨ璁惧閰嶇疆
 	 * 
-	 * @param device 瑙嗛璁惧
-	 */  
+	 * @param device 		瑙嗛璁惧
+	 * @param channelId		閫氶亾缂栫爜锛堝彲閫夛級
+	 * @param configType	閰嶇疆绫诲瀷锛�
+	 */
 	@Override
-	public boolean configQuery(Device device) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean deviceConfigQuery(Device device, String channelId, String configType,  SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Query>\r\n");
+			cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			if (XmlUtil.isEmpty(channelId)) {
+				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			} else {
+				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+			}
+			cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
+			cmdXml.append("</Query>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null);
+			transmitRequest(device, request, errorEvent);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
 	}
 
 	/**
@@ -766,9 +1060,28 @@
 	 * @param device 瑙嗛璁惧
 	 */  
 	@Override
-	public boolean presetQuery(Device device) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
+			cmdXml.append("<Query>\r\n");
+			cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			if (XmlUtil.isEmpty(channelId)) {
+				cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			} else {
+				cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
+			}
+			cmdXml.append("</Query>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null);
+			transmitRequest(device, request, errorEvent);
+			return true;
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		} 
 	}
 
 	/**
@@ -777,11 +1090,116 @@
 	 * @param device 瑙嗛璁惧
 	 */  
 	@Override
-	public boolean mobilePostitionQuery(Device device) {
-		// TODO Auto-generated method stub
-		return false;
+	public boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) {
+		try {
+			StringBuffer mobilePostitionXml = new StringBuffer(200);
+			mobilePostitionXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
+			mobilePostitionXml.append("<Query>\r\n");
+			mobilePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
+			mobilePostitionXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			mobilePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			mobilePostitionXml.append("<Interval>60</Interval>\r\n");
+			mobilePostitionXml.append("</Query>\r\n");
+			
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null);
+
+			transmitRequest(device, request, errorEvent);
+			
+		} catch (SipException | ParseException | InvalidArgumentException e) {
+			e.printStackTrace();
+			return false;
+		}
+		return true;
 	}
 
+	/**
+	 * 璁㈤槄銆佸彇娑堣闃呯Щ鍔ㄤ綅缃�
+	 * 
+	 * @param device	瑙嗛璁惧
+	 * @param expires	璁㈤槄瓒呮椂鏃堕棿
+	 * @param interval	涓婃姤鏃堕棿闂撮殧
+	 * @return			true = 鍛戒护鍙戦�佹垚鍔�
+	 */
+	public boolean mobilePositionSubscribe(Device device, int expires, int interval) {
+		try {
+			StringBuffer subscribePostitionXml = new StringBuffer(200);
+			subscribePostitionXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
+			subscribePostitionXml.append("<Query>\r\n");
+			subscribePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
+			subscribePostitionXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			subscribePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			if (expires > 0) {
+				subscribePostitionXml.append("<Interval>" + String.valueOf(interval) + "</Interval>\r\n");
+			}
+			subscribePostitionXml.append("</Query>\r\n");
+
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); //Position;id=" + tm.substring(tm.length() - 4));
+			transmitRequest(device, request);
+
+			return true;
+
+		} catch ( NumberFormatException | ParseException | InvalidArgumentException	| SipException e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 璁㈤槄銆佸彇娑堣闃呮姤璀︿俊鎭�
+	 * 
+	 * @param device		瑙嗛璁惧
+	 * @param expires		璁㈤槄杩囨湡鏃堕棿锛�0 = 鍙栨秷璁㈤槄锛�
+	 * @param startPriority	鎶ヨ璧峰绾у埆锛堝彲閫夛級
+	 * @param endPriority	鎶ヨ缁堟绾у埆锛堝彲閫夛級
+	 * @param alarmMethod	鎶ヨ鏂瑰紡鏉′欢锛堝彲閫夛級
+	 * @param alarmType		鎶ヨ绫诲瀷
+	 * @param startTime		鎶ヨ鍙戠敓璧峰鏃堕棿锛堝彲閫夛級
+	 * @param endTime		鎶ヨ鍙戠敓缁堟鏃堕棿锛堝彲閫夛級
+	 * @return				true = 鍛戒护鍙戦�佹垚鍔�
+	 */
+	public boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) {
+		try {
+			StringBuffer cmdXml = new StringBuffer(200);
+			cmdXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
+			cmdXml.append("<Query>\r\n");
+			cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
+			cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+			cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+			if (!XmlUtil.isEmpty(startPriority)) {
+				cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
+			}
+			if (!XmlUtil.isEmpty(endPriority)) {
+				cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
+			}
+			if (!XmlUtil.isEmpty(alarmMethod)) {
+				cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
+			}
+			if (!XmlUtil.isEmpty(alarmType)) {
+				cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
+			}
+			if (!XmlUtil.isEmpty(startTime)) {
+				cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
+			}
+			if (!XmlUtil.isEmpty(endTime)) {
+				cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
+			}
+			cmdXml.append("</Query>\r\n");
+
+			String tm = Long.toString(System.currentTimeMillis());
+			Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); 
+			transmitRequest(device, request);
+
+			return true;
+
+		} catch ( NumberFormatException | ParseException | InvalidArgumentException	| SipException e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+
 	private ClientTransaction transmitRequest(Device device, Request request) throws SipException {
 		return transmitRequest(device, request, null, null);
 	}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
index 22ed34a..968d01d 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
@@ -26,6 +26,10 @@
 import org.springframework.beans.factory.annotation.Autowired;
 
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
+import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
 import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
@@ -73,9 +77,12 @@
 	private static final String MESSAGE_RECORD_INFO = "RecordInfo";
 	private static final String MESSAGE_MEDIA_STATUS = "MediaStatus";
 	// private static final String MESSAGE_BROADCAST = "Broadcast";
-	// private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
-	// private static final String MESSAGE_MOBILE_POSITION = "MobilePosition";
+	private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
+	private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl";
+	private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig";
+	private static final String MESSAGE_MOBILE_POSITION = "MobilePosition";
 	// private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval";
+	private static final String MESSAGE_PRESET_QUERY = "PresetQuery";
 
 	/**
 	 * 澶勭悊MESSAGE璇锋眰
@@ -94,12 +101,22 @@
 				processMessageKeepAlive(evt);
 			} else if (MESSAGE_CONFIG_DOWNLOAD.equals(cmd)) {
 				logger.info("鎺ユ敹鍒癈onfigDownload娑堟伅");
+				processMessageConfigDownload(evt);
 			} else if (MESSAGE_CATALOG.equals(cmd)) {
 				logger.info("鎺ユ敹鍒癈atalog娑堟伅");
 				processMessageCatalogList(evt);
 			} else if (MESSAGE_DEVICE_INFO.equals(cmd)) {
 				logger.info("鎺ユ敹鍒癉eviceInfo娑堟伅");
 				processMessageDeviceInfo(evt);
+			} else if (MESSAGE_DEVICE_STATUS.equals(cmd)) {
+				logger.info("鎺ユ敹鍒癉eviceStatus娑堟伅");
+				processMessageDeviceStatus(evt);
+			} else if (MESSAGE_DEVICE_CONTROL.equals(cmd)) {
+				logger.info("鎺ユ敹鍒癉eviceControl娑堟伅");
+				processMessageDeviceControl(evt);
+			} else if (MESSAGE_DEVICE_CONFIG.equals(cmd)) {
+				logger.info("鎺ユ敹鍒癉eviceConfig娑堟伅");
+				processMessageDeviceConfig(evt);
 			} else if (MESSAGE_ALARM.equals(cmd)) {
 				logger.info("鎺ユ敹鍒癆larm娑堟伅");
 				processMessageAlarm(evt);
@@ -109,16 +126,236 @@
 			}else if (MESSAGE_MEDIA_STATUS.equals(cmd)) {
 				logger.info("鎺ユ敹鍒癕ediaStatus娑堟伅");
 				processMessageMediaStatus(evt);
+			} else if (MESSAGE_MOBILE_POSITION.equals(cmd)) {
+				logger.info("鎺ユ敹鍒癕obilePosition娑堟伅");
+				processMessageMobilePosition(evt);
+			} else if (MESSAGE_PRESET_QUERY.equals(cmd)) {
+				logger.info("鎺ユ敹鍒癙resetQuery娑堟伅");
+				processMessagePresetQuery(evt);
 			} else {
 				logger.info("鎺ユ敹鍒版秷鎭細" + cmd);
+				responseAck(evt);
 			}
-		} catch (DocumentException e) {
+		} catch (DocumentException | SipException |InvalidArgumentException | ParseException e) {
 			e.printStackTrace();
 		}
 	}
 
 	/**
-	 * 鏀跺埌deviceInfo璁惧淇℃伅璇锋眰 澶勭悊
+	 * 澶勭悊MobilePosition绉诲姩浣嶇疆娑堟伅
+	 * 
+	 * @param evt
+	 */
+	private void processMessageMobilePosition(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			MobilePosition mobilePosition = new MobilePosition();
+			Element deviceIdElement = rootElement.element("DeviceID");
+			String deviceId = deviceIdElement.getTextTrim().toString();
+			Device device = storager.queryVideoDevice(deviceId);
+			if (device != null) {
+				if (!StringUtils.isEmpty(device.getName())) {
+					mobilePosition.setDeviceName(device.getName());
+				}
+			}
+			mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID"));
+			mobilePosition.setTime(XmlUtil.getText(rootElement, "Time"));
+			mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
+			mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
+            if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) {
+				mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed")));
+			} else {
+				mobilePosition.setSpeed(0.0);
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) {
+				mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction")));
+			} else {
+				mobilePosition.setDirection(0.0);
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) {
+				mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude")));
+			} else {
+				mobilePosition.setAltitude(0.0);
+			}
+			mobilePosition.setReportSource("Mobile Position");
+			BaiduPoint bp = new BaiduPoint();
+			bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
+			logger.info("鐧惧害鍧愭爣锛�" + bp.getBdLng() + ", " + bp.getBdLat());
+			mobilePosition.setGeodeticSystem("BD-09");
+			mobilePosition.setCnLng(bp.getBdLng());
+			mobilePosition.setCnLat(bp.getBdLat());
+			if (!userSetup.getSavePositionHistory()) {
+				storager.clearMobilePositionsByDeviceId(deviceId);
+			}
+			storager.insertMobilePosition(mobilePosition);
+			//鍥炲 200 OK
+			responseAck(evt);
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 澶勭悊DeviceStatus璁惧鐘舵�丮essage
+	 * 
+	 * @param evt
+	 */
+	private void processMessageDeviceStatus(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			String deviceId = XmlUtil.getText(rootElement, "DeviceID");
+			// 妫�鏌ヨ澶囨槸鍚﹀瓨鍦紝 涓嶅瓨鍦ㄥ垯涓嶅洖澶�
+			if (storager.exists(deviceId)) {
+				// 鍥炲200 OK
+				responseAck(evt);
+				JSONObject json = new JSONObject();
+				XmlUtil.node2Json(rootElement, json);
+				if (logger.isDebugEnabled()) {
+					logger.debug(json.toJSONString());
+				}
+				RequestMessage msg = new RequestMessage();
+				msg.setDeviceId(deviceId);
+				msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS);
+				msg.setData(json);
+				deferredResultHolder.invokeResult(msg);
+
+				if (offLineDetector.isOnline(deviceId)) {
+					publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
+				} else {
+				}
+			}
+		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 澶勭悊DeviceControl璁惧鐘舵�丮essage
+	 * 
+	 * @param evt
+	 */
+	private void processMessageDeviceControl(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			String deviceId = XmlUtil.getText(rootElement, "DeviceID");
+			//String result = XmlUtil.getText(rootElement, "Result");
+			// 鍥炲200 OK
+			responseAck(evt);
+			if (rootElement.getName().equals("Response")) {//} !XmlUtil.isEmpty(result)) {
+				// 姝ゅ鏄鏈钩鍙板彂鍑篋eviceControl鎸囦护鐨勫簲绛�
+				JSONObject json = new JSONObject();
+				XmlUtil.node2Json(rootElement, json);
+				if (logger.isDebugEnabled()) {
+					logger.debug(json.toJSONString());
+				}
+				RequestMessage msg = new RequestMessage();
+				msg.setDeviceId(deviceId);
+				msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL);
+				msg.setData(json);
+				deferredResultHolder.invokeResult(msg);
+			} else {
+				// 姝ゅ鏄笂绾у彂鍑虹殑DeviceControl鎸囦护
+			}
+		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 澶勭悊DeviceConfig璁惧鐘舵�丮essage
+	 * 
+	 * @param evt
+	 */
+	private void processMessageDeviceConfig(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			String deviceId = XmlUtil.getText(rootElement, "DeviceID");
+			// 鍥炲200 OK
+			responseAck(evt);
+			if (rootElement.getName().equals("Response")) {
+					// 姝ゅ鏄鏈钩鍙板彂鍑篋eviceControl鎸囦护鐨勫簲绛�
+				JSONObject json = new JSONObject();
+				XmlUtil.node2Json(rootElement, json);
+				if (logger.isDebugEnabled()) {
+					logger.debug(json.toJSONString());
+				}
+				RequestMessage msg = new RequestMessage();
+				msg.setDeviceId(deviceId);
+				msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG);
+				msg.setData(json);
+				deferredResultHolder.invokeResult(msg);
+			} else {
+				// 姝ゅ鏄笂绾у彂鍑虹殑DeviceConfig鎸囦护
+			}
+		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 澶勭悊ConfigDownload璁惧鐘舵�丮essage
+	 * 
+	 * @param evt
+	 */
+	private void processMessageConfigDownload(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			String deviceId = XmlUtil.getText(rootElement, "DeviceID");
+			// 鍥炲200 OK
+			responseAck(evt);
+			if (rootElement.getName().equals("Response")) {
+					// 姝ゅ鏄鏈钩鍙板彂鍑篋eviceControl鎸囦护鐨勫簲绛�
+				JSONObject json = new JSONObject();
+				XmlUtil.node2Json(rootElement, json);
+				if (logger.isDebugEnabled()) {
+					logger.debug(json.toJSONString());
+				}
+				RequestMessage msg = new RequestMessage();
+				msg.setDeviceId(deviceId);
+				msg.setType(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD);
+				msg.setData(json);
+				deferredResultHolder.invokeResult(msg);
+			} else {
+				// 姝ゅ鏄笂绾у彂鍑虹殑DeviceConfig鎸囦护
+			}
+		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 澶勭悊PresetQuery棰勭疆浣嶅垪琛∕essage
+	 * 
+	 * @param evt
+	 */
+	private void processMessagePresetQuery(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			String deviceId = XmlUtil.getText(rootElement, "DeviceID");
+			// 鍥炲200 OK
+			responseAck(evt);
+			if (rootElement.getName().equals("Response")) {//   !XmlUtil.isEmpty(result)) {
+				// 姝ゅ鏄鏈钩鍙板彂鍑篋eviceControl鎸囦护鐨勫簲绛�
+				JSONObject json = new JSONObject();
+				XmlUtil.node2Json(rootElement, json);
+				if (logger.isDebugEnabled()) {
+					logger.debug(json.toJSONString());
+				}
+				RequestMessage msg = new RequestMessage();
+				msg.setDeviceId(deviceId);
+				msg.setType(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY);
+				msg.setData(json);
+				deferredResultHolder.invokeResult(msg);
+			} else {
+				// 姝ゅ鏄笂绾у彂鍑虹殑DeviceControl鎸囦护
+			}
+		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 澶勭悊DeviceInfo璁惧淇℃伅Message
 	 * 
 	 * @param evt
 	 */
@@ -126,7 +363,7 @@
 		try {
 			Element rootElement = getRootElement(evt);
 			Element deviceIdElement = rootElement.element("DeviceID");
-			String deviceId = deviceIdElement.getText().toString();
+			String deviceId = deviceIdElement.getTextTrim().toString();
 
 			Device device = storager.queryVideoDevice(deviceId);
 			if (device == null) {
@@ -228,80 +465,78 @@
 							deviceChannel.setStatus(0);
 						}
 
-						deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
-						deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
-						deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
-						deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
-						deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
-						deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
-						if (XmlUtil.getText(itemDevice, "Parental") == null || XmlUtil.getText(itemDevice, "Parental") == "") {
-							deviceChannel.setParental(0);
-						} else {
-							deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
-						}
-						deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
-						if (XmlUtil.getText(itemDevice, "SafetyWay") == null || XmlUtil.getText(itemDevice, "SafetyWay")== "") {
-							deviceChannel.setSafetyWay(0);
-						} else {
-							deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
-						}
-						if (XmlUtil.getText(itemDevice, "RegisterWay") == null || XmlUtil.getText(itemDevice, "RegisterWay") =="") {
-							deviceChannel.setRegisterWay(1);
-						} else {
-							deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
-						}
-						deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
-						if (XmlUtil.getText(itemDevice, "Certifiable") == null || XmlUtil.getText(itemDevice, "Certifiable") == "") {
-							deviceChannel.setCertifiable(0);
-						} else {
-							deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
-						}
-						if (XmlUtil.getText(itemDevice, "ErrCode") == null || XmlUtil.getText(itemDevice, "ErrCode") == "") {
-							deviceChannel.setErrCode(0);
-						} else {
-							deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
-						}
-						deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
-						deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
-						deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
-						if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") =="") {
-							deviceChannel.setPort(0);
-						} else {
-							deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
-						}
-						deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
-						if (XmlUtil.getText(itemDevice, "Longitude") == null || XmlUtil.getText(itemDevice, "Longitude") == "") {
-							deviceChannel.setLongitude(0.00);
-						} else {
-							deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
-						}
-						if (XmlUtil.getText(itemDevice, "Latitude") == null || XmlUtil.getText(itemDevice, "Latitude") =="") {
-							deviceChannel.setLatitude(0.00);
-						} else {
-							deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
-						}
-						if (XmlUtil.getText(itemDevice, "PTZType") == null || XmlUtil.getText(itemDevice, "PTZType") == "") {
-							deviceChannel.setPTZType(0);
-						} else {
-							deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
-						}
-						deviceChannel.setHasAudio(true); // 榛樿鍚湁闊抽锛屾挱鏀炬椂鍐嶆鏌ユ槸鍚︽湁闊抽鍙婃槸鍚AC
-						storager.updateChannel(device.getDeviceId(), deviceChannel);
+					deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
+					deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
+					deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
+					deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
+					deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
+					deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
+					if (XmlUtil.getText(itemDevice, "Parental") == null || XmlUtil.getText(itemDevice, "Parental") == "") {
+						deviceChannel.setParental(0);
+					} else {
+						deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
+					} 
+					deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
+					if (XmlUtil.getText(itemDevice, "SafetyWay") == null || XmlUtil.getText(itemDevice, "SafetyWay")== "") {
+						deviceChannel.setSafetyWay(0);
+					} else {
+						deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
 					}
+					if (XmlUtil.getText(itemDevice, "RegisterWay") == null || XmlUtil.getText(itemDevice, "RegisterWay") =="") {
+						deviceChannel.setRegisterWay(1);
+					} else {
+						deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
+					}
+					deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
+					if (XmlUtil.getText(itemDevice, "Certifiable") == null || XmlUtil.getText(itemDevice, "Certifiable") == "") {
+						deviceChannel.setCertifiable(0);
+					} else {
+						deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
+					}
+					if (XmlUtil.getText(itemDevice, "ErrCode") == null || XmlUtil.getText(itemDevice, "ErrCode") == "") {
+						deviceChannel.setErrCode(0);
+					} else {
+						deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
+					}
+					deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
+					deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
+					deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
+					if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") =="") {
+						deviceChannel.setPort(0);
+					} else {
+						deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
+					}
+					deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
+					if (XmlUtil.getText(itemDevice, "Longitude") == null || XmlUtil.getText(itemDevice, "Longitude") == "") {
+						deviceChannel.setLongitude(0.00);
+					} else {
+						deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
+					}
+					if (XmlUtil.getText(itemDevice, "Latitude") == null || XmlUtil.getText(itemDevice, "Latitude") =="") {
+						deviceChannel.setLatitude(0.00);
+					} else {
+						deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
+					}
+					if (XmlUtil.getText(itemDevice, "PTZType") == null || XmlUtil.getText(itemDevice, "PTZType") == "") {
+						deviceChannel.setPTZType(0);
+					} else {
+						deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
+					}
+					deviceChannel.setHasAudio(true); // 榛樿鍚湁闊抽锛屾挱鏀炬椂鍐嶆鏌ユ槸鍚︽湁闊抽鍙婃槸鍚AC
+					storager.updateChannel(device.getDeviceId(), deviceChannel);
+				}
 
-					RequestMessage msg = new RequestMessage();
-					msg.setDeviceId(deviceId);
-					msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG);
-					msg.setData(device);
-					deferredResultHolder.invokeResult(msg);
-					// 鍥炲200 OK
-					responseAck(evt);
-					if (offLineDetector.isOnline(deviceId)) {
-						publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
-					}
+				RequestMessage msg = new RequestMessage();
+				msg.setDeviceId(deviceId);
+				msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG);
+				msg.setData(device);
+				deferredResultHolder.invokeResult(msg);
+				// 鍥炲200 OK
+				responseAck(evt);
+				if (offLineDetector.isOnline(deviceId)) {
+					publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
 				}
 			}
-
 		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
 			e.printStackTrace();
 		}
@@ -324,41 +559,19 @@
 				// storager.queryChannel(deviceId)
 				return;
 			}
-
-			DeviceAlarm deviceAlarm = new DeviceAlarm();
-			deviceAlarm.setDeviceId(deviceId);
-			deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
-			deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
-			deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
-			if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
-				deviceAlarm.setAlarmDescription("");
-			} else {
-				deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription"));
+			device.setName(XmlUtil.getText(rootElement, "DeviceName"));
+			device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
+			device.setModel(XmlUtil.getText(rootElement, "Model"));
+			device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
+			if (StringUtils.isEmpty(device.getStreamMode())) {
+				device.setStreamMode("UDP");
 			}
-			if (XmlUtil.getText(rootElement, "Longitude") == null || XmlUtil.getText(rootElement, "Longitude") == "") {
-				deviceAlarm.setLongitude(0.00);
-			} else {
-				deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
-			}
-			if (XmlUtil.getText(rootElement, "Latitude") == null || XmlUtil.getText(rootElement, "Latitude") =="") {
-				deviceAlarm.setLatitude(0.00);
-			} else {
-				deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
-			}
-
-			// device.setName(XmlUtil.getText(rootElement, "DeviceName"));
-			// device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
-			// device.setModel(XmlUtil.getText(rootElement, "Model"));
-			// device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
-			// if (StringUtils.isEmpty(device.getStreamMode())) {
-			// 	device.setStreamMode("UDP");
-			// }
-			// storager.updateDevice(device);
+			storager.updateDevice(device);
 			//cmder.catalogQuery(device, null);
 			// 鍥炲200 OK
 			responseAck(evt);
 			if (offLineDetector.isOnline(deviceId)) {
-				publisher.deviceAlarmEventPublish(deviceAlarm);
+				publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
 			}
 		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
 			// } catch (DocumentException e) {
@@ -384,14 +597,13 @@
 				} else {
 				}
 			}
-
 		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
 			e.printStackTrace();
 		}
 	}
 
 	/***
-	 * 鏀跺埌catalog璁惧鐩綍鍒楄〃璇锋眰 澶勭悊 TODO 杩囨湡鏃堕棿鏆傛椂鍐欐180绉掞紝鍚庣画涓嶥eferredResult瓒呮椂鏃堕棿淇濇寔涓�鑷�
+	 * 澶勭悊RecordInfo璁惧褰曞儚鍒楄〃Message璇锋眰 TODO 杩囨湡鏃堕棿鏆傛椂鍐欐180绉掞紝鍚庣画涓嶥eferredResult瓒呮椂鏃堕棿淇濇寔涓�鑷�
 	 * 
 	 * @param evt
 	 */
@@ -415,8 +627,11 @@
 			Element recordListElement = rootElement.element("RecordList");
 			if (recordListElement == null || recordInfo.getSumNum() == 0) {
 				logger.info("鏃犲綍鍍忔暟鎹�");
-				// responseAck(evt);
-				// return;
+				RequestMessage msg = new RequestMessage();
+				msg.setDeviceId(deviceId);
+				msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
+				msg.setData(recordInfo);
+				deferredResultHolder.invokeResult(msg);
 			} else {
 				Iterator<Element> recordListIterator = recordListElement.elementIterator();
 				List<RecordItem> recordList = new ArrayList<RecordItem>();
@@ -446,53 +661,73 @@
 						record.setRecorderId(XmlUtil.getText(itemRecord, "RecorderID"));
 						recordList.add(record);
 					}
-					// recordList.sort(Comparator.naturalOrder());
 					recordInfo.setRecordList(recordList);
 				}
 
-				// 瀛樺湪褰曞儚涓斿鏋滃綋鍓嶅綍鍍忔槑缁嗕釜鏁板皬浜庢�绘潯鏁帮紝璇存槑鎷嗗寘杩斿洖锛岄渶瑕佺粍瑁咃紝鏆備笉杩斿洖
-				if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) {
-					// 涓洪槻姝㈣繛缁姹傝璁惧鐨勫綍鍍忔暟鎹紝杩斿洖鏁版嵁閿欎贡锛岀壒澧炲姞sn杩涜鍖哄垎
-					String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
-
-					redis.set(cacheKey + "_" + uuid, recordList, 90);
-					List<Object> cacheKeys = redis.scan(cacheKey + "_*");
-					List<RecordItem> totalRecordList = new ArrayList<RecordItem>();
-					for (int i = 0; i < cacheKeys.size(); i++) {
-						totalRecordList.addAll((List<RecordItem>) redis.get(cacheKeys.get(i).toString()));
+				// 鏀圭敤鍗曠嫭绾跨▼缁熻宸茶幏鍙栧綍鍍忔枃浠舵暟閲忥紝閬垮厤澶氬寘骞惰鍒嗗埆缁熻涓嶅畬鏁寸殑闂
+				String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
+				redis.set(cacheKey + "_" + uuid, recordList, 90);
+				if (!threadNameList.contains(cacheKey)) {
+					threadNameList.add(cacheKey);
+					CheckForAllRecordsThread chk = new CheckForAllRecordsThread(cacheKey, recordInfo);
+					chk.setName(cacheKey);
+					chk.setDeferredResultHolder(deferredResultHolder);
+					chk.setRedis(redis);
+					chk.setLogger(logger);
+					chk.start();
+					if (logger.isDebugEnabled()) {
+						logger.debug("Start Thread " + cacheKey + ".");
 					}
-					if (totalRecordList.size() < recordInfo.getSumNum()) {
-						logger.info("宸茶幏鍙�" + totalRecordList.size() + "椤瑰綍鍍忔暟鎹紝鍏�" + recordInfo.getSumNum() + "椤�");
-						return;
-					}
-					logger.info("褰曞儚鏁版嵁宸插叏閮ㄨ幏鍙栵紝鍏�" + recordInfo.getSumNum() + "椤�");
-					recordInfo.setRecordList(totalRecordList);
-					for (int i = 0; i < cacheKeys.size(); i++) {
-						redis.del(cacheKeys.get(i).toString());
+				} else {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Thread " + cacheKey + " already started.");
 					}
 				}
-				// 鑷劧椤哄簭鎺掑簭, 鍏冪礌杩涜鍗囧簭鎺掑垪
-				recordInfo.getRecordList().sort(Comparator.naturalOrder());
+
+				// 瀛樺湪褰曞儚涓斿鏋滃綋鍓嶅綍鍍忔槑缁嗕釜鏁板皬浜庢�绘潯鏁帮紝璇存槑鎷嗗寘杩斿洖锛岄渶瑕佺粍瑁咃紝鏆備笉杩斿洖
+				// if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) {
+				// 	// 涓洪槻姝㈣繛缁姹傝璁惧鐨勫綍鍍忔暟鎹紝杩斿洖鏁版嵁閿欎贡锛岀壒澧炲姞sn杩涜鍖哄垎
+				// 	String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
+
+				// 	redis.set(cacheKey + "_" + uuid, recordList, 90);
+				// 	List<Object> cacheKeys = redis.scan(cacheKey + "_*");
+				// 	List<RecordItem> totalRecordList = new ArrayList<RecordItem>();
+				// 	for (int i = 0; i < cacheKeys.size(); i++) {
+				// 		totalRecordList.addAll((List<RecordItem>) redis.get(cacheKeys.get(i).toString()));
+				// 	}
+				// 	if (totalRecordList.size() < recordInfo.getSumNum()) {
+				// 		logger.info("宸茶幏鍙�" + totalRecordList.size() + "椤瑰綍鍍忔暟鎹紝鍏�" + recordInfo.getSumNum() + "椤�");
+				// 		return;
+				// 	}
+				// 	logger.info("褰曞儚鏁版嵁宸插叏閮ㄨ幏鍙栵紝鍏�" + recordInfo.getSumNum() + "椤�");
+				// 	recordInfo.setRecordList(totalRecordList);
+				// 	for (int i = 0; i < cacheKeys.size(); i++) {
+				// 		redis.del(cacheKeys.get(i).toString());
+				// 	}
+				// }
+				// // 鑷劧椤哄簭鎺掑簭, 鍏冪礌杩涜鍗囧簭鎺掑垪
+				// recordInfo.getRecordList().sort(Comparator.naturalOrder());
 			}
 			// 璧板埌杩欓噷锛屾湁浠ヤ笅鍙兘锛�1銆佹病鏈夊綍鍍忎俊鎭�,绗竴娆℃敹鍒皉ecordinfo鐨勬秷鎭嵆杩斿洖鍝嶅簲鏁版嵁锛屾棤redis鎿嶄綔
 			// 2銆佹湁褰曞儚鏁版嵁锛屼笖绗竴娆″嵆鏀跺埌瀹屾暣鏁版嵁锛岃繑鍥炲搷搴旀暟鎹紝鏃爎edis鎿嶄綔
 			// 3銆佹湁褰曞儚鏁版嵁锛屽湪瓒呮椂鏃堕棿鍐呮敹鍒板娆″寘缁勮鍚庢暟閲忚冻澶燂紝杩斿洖鏁版嵁
 
-			// 瀵硅褰曡繘琛屾帓搴�
-			RequestMessage msg = new RequestMessage();
-			msg.setDeviceId(deviceId);
-			msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
-			// // 鑷劧椤哄簭鎺掑簭, 鍏冪礌杩涜鍗囧簭鎺掑垪
-			// recordInfo.getRecordList().sort(Comparator.naturalOrder());
-			msg.setData(recordInfo);
-			deferredResultHolder.invokeResult(msg);
-			logger.info("澶勭悊瀹屾垚锛岃繑鍥炵粨鏋�");
+			// RequestMessage msg = new RequestMessage();
+			// msg.setDeviceId(deviceId);
+			// msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
+			// msg.setData(recordInfo);
+			// deferredResultHolder.invokeResult(msg);
+			// logger.info("澶勭悊瀹屾垚锛岃繑鍥炵粨鏋�");
 		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
 			e.printStackTrace();
 		}
 	}
 
-
+	/**
+	 * 鏀跺埌MediaStatus娑堟伅澶勭悊
+ 	 *
+ 	 * @param evt
+ 	 */
 	private void processMessageMediaStatus(RequestEvent evt){
 		try {
 			// 鍥炲200 OK
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java
new file mode 100644
index 0000000..897b6ab
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java
@@ -0,0 +1,384 @@
+package com.genersoft.iot.vmp.gb28181.transmit.request.impl;
+
+import java.io.ByteArrayInputStream;
+import java.text.ParseException;
+import java.util.Iterator;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
+import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.utils.GpsUtil;
+import com.genersoft.iot.vmp.utils.SpringBeanFactory;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+/**
+ * @Description: Notify璇锋眰澶勭悊鍣�
+ * @author: lawrencehj
+ * @date: 2021骞�1鏈�27鏃�
+ */
+
+public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
+    
+	private UserSetup userSetup = (UserSetup) SpringBeanFactory.getBean("userSetup");
+
+    private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
+
+	private IVideoManagerStorager storager;
+
+	private IRedisCatchStorage redisCatchStorage;
+
+	private EventPublisher publisher;
+
+	private DeviceOffLineDetector offLineDetector;
+
+	private static final String NOTIFY_CATALOG = "Catalog";
+	private static final String NOTIFY_ALARM = "Alarm";
+	private static final String NOTIFY_MOBILE_POSITION = "MobilePosition";
+
+	@Override
+	public void process(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			String cmd = XmlUtil.getText(rootElement, "CmdType");
+
+			if (NOTIFY_CATALOG.equals(cmd)) {
+				logger.info("鎺ユ敹鍒癈atalog閫氱煡");
+				processNotifyCatalogList(evt);
+			} else if (NOTIFY_ALARM.equals(cmd)) {
+				logger.info("鎺ユ敹鍒癆larm閫氱煡");
+				processNotifyAlarm(evt);
+			} else if (NOTIFY_MOBILE_POSITION.equals(cmd)) {
+				logger.info("鎺ユ敹鍒癕obilePosition閫氱煡");
+				processNotifyMobilePosition(evt);
+			} else {
+				logger.info("鎺ユ敹鍒版秷鎭細" + cmd);
+				response200Ok(evt);
+			}
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 澶勭悊MobilePosition绉诲姩浣嶇疆Notify
+	 * 
+	 * @param evt
+	 */
+	private void processNotifyMobilePosition(RequestEvent evt) {
+		try {
+			// 鍥炲 200 OK
+			Element rootElement = getRootElement(evt);
+			MobilePosition mobilePosition = new MobilePosition();
+			Element deviceIdElement = rootElement.element("DeviceID");
+			String deviceId = deviceIdElement.getTextTrim().toString();
+			Device device = storager.queryVideoDevice(deviceId);
+			if (device != null) {
+				if (!StringUtils.isEmpty(device.getName())) {
+					mobilePosition.setDeviceName(device.getName());
+				}
+			}
+			mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID"));
+			mobilePosition.setTime(XmlUtil.getText(rootElement, "Time"));
+			mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
+			mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) {
+				mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed")));
+			} else {
+				mobilePosition.setSpeed(0.0);
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) {
+				mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction")));
+			} else {
+				mobilePosition.setDirection(0.0);
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) {
+				mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude")));
+			} else {
+				mobilePosition.setAltitude(0.0);
+			}
+			mobilePosition.setReportSource("Mobile Position");
+			BaiduPoint bp = new BaiduPoint();
+			bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
+			logger.info("鐧惧害鍧愭爣锛�" + bp.getBdLng() + ", " + bp.getBdLat());
+			mobilePosition.setGeodeticSystem("BD-09");
+			mobilePosition.setCnLng(bp.getBdLng());
+			mobilePosition.setCnLat(bp.getBdLat());
+			if (!userSetup.getSavePositionHistory()) {
+				storager.clearMobilePositionsByDeviceId(deviceId);
+			}
+			storager.insertMobilePosition(mobilePosition);
+			response200Ok(evt);
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/***
+	 * 澶勭悊alarm璁惧鎶ヨNotify
+	 * 
+	 * @param evt
+	 */
+	private void processNotifyAlarm(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			Element deviceIdElement = rootElement.element("DeviceID");
+			String deviceId = deviceIdElement.getText().toString();
+
+			Device device = storager.queryVideoDevice(deviceId);
+			if (device == null) {
+				return;
+			}
+			DeviceAlarm deviceAlarm = new DeviceAlarm();
+			deviceAlarm.setDeviceId(deviceId);
+			deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
+			deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
+			deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
+			if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
+				deviceAlarm.setAlarmDescription("");
+			} else {
+				deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription"));
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) {
+				deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
+			} else {
+				deviceAlarm.setLongitude(0.00);
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) {
+				deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
+			} else {
+				deviceAlarm.setLatitude(0.00);
+			}
+
+			if (deviceAlarm.getAlarmMethod().equals("4")) {
+				MobilePosition mobilePosition = new MobilePosition();
+				mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
+				mobilePosition.setTime(deviceAlarm.getAlarmTime());
+				mobilePosition.setLongitude(deviceAlarm.getLongitude());
+				mobilePosition.setLatitude(deviceAlarm.getLatitude());
+				mobilePosition.setReportSource("GPS Alarm");
+				BaiduPoint bp = new BaiduPoint();
+				bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
+				logger.info("鐧惧害鍧愭爣锛�" + bp.getBdLng() + ", " + bp.getBdLat());
+				mobilePosition.setGeodeticSystem("BD-09");
+				mobilePosition.setCnLng(bp.getBdLng());
+				mobilePosition.setCnLat(bp.getBdLat());
+				if (!userSetup.getSavePositionHistory()) {
+					storager.clearMobilePositionsByDeviceId(deviceId);
+				}
+				storager.insertMobilePosition(mobilePosition);
+			}
+			// TODO: 闇�瑕佸疄鐜板瓨鍌ㄦ姤璀︿俊鎭�佹姤璀﹀垎绫�
+
+			// 鍥炲200 OK
+			response200Ok(evt);
+			if (offLineDetector.isOnline(deviceId)) {
+				publisher.deviceAlarmEventPublish(deviceAlarm);
+			}
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/***
+	 * 澶勭悊catalog璁惧鐩綍鍒楄〃Notify
+	 * 
+	 * @param evt
+	 */
+	private void processNotifyCatalogList(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			Element deviceIdElement = rootElement.element("DeviceID");
+			String deviceId = deviceIdElement.getText();
+			Element deviceListElement = rootElement.element("DeviceList");
+			if (deviceListElement == null) {
+				return;
+			}
+			Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
+			if (deviceListIterator != null) {
+				Device device = storager.queryVideoDevice(deviceId);
+				if (device == null) {
+					return;
+				}
+				// 閬嶅巻DeviceList
+				while (deviceListIterator.hasNext()) {
+					Element itemDevice = deviceListIterator.next();
+					Element channelDeviceElement = itemDevice.element("DeviceID");
+					if (channelDeviceElement == null) {
+						continue;
+					}
+					String channelDeviceId = channelDeviceElement.getTextTrim();
+					Element channdelNameElement = itemDevice.element("Name");
+					String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
+					Element statusElement = itemDevice.element("Status");
+					String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
+					DeviceChannel deviceChannel = new DeviceChannel();
+					deviceChannel.setName(channelName);
+					deviceChannel.setChannelId(channelDeviceId);
+					// ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR鐨勫吋瀹规�у鐞�
+					if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
+						deviceChannel.setStatus(1);
+					}
+					if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
+						deviceChannel.setStatus(0);
+					}
+
+					deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
+					deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
+					deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
+					deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
+					deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
+					deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
+					if (XmlUtil.getText(itemDevice, "Parental") == null
+							|| XmlUtil.getText(itemDevice, "Parental") == "") {
+						deviceChannel.setParental(0);
+					} else {
+						deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
+					}
+					deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
+					if (XmlUtil.getText(itemDevice, "SafetyWay") == null
+							|| XmlUtil.getText(itemDevice, "SafetyWay") == "") {
+						deviceChannel.setSafetyWay(0);
+					} else {
+						deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
+					}
+					if (XmlUtil.getText(itemDevice, "RegisterWay") == null
+							|| XmlUtil.getText(itemDevice, "RegisterWay") == "") {
+						deviceChannel.setRegisterWay(1);
+					} else {
+						deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
+					}
+					deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
+					if (XmlUtil.getText(itemDevice, "Certifiable") == null
+							|| XmlUtil.getText(itemDevice, "Certifiable") == "") {
+						deviceChannel.setCertifiable(0);
+					} else {
+						deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
+					}
+					if (XmlUtil.getText(itemDevice, "ErrCode") == null
+							|| XmlUtil.getText(itemDevice, "ErrCode") == "") {
+						deviceChannel.setErrCode(0);
+					} else {
+						deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
+					}
+					deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
+					deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
+					deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
+					if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") {
+						deviceChannel.setPort(0);
+					} else {
+						deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
+					}
+					deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
+					if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {
+						deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
+					} else {
+						deviceChannel.setLongitude(0.00);
+					}
+					if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) {
+						deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
+					} else {
+						deviceChannel.setLatitude(0.00);
+					}
+					if (XmlUtil.getText(itemDevice, "PTZType") == null
+							|| XmlUtil.getText(itemDevice, "PTZType") == "") {
+						deviceChannel.setPTZType(0);
+					} else {
+						deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
+					}
+					deviceChannel.setHasAudio(true); // 榛樿鍚湁闊抽锛屾挱鏀炬椂鍐嶆鏌ユ槸鍚︽湁闊抽鍙婃槸鍚AC
+					storager.updateChannel(device.getDeviceId(), deviceChannel);
+				}
+
+				// RequestMessage msg = new RequestMessage();
+				// msg.setDeviceId(deviceId);
+				// msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG);
+				// msg.setData(device);
+				// deferredResultHolder.invokeResult(msg);
+				// 鍥炲200 OK
+				response200Ok(evt);
+				if (offLineDetector.isOnline(deviceId)) {
+					publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
+				}
+			}
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/***
+	 * 鍥炲200 OK
+	 * 
+	 * @param evt
+	 * @throws SipException
+	 * @throws InvalidArgumentException
+	 * @throws ParseException
+	 */
+	private void response200Ok(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
+		getServerTransaction(evt).sendResponse(response);
+	}
+
+	private Element getRootElement(RequestEvent evt) throws DocumentException {
+		Request request = evt.getRequest();
+		SAXReader reader = new SAXReader();
+		reader.setEncoding("gbk");
+		Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
+		return xml.getRootElement();
+	}
+
+	public void setCmder(SIPCommander cmder) {
+	}
+
+	public void setStorager(IVideoManagerStorager storager) {
+		this.storager = storager;
+	}
+
+	public void setPublisher(EventPublisher publisher) {
+		this.publisher = publisher;
+	}
+
+	public void setRedis(RedisUtil redis) {
+	}
+
+	public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) {
+	}
+
+	public void setOffLineDetector(DeviceOffLineDetector offLineDetector) {
+		this.offLineDetector = offLineDetector;
+	}
+
+	public IRedisCatchStorage getRedisCatchStorage() {
+		return redisCatchStorage;
+	}
+
+	public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
+		this.redisCatchStorage = redisCatchStorage;
+	}
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java
index 63da991..028835b 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java
@@ -21,7 +21,7 @@
 	 */  
 	@Override
 	public void process(RequestEvent evt) {
-		System.out.println("no support the method! Method:" + evt.getRequest().getMethod());
+		System.out.println("Unsupported the method: " + evt.getRequest().getMethod());
 	}
 
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
index 18b5de1..22d54a7 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
@@ -145,7 +145,7 @@
 			// 涓嬪彂catelog鏌ヨ鐩綍
 			if (registerFlag == 1 && device != null) {
 				logger.info("娉ㄥ唽鎴愬姛! deviceId:" + device.getDeviceId());
-				boolean exists = storager.exists(device.getDeviceId());
+				// boolean exists = storager.exists(device.getDeviceId());
 				device.setRegisterTimeMillis(System.currentTimeMillis());
 				storager.updateDevice(device);
 				publisher.onlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_ONLINE_REGISTER);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
index 93f533f..d67267a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
@@ -12,14 +12,12 @@
 import javax.sip.message.Request;
 import javax.sip.message.Response;
 
-import gov.nist.javax.sip.header.CSeq;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+// import org.slf4j.Logger;
+// import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.gb28181.SipLayer;
-import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory;
 import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
 
 
@@ -31,7 +29,7 @@
 @Component
 public class InviteResponseProcessor implements ISIPResponseProcessor {
 
-	private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
+	// private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
 
 	/**
 	 * 澶勭悊invite鍝嶅簲
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java
new file mode 100644
index 0000000..4a399b8
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java
@@ -0,0 +1,41 @@
+package com.genersoft.iot.vmp.gb28181.utils;
+
+/**
+ * 鏁板�兼牸寮忓垽鏂拰澶勭悊
+ * @author lawrencehj
+ * @date 2021骞�1鏈�27鏃�
+ */
+public class NumericUtil {
+
+    /**
+     * 鍒ゆ柇鏄惁Double鏍煎紡
+     * @param str
+     * @return true/false
+     */
+    public static boolean isDouble(String str) {
+        try { 
+            Double num2 = Double.valueOf(str); 
+            System.out.println(num2 + " is a valid numeric string!"); 
+            return true;
+        } catch (Exception e) { 
+            System.out.println(str + " is an invalid numeric string!"); 
+            return false;
+        }
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁Double鏍煎紡
+     * @param str
+     * @return true/false
+     */
+    public static boolean isInteger(String str) {
+        try { 
+            int num2 = Integer.valueOf(str); 
+            System.out.println(num2 + " is an integer!"); 
+            return true;
+        } catch (Exception e) { 
+            System.out.println(str + " is not an integer!"); 
+            return false;
+        }
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
index e0d776c..c82ba60 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
@@ -7,6 +7,9 @@
 import java.util.List;
 import java.util.Map;
 
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
 import org.dom4j.Attribute;
 import org.dom4j.Document;
 import org.dom4j.DocumentException;
@@ -20,8 +23,7 @@
  * 
  * 
  */
-public class XmlUtil
-{
+public class XmlUtil {
     /**
      * 鏃ュ織鏈嶅姟
      */
@@ -30,22 +32,18 @@
     /**
      * 瑙f瀽XML涓篋ocument瀵硅薄
      * 
-     * @param xml
-     *            琚В鏋愮殑XMl
+     * @param xml 琚В鏋愮殑XMl
+     * 
      * @return Document
      */
-    public static Element parseXml(String xml)
-    {
+    public static Element parseXml(String xml) {
         Document document = null;
         //
         StringReader sr = new StringReader(xml);
         SAXReader saxReader = new SAXReader();
-        try
-        {
+        try {
             document = saxReader.read(sr);
-        }
-        catch (DocumentException e)
-        {
+        } catch (DocumentException e) {
             LOG.error("瑙f瀽澶辫触", e);
         }
         return null == document ? null : document.getRootElement();
@@ -54,16 +52,12 @@
     /**
      * 鑾峰彇element瀵硅薄鐨則ext鐨勫��
      * 
-     * @param em
-     *            鑺傜偣鐨勫璞�
-     * @param tag
-     *            鑺傜偣鐨則ag
+     * @param em  鑺傜偣鐨勫璞�
+     * @param tag 鑺傜偣鐨則ag
      * @return 鑺傜偣
      */
-    public static String getText(Element em, String tag)
-    {
-        if (null == em)
-        {
+    public static String getText(Element em, String tag) {
+        if (null == em) {
             return null;
         }
         Element e = em.element(tag);
@@ -74,16 +68,12 @@
     /**
      * 閫掑綊瑙f瀽xml鑺傜偣锛岄�傜敤浜� 澶氳妭鐐规暟鎹�
      * 
-     * @param node
-     *            node
-     * @param nodeName
-     *            nodeName
+     * @param node     node
+     * @param nodeName nodeName
      * @return List<Map<String, Object>>
      */
-    public static List<Map<String, Object>> listNodes(Element node, String nodeName)
-    {
-        if (null == node)
-        {
+    public static List<Map<String, Object>> listNodes(Element node, String nodeName) {
+        if (null == node) {
             return null;
         }
         // 鍒濆鍖栬繑鍥�
@@ -93,12 +83,9 @@
 
         Map<String, Object> map = null;
         // 閬嶅巻灞炴�ц妭鐐�
-        for (Attribute attribute : list)
-        {
-            if (nodeName.equals(node.getName()))
-            {
-                if (null == map)
-                {
+        for (Attribute attribute : list) {
+            if (nodeName.equals(node.getName())) {
+                if (null == map) {
                     map = new HashMap<String, Object>();
                     listMap.add(map);
                 }
@@ -110,12 +97,74 @@
         // 閬嶅巻褰撳墠鑺傜偣涓嬬殑鎵�鏈夎妭鐐� 锛宯odeName 瑕佽В鏋愮殑鑺傜偣鍚嶇О
         // 浣跨敤閫掑綊
         Iterator<Element> iterator = node.elementIterator();
-        while (iterator.hasNext())
-        {
+        while (iterator.hasNext()) {
             Element e = iterator.next();
             listMap.addAll(listNodes(e, nodeName));
         }
         return listMap;
     }
 
+    /**
+     * xml杞琷son
+     * 
+     * @param element
+     * @param json
+     */
+    public static void node2Json(Element element, JSONObject json) {
+        // 濡傛灉鏄睘鎬�
+        for (Object o : element.attributes()) {
+            Attribute attr = (Attribute) o;
+            if (!isEmpty(attr.getValue())) {
+                json.put("@" + attr.getName(), attr.getValue());
+            }
+        }
+        List<Element> chdEl = element.elements();
+        if (chdEl.isEmpty() && !isEmpty(element.getText())) {// 濡傛灉娌℃湁瀛愬厓绱�,鍙湁涓�涓��
+            json.put(element.getName(), element.getText());
+        }
+
+        for (Element e : chdEl) {   // 鏈夊瓙鍏冪礌
+            if (!e.elements().isEmpty()) {  // 瀛愬厓绱犱篃鏈夊瓙鍏冪礌
+                JSONObject chdjson = new JSONObject();
+                node2Json(e, chdjson);
+                Object o = json.get(e.getName());
+                if (o != null) {
+                    JSONArray jsona = null;
+                    if (o instanceof JSONObject) {  // 濡傛灉姝ゅ厓绱犲凡瀛樺湪,鍒欒浆涓簀sonArray
+                        JSONObject jsono = (JSONObject) o;
+                        json.remove(e.getName());
+                        jsona = new JSONArray();
+                        jsona.add(jsono);
+                        jsona.add(chdjson);
+                    }
+                    if (o instanceof JSONArray) {
+                        jsona = (JSONArray) o;
+                        jsona.add(chdjson);
+                    }
+                    json.put(e.getName(), jsona);
+                } else {
+                    if (!chdjson.isEmpty()) {
+                        json.put(e.getName(), chdjson);
+                    }
+                }
+            } else { // 瀛愬厓绱犳病鏈夊瓙鍏冪礌
+                for (Object o : element.attributes()) {
+                    Attribute attr = (Attribute) o;
+                    if (!isEmpty(attr.getValue())) {
+                        json.put("@" + attr.getName(), attr.getValue());
+                    }
+                }
+                if (!e.getText().isEmpty()) {
+                    json.put(e.getName(), e.getText());
+                }
+            }
+        }
+    }
+
+    public static boolean isEmpty(String str) {
+        if (str == null || str.trim().isEmpty() || "null".equals(str)) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java
index 9daef23..d5c218c 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java
@@ -1,34 +1,28 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.conf.MediaServerConfig;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+// import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+// import org.slf4j.Logger;
+// import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpRequest;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.RestTemplate;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.util.Enumeration;
 
 @RestController
 @RequestMapping("/zlm")
 public class ZLMHTTPProxyController {
 
 
-    private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class);
+    // private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class);
 
-    @Autowired
-    private IVideoManagerStorager storager;
+    // @Autowired
+    // private IVideoManagerStorager storager;
 
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
index 9fc64c7..70026a8 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -82,10 +82,10 @@
 	@ResponseBody
 	@PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8")
 	public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){
-
-		logger.debug("ZLM HOOK on_flow_report API璋冪敤锛屽弬鏁帮細" + json.toString());
-		// TODO Auto-generated method stub
-
+		
+		if (logger.isDebugEnabled()) {
+			logger.debug("ZLM HOOK on_flow_report API璋冪敤锛屽弬鏁帮細" + json.toString());
+		}
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
@@ -103,8 +103,6 @@
 		if (logger.isDebugEnabled()) {
 			logger.debug("ZLM HOOK on_http_access API 璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
-		// TODO Auto-generated method stub
-		
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("err", "");
@@ -124,8 +122,6 @@
 		if (logger.isDebugEnabled()) {
 			logger.debug("ZLM HOOK on_play API璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
-		// TODO Auto-generated method stub
-		
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
@@ -143,15 +139,10 @@
 		if (logger.isDebugEnabled()) {
 			logger.debug("ZLM HOOK on_publish API璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
-		String app = json.getString("app");
-		String streamId = json.getString("id");
 
 		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
 		if (subscribe != null) subscribe.response(json);
 
-
-		// TODO Auto-generated method stub
-		
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
@@ -172,8 +163,6 @@
 		if (logger.isDebugEnabled()) {
 			logger.debug("ZLM HOOK on_record_mp4 API璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
-		// TODO Auto-generated method stub
-		
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
@@ -191,8 +180,6 @@
 		if (logger.isDebugEnabled()) {
 			logger.debug("ZLM HOOK on_rtsp_realm API璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
-		// TODO Auto-generated method stub
-		
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("realm", "");
@@ -211,8 +198,6 @@
 		if (logger.isDebugEnabled()) {
 			logger.debug("ZLM HOOK on_rtsp_auth API璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
-		// TODO Auto-generated method stub
-		
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("encrypted", false);
@@ -269,8 +254,6 @@
 				redisCatchStorage.stopPlayback(streamInfo);
 			}
 		}
-
-
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
@@ -318,8 +301,6 @@
 		if (logger.isDebugEnabled()) {
 			logger.debug("ZLM HOOK on_stream_not_found API璋冪敤锛屽弬鏁帮細" + json.toString());
 		}
-		// TODO Auto-generated method stub
-
 		if (autoApplyPlay) {
 			String app = json.getString("app");
 			String streamId = json.getString("stream");
@@ -369,8 +350,6 @@
 		mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp);
 		mediaServerConfig.setLocalIP(mediaIp);
 		redisCatchStorage.updateMediaInfo(mediaServerConfig);
-		// TODO Auto-generated method stub
-		
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
index 0c00b82..995f916 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
@@ -1,24 +1,8 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.conf.MediaServerConfig;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
-import org.springframework.util.ConcurrentReferenceHashMap;
-import org.springframework.web.bind.annotation.*;
 
-import javax.servlet.http.HttpServletRequest;
-import java.math.BigInteger;
-import java.text.DecimalFormat;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -30,8 +14,6 @@
  */
 @Component
 public class ZLMHttpHookSubscribe {
-
-    private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookSubscribe.class);
 
     public enum HookType{
         on_flow_report,
@@ -72,8 +54,6 @@
         for (JSONObject key : eventMap.keySet()) {
             Boolean result = null;
             for (String s : key.keySet()) {
-                String string = hookResponse.getString(s);
-                String string1 = key.getString(s);
                 if (result == null) {
                     result = key.getString(s).equals(hookResponse.getString(s));
                 }else {
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
index ac1f51a..58de847 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -11,7 +11,6 @@
 import java.io.IOException;
 import java.net.ConnectException;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 
 @Component
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
index 282699f..490bfd8 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
@@ -5,8 +5,7 @@
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.conf.MediaServerConfig;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import okhttp3.*;
+//import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -16,9 +15,6 @@
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -28,8 +24,8 @@
 
     private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);
 
-    @Autowired
-    private IVideoManagerStorager storager;
+    // @Autowired
+    // private IVideoManagerStorager storager;
 
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
index 7a2a76c..bbdf5e2 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
@@ -8,6 +8,7 @@
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce;
+import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
 import com.github.pagehelper.PageInfo;
 import gov.nist.javax.sip.stack.NioTcpMessageProcessor;
 
@@ -236,4 +237,32 @@
     DeviceChannel queryChannelInParentPlatform(String platformId, String channelId);
 
     Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId);
+
+
+	/**
+	 * 娣诲姞Mobile Position璁惧绉诲姩浣嶇疆
+	 * @param MobilePosition
+	 * @return
+	 */
+	public boolean insertMobilePosition(MobilePosition mobilePosition);
+
+	/**
+	 * 鏌ヨ绉诲姩浣嶇疆杞ㄨ抗
+	 * @param deviceId
+	 * @param startTime
+	 * @param endTime
+	 */
+	public List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime);
+
+	/**
+	 * 鏌ヨ鏈�鏂扮Щ鍔ㄤ綅缃�
+	 * @param deviceId
+	 */
+	public MobilePosition queryLatestPosition(String deviceId);
+
+	/**
+	 * 鍒犻櫎鎸囧畾璁惧鐨勬墍鏈夌Щ鍔ㄤ綅缃�
+	 * @param deviceId
+	 */
+	public int clearMobilePositionsByDeviceId(String deviceId);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java
new file mode 100644
index 0000000..29f3c4d
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java
@@ -0,0 +1,33 @@
+package com.genersoft.iot.vmp.storager.dao;
+
+import java.util.List;
+
+import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
+import org.apache.ibatis.annotations.*;
+//import org.springframework.stereotype.Repository;
+
+@Mapper
+//@Repository
+public interface DeviceMobilePositionMapper {
+
+    @Insert("INSERT INTO device_mobile_position (deviceId, deviceName, time, longitude, latitude, altitude, speed, direction, reportSource, geodeticSystem, cnLng, cnLat) " +
+            "VALUES ('${deviceId}', '${deviceName}', '${time}', ${longitude}, ${latitude}, ${altitude}, ${speed}, ${direction}, '${reportSource}', '${geodeticSystem}', '${cnLng}', '${cnLat}')")
+    int insertNewPosition(MobilePosition mobilePosition);
+
+    @Select(value = {" <script>" +
+    "SELECT * FROM device_mobile_position" +
+    " WHERE deviceId = #{deviceId} " +
+    "<if test=\"startTime != null\"> AND time&gt;=#{startTime}</if>" +
+    "<if test=\"endTime != null\"> AND time&lt;=#{endTime}</if>" +
+    " ORDER BY time ASC" +
+    " </script>"})
+    List<MobilePosition> queryPositionByDeviceIdAndTime(String deviceId, String startTime, String endTime);
+
+    @Select("SELECT * FROM device_mobile_position WHERE deviceId = #{deviceId}" +
+            " ORDER BY time DESC LIMIT 1")
+    MobilePosition queryLatestPositionByDevice(String deviceId);
+
+    @Delete("DELETE FROM device_mobile_position WHERE deviceId = #{deviceId}")
+    int clearMobilePositionsByDeviceId(String deviceId);
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
index 960e728..083c86c 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -14,7 +14,6 @@
 import org.springframework.stereotype.Component;
 
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
@@ -147,9 +146,9 @@
 
     @Override
     public StreamInfo queryPlaybackByDevice(String deviceId, String code) {
-        String format = String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
-                deviceId,
-                code);
+        // String format = String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
+        //         deviceId,
+        //         code);
         List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
                 deviceId,
                 code));
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
index 7417935..7443eae 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
@@ -6,11 +6,13 @@
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
 import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
 import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
 import com.genersoft.iot.vmp.storager.dao.PatformChannelMapper;
 import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce;
+import com.genersoft.iot.vmp.storager.dao.DeviceMobilePositionMapper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -32,7 +34,10 @@
     private DeviceMapper deviceMapper;
 
 	@Autowired
-    private DeviceChannelMapper deviceChannelMapper;
+	private DeviceChannelMapper deviceChannelMapper;
+
+	@Autowired
+	private DeviceMobilePositionMapper deviceMobilePositionMapper;
 
 	@Autowired
     private ParentPlatformMapper platformMapper;
@@ -194,11 +199,11 @@
 	@Override
 	public synchronized boolean online(String deviceId) {
 		Device device = deviceMapper.getDeviceByDeviceId(deviceId);
-		device.setOnline(1);
-		System.out.println("鏇存柊璁惧鍦ㄧ嚎");
 		if (device == null) {
 			return false;
 		}
+		device.setOnline(1);
+		System.out.println("鏇存柊璁惧鍦ㄧ嚎");
 		return deviceMapper.update(device) > 0;
 	}
 
@@ -216,10 +221,33 @@
 		return deviceMapper.update(device) > 0;
 	}
 
-
+	/**
+	 * 娓呯┖閫氶亾
+	 * @param deviceId
+	 */
 	@Override
 	public void cleanChannelsForDevice(String deviceId) {
-		int result = deviceChannelMapper.cleanChannelsByDeviceId(deviceId);
+		deviceChannelMapper.cleanChannelsByDeviceId(deviceId);
+	}
+
+	/**
+	 * 娣诲姞Mobile Position璁惧绉诲姩浣嶇疆
+	 * @param MobilePosition
+	 */
+	@Override
+	public synchronized boolean insertMobilePosition(MobilePosition mobilePosition) {
+		return deviceMobilePositionMapper.insertNewPosition(mobilePosition) > 0;
+	}
+
+	/**
+	 * 鏌ヨ绉诲姩浣嶇疆杞ㄨ抗
+	 * @param deviceId
+	 * @param startTime
+	 * @param endTime
+	 */
+	@Override
+	public synchronized List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime) {
+		return deviceMobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, startTime, endTime);
 	}
 
 	@Override
@@ -283,7 +311,7 @@
 
 	@Override
 	public PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online,
-														 Boolean channelType, String platformId, Boolean inPlatform) {
+													   Boolean channelType, String platformId, Boolean inPlatform) {
 		PageHelper.startPage(page, count);
 		List<ChannelReduce> all = deviceChannelMapper.queryChannelListInAll(query, online, channelType, platformId, inPlatform);
 		return new PageInfo<>(all);
@@ -341,4 +369,22 @@
 		Device device = patformChannelMapper.queryVideoDeviceByPlatformIdAndChannelId(platformId, channelId);
 		return device;
 	}
+
+	/**
+	 * 鏌ヨ鏈�鏂扮Щ鍔ㄤ綅缃�
+	 * @param deviceId
+	 */
+	@Override
+	public MobilePosition queryLatestPosition(String deviceId) {
+		return deviceMobilePositionMapper.queryLatestPositionByDevice(deviceId);
+	}
+
+	/**
+	 * 鍒犻櫎鎸囧畾璁惧鐨勬墍鏈夌Щ鍔ㄤ綅缃�
+	 * @param deviceId
+	 */
+	public int clearMobilePositionsByDeviceId(String deviceId) {
+		return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId);
+	}
+
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java
new file mode 100644
index 0000000..1672441
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java
@@ -0,0 +1,68 @@
+package com.genersoft.iot.vmp.utils;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Base64;
+
+import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint;
+
+public class GpsUtil {
+    public static BaiduPoint Wgs84ToBd09(String xx, String yy) {
+        try {
+            Socket s = new Socket("api.map.baidu.com", 80);
+            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
+            OutputStream out = s.getOutputStream();
+            StringBuffer sb = new StringBuffer("GET /ag/coord/convert?from=0&to=4");
+            sb.append("&x=" + xx + "&y=" + yy);
+            sb.append("&callback=BMap.Convertor.cbk_3976 HTTP/1.1\r\n");
+            sb.append("User-Agent: Java/1.6.0_20\r\n");
+            sb.append("Host: api.map.baidu.com:80\r\n");
+            sb.append("Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\r\n");
+            sb.append("Connection: Close\r\n");
+            sb.append("\r\n");
+            out.write(sb.toString().getBytes());
+            String json = "";
+            String tmp = "";
+            while ((tmp = br.readLine()) != null) {
+                // System.out.println(tmp);
+                json += tmp;
+            }
+
+            s.close();
+            int start = json.indexOf("cbk_3976");
+            int end = json.lastIndexOf("}");
+            if (start != -1 && end != -1 && json.contains("\"x\":\"")) {
+                json = json.substring(start, end);
+                String[] point = json.split(",");
+                String x = point[1].split(":")[1].replace("\"", "");
+                String y = point[2].split(":")[1].replace("\"", "");
+                BaiduPoint bdPoint= new BaiduPoint();
+                bdPoint.setBdLng(new String(decode(x)));
+                bdPoint.setBdLat(new String(decode(y)));
+                return bdPoint;
+                //return (new String(decode(x)) + "," + new String(decode(y)));
+            } else {
+                System.out.println("gps鍧愭爣鏃犳晥锛侊紒");
+            }
+            out.close();
+            br.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * BASE64瑙g爜
+     * @param str
+     * @return string
+     */
+    public static byte[] decode(String str) {
+        byte[] bt = null;
+        final Base64.Decoder decoder = Base64.getDecoder();
+        bt = decoder.decode(str); // .decodeBuffer(str);
+        return bt;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
index e0f7e41..f83d5f3 100644
--- a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
+++ b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
@@ -4,8 +4,6 @@
 import java.util.concurrent.TimeUnit;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.dao.DataAccessException;
-import org.springframework.data.redis.connection.RedisConnection;
 import org.springframework.data.redis.core.*;
 import org.springframework.stereotype.Component;
 import org.springframework.util.CollectionUtils;
@@ -16,6 +14,7 @@
  * @date:   2020骞�5鏈�6鏃� 涓嬪崍8:27:29     
  */
 @Component
+@SuppressWarnings(value = {"rawtypes", "unchecked"})
 public class RedisUtil {
 
 	@Autowired
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/MobilePosition/MobilePositionController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/MobilePosition/MobilePositionController.java
new file mode 100644
index 0000000..92806b1
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/MobilePosition/MobilePositionController.java
@@ -0,0 +1,118 @@
+package com.genersoft.iot.vmp.vmanager.MobilePosition;
+
+import java.util.List;
+
+import javax.sip.message.Response;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.github.pagehelper.util.StringUtil;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.async.DeferredResult;
+
+@CrossOrigin
+@RestController
+@RequestMapping("/api")
+public class MobilePositionController {
+
+    private final static Logger logger = LoggerFactory.getLogger(MobilePositionController.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+    
+	@Autowired
+	private SIPCommander cmder;
+	
+	@Autowired
+	private DeferredResultHolder resultHolder;
+	
+    @GetMapping("/positions/{deviceId}/history")
+    public ResponseEntity<List<MobilePosition>> positions(@PathVariable String deviceId,
+                                                    @RequestParam(required = false) String start,
+                                                    @RequestParam(required = false) String end) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("鏌ヨ璁惧" + deviceId + "鐨勫巻鍙茶建杩�");
+        }
+
+        if (StringUtil.isEmpty(start)) {
+            start = null;
+        }
+        if (StringUtil.isEmpty(end)) {
+            end = null;
+        }
+
+        List<MobilePosition> result = storager.queryMobilePositions(deviceId, start, end);
+        return new ResponseEntity<>(result, HttpStatus.OK);
+    }
+
+    @GetMapping("/positions/{deviceId}/latest")
+    public ResponseEntity<MobilePosition> latestPosition(@PathVariable String deviceId) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("鏌ヨ璁惧" + deviceId + "鐨勬渶鏂颁綅缃�");
+        }
+        MobilePosition result = storager.queryLatestPosition(deviceId);
+        return new ResponseEntity<>(result, HttpStatus.OK);
+    }
+
+    @GetMapping("/positions/{deviceId}/realtime")
+    public DeferredResult<ResponseEntity<MobilePosition>> realTimePosition(@PathVariable String deviceId) {
+        Device device = storager.queryVideoDevice(deviceId);
+        cmder.mobilePostitionQuery(device, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_MOBILEPOSITION + deviceId);
+			msg.setData(String.format("鑾峰彇绉诲姩浣嶇疆淇℃伅澶辫触锛岄敊璇爜锛� %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<MobilePosition>> result = new DeferredResult<ResponseEntity<MobilePosition>>(5*1000L);
+		result.onTimeout(()->{
+			logger.warn(String.format("鑾峰彇绉诲姩浣嶇疆淇℃伅瓒呮椂"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId);
+			msg.setData("Timeout");
+			resultHolder.invokeResult(msg);
+		});
+        resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result);
+        return result;
+    }
+
+    @GetMapping("/positions/{deviceId}/subscribe")
+    public ResponseEntity<String> positionSubscribe(@PathVariable String deviceId,
+                                                    @RequestParam String expires,
+                                                    @RequestParam String interval) {
+        String msg = ((expires.equals("0")) ? "鍙栨秷" : "") + "璁㈤槄璁惧" + deviceId + "鐨勭Щ鍔ㄤ綅缃�";
+        if (logger.isDebugEnabled()) {
+            logger.debug(msg);
+        }
+
+        if (StringUtil.isEmpty(interval)) {
+            interval = "5";
+        }
+        Device device = storager.queryVideoDevice(deviceId);
+
+        String result = msg;
+        if (cmder.mobilePositionSubscribe(device, Integer.parseInt(expires), Integer.parseInt(interval))) {
+            result += "锛屾垚鍔�";
+        } else {
+            result += "锛屽け璐�";
+        }
+
+        return new ResponseEntity<>(result, HttpStatus.OK);
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java
index 01af4a0..1e55c32 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java
@@ -3,6 +3,7 @@
 import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@@ -13,6 +14,7 @@
  * @data: 2021-01-20
  */
 
+@CrossOrigin
 @Controller
 @RequestMapping("/api")
 public class SseController {
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java
new file mode 100644
index 0000000..d046c0d
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java
@@ -0,0 +1,120 @@
+/**
+ * 璁惧璁剧疆鍛戒护API鎺ュ彛
+ * 
+ * @author lawrencehj
+ * @date 2021骞�2鏈�2鏃�
+ */
+
+package com.genersoft.iot.vmp.vmanager.device;
+
+import javax.sip.message.Response;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.request.async.DeferredResult;
+
+@CrossOrigin
+@RestController
+@RequestMapping("/api")
+public class DeviceConfig {
+
+    private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private SIPCommander cmder;
+
+    @Autowired
+    private DeferredResultHolder resultHolder;
+
+	/**
+	 * 鐪嬪畧浣嶆帶鍒跺懡浠PI鎺ュ彛
+	 * 
+	 * @param deviceId
+	 * @param enabled       鐪嬪畧浣嶄娇鑳�1:寮�鍚�,0:鍏抽棴
+	 * @param resetTime     鑷姩褰掍綅鏃堕棿闂撮殧锛堝彲閫夛級
+     * @param presetIndex   璋冪敤棰勭疆浣嶇紪鍙凤紙鍙�夛級
+     * @param channelId     閫氶亾缂栫爜锛堝彲閫夛級
+	 */
+	@GetMapping("/config/{deviceId}/basicParam")
+	public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId,
+                                                                @RequestParam(required = false) String channelId,
+                                                                @RequestParam(required = false) String name,
+																@RequestParam(required = false) String expiration,
+																@RequestParam(required = false) String heartBeatInterval,
+                                                                @RequestParam(required = false) String heartBeatCount) {
+        if (logger.isDebugEnabled()) {
+			logger.debug("鎶ヨ澶嶄綅API璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			msg.setData(String.format("璁惧閰嶇疆鎿嶄綔澶辫触锛岄敊璇爜锛� %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
+		result.onTimeout(() -> {
+			logger.warn(String.format("璁惧閰嶇疆鎿嶄綔瓒呮椂, 璁惧鏈繑鍥炲簲绛旀寚浠�"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			JSONObject json = new JSONObject();
+			json.put("DeviceID", deviceId);
+			json.put("Status", "Timeout");
+			json.put("Description", "璁惧閰嶇疆鎿嶄綔瓒呮椂, 璁惧鏈繑鍥炲簲绛旀寚浠�");
+			msg.setData(json); //("鐪嬪畧浣嶆帶鍒舵搷浣滆秴鏃�, 璁惧鏈繑鍥炲簲绛旀寚浠�");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
+		return result;
+	}
+
+	/**
+	 * 璁惧閰嶇疆鏌ヨ璇锋眰API鎺ュ彛
+	 * 
+	 * @param deviceId
+	 */
+	@GetMapping("/config/{deviceId}/query/{configType}")
+    public DeferredResult<ResponseEntity<String>> configDownloadApi(@PathVariable String deviceId, 
+                                                                @PathVariable String configType,
+                                                                @RequestParam(required = false) String channelId) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("璁惧鐘舵�佹煡璇PI璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		cmder.deviceConfigQuery(device, channelId, configType, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			msg.setData(String.format("鑾峰彇璁惧閰嶇疆澶辫触锛岄敊璇爜锛� %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L);
+		result.onTimeout(()->{
+			logger.warn(String.format("鑾峰彇璁惧閰嶇疆瓒呮椂"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			msg.setData("Timeout. Device did not response to this command.");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
+		return result;
+	}
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java
new file mode 100644
index 0000000..7221359
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java
@@ -0,0 +1,238 @@
+/**
+ * 璁惧鎺у埗鍛戒护API鎺ュ彛
+ * 
+ * @author lawrencehj
+ * @date 2021骞�2鏈�1鏃�
+ */
+
+package com.genersoft.iot.vmp.vmanager.device;
+
+import javax.sip.message.Response;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.request.async.DeferredResult;
+
+@CrossOrigin
+@RestController
+@RequestMapping("/api")
+public class DeviceControl {
+
+    private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private SIPCommander cmder;
+
+    @Autowired
+    private DeferredResultHolder resultHolder;
+
+    /**
+     * 杩滅▼鍚姩鎺у埗鍛戒护API鎺ュ彛
+     * 
+     * @param deviceId
+     */
+    @GetMapping("/control/{deviceId}/teleboot")
+    @PostMapping("/control/{deviceId}/teleboot")
+    public ResponseEntity<String> teleBootApi(@PathVariable String deviceId) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("璁惧杩滅▼鍚姩API璋冪敤");
+        }
+        Device device = storager.queryVideoDevice(deviceId);
+        boolean sucsess = cmder.teleBootCmd(device);
+        if (sucsess) {
+            JSONObject json = new JSONObject();
+            json.put("DeviceID", deviceId);
+            json.put("Result", "OK");
+            return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK);
+        } else {
+            logger.warn("璁惧杩滅▼鍚姩API璋冪敤澶辫触锛�");
+            return new ResponseEntity<String>("璁惧杩滅▼鍚姩API璋冪敤澶辫触锛�", HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    /**
+     * 褰曞儚鎺у埗鍛戒护API鎺ュ彛
+     * 
+     * @param deviceId
+     * @param recordCmdStr  Record锛氭墜鍔ㄥ綍鍍忥紝StopRecord锛氬仠姝㈡墜鍔ㄥ綍鍍�
+     * @param channelId     閫氶亾缂栫爜锛堝彲閫夛級
+     */
+    @GetMapping("/control/{deviceId}/record/{recordCmdStr}")
+    public DeferredResult<ResponseEntity<String>> recordApi(@PathVariable String deviceId,
+            @PathVariable String recordCmdStr, @RequestParam(required = false) String channelId) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("寮�濮�/鍋滄褰曞儚API璋冪敤");
+        }
+        Device device = storager.queryVideoDevice(deviceId);
+        cmder.recordCmd(device, channelId, recordCmdStr, event -> {
+            Response response = event.getResponse();
+            RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			msg.setData(String.format("寮�濮�/鍋滄褰曞儚鎿嶄綔澶辫触锛岄敊璇爜锛� %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
+		result.onTimeout(() -> {
+			logger.warn(String.format("寮�濮�/鍋滄褰曞儚鎿嶄綔瓒呮椂, 璁惧鏈繑鍥炲簲绛旀寚浠�"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			msg.setData("Timeout. Device did not response to this command.");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
+		return result;
+	}
+
+	/**
+	 * 鎶ヨ甯冮槻/鎾ら槻鍛戒护API鎺ュ彛
+	 * 
+	 * @param	deviceId
+	 * @param	guardCmdStr SetGuard锛氬竷闃诧紝ResetGuard锛氭挙闃�
+	 */
+	@GetMapping("/control/{deviceId}/guard/{guardCmdStr}")
+	public DeferredResult<ResponseEntity<String>> guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("甯冮槻/鎾ら槻API璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		cmder.guardCmd(device, guardCmdStr, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
+			msg.setData(String.format("甯冮槻/鎾ら槻鎿嶄綔澶辫触锛岄敊璇爜锛� %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
+		result.onTimeout(() -> {
+			logger.warn(String.format("甯冮槻/鎾ら槻鎿嶄綔瓒呮椂, 璁惧鏈繑鍥炲簲绛旀寚浠�"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
+			msg.setData("Timeout. Device did not response to this command.");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result);
+		return result;
+	}
+
+	/**
+	 * 鎶ヨ澶嶄綅API鎺ュ彛
+	 * 
+	 * @param	deviceId
+	 * @param	alarmMethod 鎶ヨ鏂瑰紡锛堝彲閫夛級
+	 * @param	alarmType   鎶ヨ绫诲瀷锛堝彲閫夛級
+	 */
+	@GetMapping("/control/{deviceId}/resetAlarm")
+	public DeferredResult<ResponseEntity<String>> resetAlarmApi(@PathVariable String deviceId, 
+																@RequestParam(required = false) String alarmMethod,
+																@RequestParam(required = false) String alarmType) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("鎶ヨ澶嶄綅API璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		cmder.alarmCmd(device, alarmMethod, alarmType, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
+			msg.setData(String.format("鎶ヨ澶嶄綅鎿嶄綔澶辫触锛岄敊璇爜锛� %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
+		result.onTimeout(() -> {
+			logger.warn(String.format("鎶ヨ澶嶄綅鎿嶄綔瓒呮椂, 璁惧鏈繑鍥炲簲绛旀寚浠�"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
+			msg.setData("Timeout. Device did not response to this command.");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result);
+		return result;
+	}
+
+	/**
+	 * 寮哄埗鍏抽敭甯PI鎺ュ彛
+	 * 
+	 * @param	deviceId
+	 * @param	channelId 
+	 */
+	@GetMapping("/control/{deviceId}/iFrame")
+	@PostMapping("/control/{deviceId}/iFrame")
+	public ResponseEntity<String> iFrame(@PathVariable String deviceId,
+										@RequestParam(required = false) String channelId) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("寮哄埗鍏抽敭甯PI璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		boolean sucsess = cmder.iFrameCmd(device, channelId);
+		if (sucsess) {
+			JSONObject json = new JSONObject();
+			json.put("DeviceID", deviceId);
+			json.put("ChannelID", channelId);
+			json.put("Result", "OK");
+			return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK);
+		} else {
+			logger.warn("寮哄埗鍏抽敭甯PI璋冪敤澶辫触锛�");
+			return new ResponseEntity<String>("寮哄埗鍏抽敭甯PI璋冪敤澶辫触锛�", HttpStatus.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * 鐪嬪畧浣嶆帶鍒跺懡浠PI鎺ュ彛
+	 * 
+	 * @param deviceId
+	 * @param enabled       鐪嬪畧浣嶄娇鑳�1:寮�鍚�,0:鍏抽棴
+	 * @param resetTime     鑷姩褰掍綅鏃堕棿闂撮殧锛堝彲閫夛級
+     * @param presetIndex   璋冪敤棰勭疆浣嶇紪鍙凤紙鍙�夛級
+     * @param channelId     閫氶亾缂栫爜锛堝彲閫夛級
+	 */
+	@GetMapping("/control/{deviceId}/homePosition/{enabled}")
+	public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId,
+																@PathVariable String enabled,
+																@RequestParam(required = false) String resetTime,
+																@RequestParam(required = false) String presetIndex,
+                                                                @RequestParam(required = false) String channelId) {
+        if (logger.isDebugEnabled()) {
+			logger.debug("鎶ヨ澶嶄綅API璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			msg.setData(String.format("鐪嬪畧浣嶆帶鍒舵搷浣滃け璐ワ紝閿欒鐮侊細 %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
+		result.onTimeout(() -> {
+			logger.warn(String.format("鐪嬪畧浣嶆帶鍒舵搷浣滆秴鏃�, 璁惧鏈繑鍥炲簲绛旀寚浠�"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			JSONObject json = new JSONObject();
+			json.put("DeviceID", deviceId);
+			json.put("Status", "Timeout");
+			json.put("Description", "鐪嬪畧浣嶆帶鍒舵搷浣滆秴鏃�, 璁惧鏈繑鍥炲簲绛旀寚浠�");
+			msg.setData(json); //("鐪嬪畧浣嶆帶鍒舵搷浣滆秴鏃�, 璁惧鏈繑鍥炲簲绛旀寚浠�");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
+		return result;
+	}
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java
similarity index 65%
rename from src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java
rename to src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java
index 17d1e7d..997a43b 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java
@@ -21,12 +21,13 @@
 
 import javax.sip.message.Response;
 
+@SuppressWarnings("rawtypes")
 @CrossOrigin
 @RestController
 @RequestMapping("/api")
-public class DeviceController {
+public class DeviceQuery {
 	
-	private final static Logger logger = LoggerFactory.getLogger(DeviceController.class);
+	private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class);
 	
 	@Autowired
 	private IVideoManagerStorager storager;
@@ -77,11 +78,9 @@
 											   int page, int count,
 											   @RequestParam(required = false) String query,
 											   @RequestParam(required = false) Boolean online,
-											   @RequestParam(required = false) Boolean channelType
-	){
-
+											   @RequestParam(required = false) Boolean channelType) {
 		if (logger.isDebugEnabled()) {
-			logger.debug("鏌ヨ鎵�鏈夎棰戣澶嘇PI璋冪敤");
+			logger.debug("鏌ヨ瑙嗛璁惧閫氶亾API璋冪敤");
 		}
 		if (StringUtils.isEmpty(query)) {
 			query = null;
@@ -135,8 +134,8 @@
 			json.put("deviceId", deviceId);
 			return new ResponseEntity<>(json.toString(),HttpStatus.OK);
 		} else {
-			logger.warn("璁惧棰勮API璋冪敤澶辫触锛�");
-			return new ResponseEntity<String>("璁惧棰勮API璋冪敤澶辫触锛�", HttpStatus.INTERNAL_SERVER_ERROR);
+			logger.warn("璁惧淇℃伅鍒犻櫎API璋冪敤澶辫触锛�");
+			return new ResponseEntity<String>("璁惧淇℃伅鍒犻櫎API璋冪敤澶辫触锛�", HttpStatus.INTERNAL_SERVER_ERROR);
 		}
 	}
 
@@ -157,7 +156,7 @@
 												  @RequestParam(required = false) Boolean channelType){
 
 		if (logger.isDebugEnabled()) {
-			logger.debug("鏌ヨ鎵�鏈夎棰戣澶嘇PI璋冪敤");
+			logger.debug("鏌ヨ鎵�鏈夎棰戦�氶亾API璋冪敤");
 		}
 		DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId);
 		if (deviceChannel == null) {
@@ -183,4 +182,74 @@
 		storager.updateDevice(device);
 		return new ResponseEntity<>(null,HttpStatus.OK);
 	}
+
+	/**
+	 * 璁惧鐘舵�佹煡璇㈣姹侫PI鎺ュ彛
+	 * 
+	 * @param deviceId
+	 */
+	@GetMapping("/devices/{deviceId}/status")
+	public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("璁惧鐘舵�佹煡璇PI璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		cmder.deviceStatusQuery(device, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId);
+			msg.setData(String.format("鑾峰彇璁惧鐘舵�佸け璐ワ紝閿欒鐮侊細 %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
+		result.onTimeout(()->{
+			logger.warn(String.format("鑾峰彇璁惧鐘舵�佽秴鏃�"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId);
+			msg.setData("Timeout. Device did not response to this command.");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, result);
+		return result;
+	}
+
+	/**
+	 * 璁惧鎶ヨ鏌ヨ璇锋眰API鎺ュ彛
+	 * 
+	 * @param deviceId
+	 */
+	@GetMapping("/alarm/{deviceId}")
+	public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId,
+														@RequestParam(required = false) String startPriority, 
+														@RequestParam(required = false) String endPriority, 
+														@RequestParam(required = false) String alarmMethod,
+														@RequestParam(required = false) String alarmType,
+														@RequestParam(required = false) String startTime,
+														@RequestParam(required = false) String endTime) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("璁惧鎶ヨ鏌ヨAPI璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId);
+			msg.setData(String.format("璁惧鎶ヨ鏌ヨ澶辫触锛岄敊璇爜锛� %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L);
+		result.onTimeout(()->{
+			logger.warn(String.format("璁惧鎶ヨ鏌ヨ瓒呮椂"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId);
+			msg.setData("璁惧鎶ヨ鏌ヨ瓒呮椂");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, result);
+		return result;
+	}
+
+
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
index 9b9a69b..ebd17eb 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -1,21 +1,17 @@
 package com.genersoft.iot.vmp.vmanager.play;
 
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.MediaServerConfig;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult;
 import com.genersoft.iot.vmp.vmanager.service.IPlayService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.CrossOrigin;
@@ -32,7 +28,6 @@
 import org.springframework.web.context.request.async.DeferredResult;
 
 import javax.sip.message.Response;
-import java.text.DecimalFormat;
 import java.util.UUID;
 
 @CrossOrigin
@@ -103,7 +98,7 @@
 				storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
 				RequestMessage msg = new RequestMessage();
 				msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid);
-				Response response = event.getResponse();
+				//Response response = event.getResponse();
 				msg.setData(String.format("success"));
 				resultHolder.invokeResult(msg);
 			}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
index 9449d26..020a26c 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
@@ -1,24 +1,19 @@
 package com.genersoft.iot.vmp.vmanager.playback;
 
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
+//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.vmanager.service.IPlayService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -47,8 +42,8 @@
 	@Autowired
 	private IRedisCatchStorage redisCatchStorage;
 
-	@Autowired
-	private ZLMRESTfulUtils zlmresTfulUtils;
+	// @Autowired
+	// private ZLMRESTfulUtils zlmresTfulUtils;
 
 	@Autowired
 	private IPlayService playService;
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java
index 4c41e16..1efdd3d 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java
@@ -5,14 +5,16 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.request.async.DeferredResult;
+
+import javax.sip.message.Response;
 
 import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 
 @CrossOrigin
@@ -28,6 +30,9 @@
 	@Autowired
 	private IVideoManagerStorager storager;
 
+	@Autowired
+	private DeferredResultHolder resultHolder;
+	
 	/***
 	 * 浜戝彴鎺у埗
 	 * @param deviceId 璁惧id
@@ -49,16 +54,18 @@
 		cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed);
 		return new ResponseEntity<String>("success",HttpStatus.OK);
 	}
-	// public ResponseEntity<String> ptz(@PathVariable String deviceId,@PathVariable String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed){
-		
-	// 	if (logger.isDebugEnabled()) {
-	// 		logger.debug(String.format("璁惧浜戝彴鎺у埗 API璋冪敤锛宒eviceId锛�%s 锛宑hannelId锛�%s 锛宭eftRight锛�%d 锛寀pDown锛�%d 锛宨nOut锛�%d 锛宮oveSpeed锛�%d 锛寊oomSpeed锛�%d",deviceId, channelId, leftRight, upDown, inOut, moveSpeed, zoomSpeed));
-	// 	}
-	// 	Device device = storager.queryVideoDevice(deviceId);
-		
-	// 	cmder.ptzCmd(device, channelId, leftRight, upDown, inOut, moveSpeed, zoomSpeed);
-	// 	return new ResponseEntity<String>("success",HttpStatus.OK);
-	// }
+
+	/**
+	 * 閫氱敤鍓嶇鎺у埗鍛戒护API鎺ュ彛
+	 * 
+	 * @param deviceId
+	 * @param channelId
+	 * @param cmdCode
+	 * @param parameter1
+	 * @param parameter2
+	 * @param combindCode2
+	 * @return
+	 */
 	@PostMapping("/frontEndCommand/{deviceId}/{channelId}")
 	public ResponseEntity<String> frontEndCommand(@PathVariable String deviceId,@PathVariable String channelId,int cmdCode, int parameter1, int parameter2, int combindCode2){
 		
@@ -70,4 +77,37 @@
 		cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2);
 		return new ResponseEntity<String>("success",HttpStatus.OK);
 	}
+
+	/**
+	 * 棰勭疆浣嶆煡璇㈠懡浠PI鎺ュ彛
+	 * 
+	 * @param deviceId
+	 * @param channelId
+	 * @return
+	 */
+	@GetMapping("/presetQuery/{deviceId}/{channelId}")
+	public DeferredResult<ResponseEntity<String>> presetQueryApi(@PathVariable String deviceId, @PathVariable String channelId) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("璁惧棰勭疆浣嶆煡璇PI璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
+		cmder.presetQuery(device, channelId, event -> {
+			Response response = event.getResponse();
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			msg.setData(String.format("鑾峰彇璁惧棰勭疆浣嶅け璐ワ紝閿欒鐮侊細 %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+			resultHolder.invokeResult(msg);
+		});
+        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L);
+		result.onTimeout(()->{
+			logger.warn(String.format("鑾峰彇璁惧棰勭疆浣嶈秴鏃�"));
+			// 閲婃斁rtpserver
+			RequestMessage msg = new RequestMessage();
+			msg.setId(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
+			msg.setData("鑾峰彇璁惧棰勭疆浣嶈秴鏃�");
+			resultHolder.invokeResult(msg);
+		});
+		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
+		return result;
+	}
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java
index 898c014..963fc8c 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java
@@ -1,12 +1,9 @@
 package com.genersoft.iot.vmp.vmanager.service;
 
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
 import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.context.request.async.DeferredResult;
 
 /**
  * 鐐规挱澶勭悊
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
index 12ade38..5ebfd2d 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
@@ -1,8 +1,5 @@
 package com.genersoft.iot.vmp.vmanager.user;
 
-import com.genersoft.iot.vmp.vmanager.play.PlayController;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.CrossOrigin;
@@ -13,9 +10,6 @@
 @RestController
 @RequestMapping("/api")
 public class UserController {
-
-    private final static Logger logger = LoggerFactory.getLogger(UserController.class);
-
 
     @Value("${auth.username}")
     private String usernameConfig;
diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java
index c9a68bf..c55824a 100644
--- a/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java
+++ b/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java
@@ -4,12 +4,9 @@
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import com.genersoft.iot.vmp.vmanager.ptz.PtzController;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
 /**
diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiController.java
index 002b2cc..43ae7d9 100644
--- a/src/main/java/com/genersoft/iot/vmp/web/ApiController.java
+++ b/src/main/java/com/genersoft/iot/vmp/web/ApiController.java
@@ -5,11 +5,9 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 
 /**
diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java
index 199a9e6..b58a3c5 100644
--- a/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java
+++ b/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java
@@ -4,9 +4,9 @@
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
-import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+// import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
+// import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+// import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import com.github.pagehelper.PageInfo;
 import org.slf4j.Logger;
@@ -19,6 +19,7 @@
 /**
  * 鍏煎LiveGBS鐨凙PI锛氳澶囦俊鎭�
  */
+@SuppressWarnings("unchecked")
 @CrossOrigin
 @RestController
 @RequestMapping(value = "/api/v1/device")
@@ -29,14 +30,14 @@
     @Autowired
     private IVideoManagerStorager storager;
 
-    @Autowired
-    private SIPCommander cmder;
+    // @Autowired
+    // private SIPCommander cmder;
 
-    @Autowired
-    private DeferredResultHolder resultHolder;
+    // @Autowired
+    // private DeferredResultHolder resultHolder;
 
-    @Autowired
-    private DeviceOffLineDetector offLineDetector;
+    // @Autowired
+    // private DeviceOffLineDetector offLineDetector;
 
     /**
      * 鍒嗛〉鑾峰彇璁惧鍒楄〃 TODO 鐜板湪鐩存帴杩斿洖锛屽皻鏈疄鐜板垎椤�
diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java
index f31bd60..5a56bef 100644
--- a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java
+++ b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java
@@ -1,21 +1,18 @@
 package com.genersoft.iot.vmp.web;
 
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
-import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
+// import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import com.genersoft.iot.vmp.vmanager.play.PlayController;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.async.DeferredResult;
@@ -23,6 +20,7 @@
 /**
  * 鍏煎LiveGBS鐨凙PI锛氬疄鏃剁洿鎾�
  */
+@SuppressWarnings(value = {"rawtypes", "unchecked"})
 @CrossOrigin
 @RestController
 @RequestMapping(value = "/api/v1/stream")
@@ -40,8 +38,8 @@
     private IRedisCatchStorage redisCatchStorage;
 
 
-    @Autowired
-    private ZLMRESTfulUtils zlmresTfulUtils;
+    // @Autowired
+    // private ZLMRESTfulUtils zlmresTfulUtils;
 
 
     @Autowired
diff --git a/src/main/java/com/genersoft/iot/vmp/web/AuthController.java b/src/main/java/com/genersoft/iot/vmp/web/AuthController.java
index 8fd66ca..702387c 100644
--- a/src/main/java/com/genersoft/iot/vmp/web/AuthController.java
+++ b/src/main/java/com/genersoft/iot/vmp/web/AuthController.java
@@ -1,9 +1,6 @@
 package com.genersoft.iot.vmp.web;
 
-import com.genersoft.iot.vmp.gb28181.bean.Device;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Controller;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index fdcae2e..b879403 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -92,4 +92,8 @@
     level:
         com:
             genersoft:
-                iot: debug
\ No newline at end of file
+                iot: debug
+# [鏍规嵁涓氬姟闇�姹傞厤缃甝
+userSettings:
+    # 淇濆瓨绉诲姩浣嶇疆鍘嗗彶杞ㄨ抗锛歵rue:淇濈暀鍘嗗彶鏁版嵁锛宖alse:浠呬繚鐣欐渶鍚庣殑浣嶇疆(榛樿)
+    savePositionHistory: false
\ No newline at end of file
diff --git a/web_src/.postcssrc.js b/web_src/.postcssrc.js
index f98d56a..2fb7b66 100644
--- a/web_src/.postcssrc.js
+++ b/web_src/.postcssrc.js
@@ -7,7 +7,7 @@
     // to edit target browsers: use "browserslist" field in package.json
     "autoprefixer": {},
     'postcss-pxtorem': {
-      rootValue: 16, 
+      rootValue: 24,
       propList: ['font-size'] // 鍙浆鍖杅ont-size
     }
   }
diff --git a/web_src/index.html b/web_src/index.html
index 1224125..8b26e72 100644
--- a/web_src/index.html
+++ b/web_src/index.html
@@ -7,6 +7,7 @@
   </head>
   <body>
     <script type="text/javascript" src="./js/EasyWasmPlayer.js"></script>
+    <script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=rk73w8dv1rkE4UdZsataG68VarhYQzrx&s=1"></script>
     <div id="app"></div>
     <!-- built files will be auto injected -->
   </body>
diff --git a/web_src/package-lock.json b/web_src/package-lock.json
index fb6278d..54840f6 100644
--- a/web_src/package-lock.json
+++ b/web_src/package-lock.json
@@ -1269,6 +1269,34 @@
       "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=",
       "dev": true
     },
+    "bmaplib.curveline": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/bmaplib.curveline/-/bmaplib.curveline-1.0.0.tgz",
+      "integrity": "sha512-9wcFMVhiYxNPqpvsLDAADn3qDhNzXp2mA6VyHSHg2XOAgSooC7ZiujdFhy0sp+0QYjTfJ/MjmLuNoUg2HHxH4Q=="
+    },
+    "bmaplib.heatmap": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/bmaplib.heatmap/-/bmaplib.heatmap-1.0.4.tgz",
+      "integrity": "sha512-rmhqUARBpUSJ9jXzUI2j7dIOqnc38bqubkx/8a349U2qtw/ulLUwyzRD535OrA8G7w5cz4aPKm6/rNvUAarg/Q=="
+    },
+    "bmaplib.lushu": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/bmaplib.lushu/-/bmaplib.lushu-1.0.7.tgz",
+      "integrity": "sha512-LVvgpESPii6xGxyjnQjq8u+ic4NjvhdCPV/RiSS/PGTUdZKeTDS7prSpleJLZH3ES0+oc0gYn8bw0LtPYUSz2w=="
+    },
+    "bmaplib.markerclusterer": {
+      "version": "1.0.13",
+      "resolved": "https://registry.npmjs.org/bmaplib.markerclusterer/-/bmaplib.markerclusterer-1.0.13.tgz",
+      "integrity": "sha512-VrLyWSiuDEVNi0yUfwOhFQ6z1oEEHS4w36GNu3iASu6p52QIx9uAXMUkuSCHReNR0bj2Cp9SA1dSx5RpojXajQ==",
+      "requires": {
+        "bmaplib.texticonoverlay": "^1.0.2"
+      }
+    },
+    "bmaplib.texticonoverlay": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bmaplib.texticonoverlay/-/bmaplib.texticonoverlay-1.0.2.tgz",
+      "integrity": "sha512-4ZTWr4ZP3B6qEWput5Tut16CfZgII38YwM3bpyb4gFTQyORlKYryFp9WHWrwZZaHlOyYDAXG9SX0hka43jTADg=="
+    },
     "bn.js": {
       "version": "5.1.3",
       "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-5.1.3.tgz",
@@ -5266,6 +5294,14 @@
         "invert-kv": "^1.0.0"
       }
     },
+    "linkify-it": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
+      "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
+      "requires": {
+        "uc.micro": "^1.0.1"
+      }
+    },
     "load-json-file": {
       "version": "2.0.0",
       "resolved": "https://registry.npm.taobao.org/load-json-file/download/load-json-file-2.0.0.tgz",
@@ -5443,6 +5479,25 @@
         "object-visit": "^1.0.0"
       }
     },
+    "markdown-it": {
+      "version": "8.4.2",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz",
+      "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==",
+      "requires": {
+        "argparse": "^1.0.7",
+        "entities": "~1.1.1",
+        "linkify-it": "^2.0.0",
+        "mdurl": "^1.0.1",
+        "uc.micro": "^1.0.5"
+      },
+      "dependencies": {
+        "entities": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+          "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
+        }
+      }
+    },
     "math-expression-evaluator": {
       "version": "1.2.22",
       "resolved": "https://registry.npm.taobao.org/math-expression-evaluator/download/math-expression-evaluator-1.2.22.tgz",
@@ -5465,6 +5520,11 @@
       "resolved": "https://registry.npm.taobao.org/mdn-data/download/mdn-data-2.0.4.tgz",
       "integrity": "sha1-aZs8OKxvHXKAkaZGULZdOIUC/Vs=",
       "dev": true
+    },
+    "mdurl": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+      "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
     },
     "media-typer": {
       "version": "0.3.0",
@@ -10074,8 +10134,7 @@
     "sprintf-js": {
       "version": "1.0.3",
       "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-      "dev": true
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
     },
     "ssri": {
       "version": "5.3.0",
@@ -10489,6 +10548,11 @@
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
       "dev": true
     },
+    "uc.micro": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+      "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
+    },
     "uglify-js": {
       "version": "3.4.10",
       "resolved": "https://registry.npm.taobao.org/uglify-js/download/uglify-js-3.4.10.tgz?cache=0&sync_timestamp=1601823880483&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuglify-js%2Fdownload%2Fuglify-js-3.4.10.tgz",
@@ -10841,6 +10905,18 @@
       "resolved": "https://registry.npm.taobao.org/vue/download/vue-2.6.12.tgz?cache=0&sync_timestamp=1600441238751&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-2.6.12.tgz",
       "integrity": "sha1-9evU+mvShpQD4pqJau1JBEVskSM="
     },
+    "vue-baidu-map": {
+      "version": "0.21.22",
+      "resolved": "https://registry.npmjs.org/vue-baidu-map/-/vue-baidu-map-0.21.22.tgz",
+      "integrity": "sha512-WQMPCih4UTh0AZCKKH/OVOYnyAWjfRNeK6BIeoLmscyY5aF8zzlJhz/NOHLb3mdztIpB0Z6aohn4Jd9mfCSjQw==",
+      "requires": {
+        "bmaplib.curveline": "^1.0.0",
+        "bmaplib.heatmap": "^1.0.4",
+        "bmaplib.lushu": "^1.0.7",
+        "bmaplib.markerclusterer": "^1.0.13",
+        "markdown-it": "^8.4.0"
+      }
+    },
     "vue-clipboard2": {
       "version": "0.3.1",
       "resolved": "https://registry.npm.taobao.org/vue-clipboard2/download/vue-clipboard2-0.3.1.tgz",
diff --git a/web_src/package.json b/web_src/package.json
index d99d7bc..4a9f766 100644
--- a/web_src/package.json
+++ b/web_src/package.json
@@ -19,6 +19,7 @@
     "moment": "^2.29.1",
     "postcss-pxtorem": "^5.1.1",
     "vue": "^2.6.11",
+    "vue-baidu-map": "^0.21.22",
     "vue-clipboard2": "^0.3.1",
     "vue-cookies": "^1.7.4",
     "vue-router": "^3.1.6"
diff --git a/web_src/src/components/GeoConvertTools.js b/web_src/src/components/GeoConvertTools.js
new file mode 100644
index 0000000..6866249
--- /dev/null
+++ b/web_src/src/components/GeoConvertTools.js
@@ -0,0 +1,250 @@
+/**
+ * 缁忕含搴﹁浆鎹�
+ */
+export default {
+  PI: 3.1415926535897932384626,  
+  //PI: 3.14159265358979324,
+  x_pi: (3.1415926535897932384626 * 3000.0) / 180.0,
+  delta: function (lat, lng) {
+    // Krasovsky 1940
+    //
+    // a = 6378245.0, 1/f = 298.3
+    // b = a * (1 - f)
+    // ee = (a^2 - b^2) / a^2;
+    var a = 6378245.0; //  a: 鍗槦妞悆鍧愭爣鎶曞奖鍒板钩闈㈠湴鍥惧潗鏍囩郴鐨勬姇褰卞洜瀛愩��
+    var ee = 0.00669342162296594323; //  ee: 妞悆鐨勫亸蹇冪巼銆�
+    var dLat = this.transformLat(lng - 105.0, lat - 35.0);
+    var dLng = this.transformLng(lng - 105.0, lat - 35.0);
+    var radLat = (lat / 180.0) * this.PI;
+    var magic = Math.sin(radLat);
+    magic = 1 - ee * magic * magic;
+    var sqrtMagic = Math.sqrt(magic);
+    dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * this.PI);
+    dLng = (dLng * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * this.PI);
+    return {
+      lat: dLat,
+      lng: dLng
+    };
+  },
+  /**
+   * WGS-84 to GCJ-02 GPS鍧愭爣杞腑鍥藉潗鏍�
+   * @param  {number} wgsLat GPS绾害
+   * @param  {number} wgsLng GPS缁忓害
+   * @return {object}        杩斿洖涓浗鍧愭爣缁忕含搴﹀璞�
+   */
+  GPSToChina: function (wgsLat, wgsLng) {
+    if (this.outOfChina(wgsLat, wgsLng)) return {
+      lat: wgsLat,
+      lng: wgsLng
+    };
+    var d = this.delta(wgsLat, wgsLng);
+    return {
+      lat: Number(wgsLat) + Number(d.lat),
+      lng: Number(wgsLng) + Number(d.lng)
+    };
+  },
+  /**
+   * GCJ-02 to WGS-84 涓浗鏍囧噯鍧愭爣杞珿PS鍧愭爣
+   * @param  {number} gcjLat 涓浗鏍囧噯鍧愭爣绾害
+   * @param  {number} gcjLng 涓浗鏍囧噯鍧愭爣缁忓害
+   * @return {object}        杩斿洖GPS缁忕含搴﹀璞�
+   */
+  chinaToGPS: function (gcjLat, gcjLng) {
+    if (this.outOfChina(gcjLat, gcjLng)) return {
+      lat: gcjLat,
+      lng: gcjLng
+    };
+    var d = this.delta(gcjLat, gcjLng);
+    return {
+      lat: Number(gcjLat) - Number(d.lat),
+      lng: Number(gcjLng) - Number(d.lng)
+    };
+  },
+  /**
+   * GCJ-02 to WGS-84 exactly 涓浗鏍囧噯鍧愭爣杞珿PS鍧愭爣(绮剧‘)
+   * @param  {number} gcjLat  涓浗鏍囧噯鍧愭爣绾害
+   * @param  {number} gcjLng  涓浗鏍囧噯鍧愭爣缁忓害
+   * @return {object}         杩斿洖GPS缁忕含搴﹀璞�(绮剧‘)
+   */
+  chinaToGPSExact: function (gcjLat, gcjLng) {
+    var initDelta = 0.01;
+    var threshold = 0.000000001;
+    var dLat = initDelta,
+      dLng = initDelta;
+    var mLat = gcjLat - dLat,
+      mLng = gcjLng - dLng;
+    var pLat = gcjLat + dLat,
+      pLng = gcjLng + dLng;
+    var wgsLat,
+      wgsLng,
+      i = 0;
+    while (1) {
+      wgsLat = (mLat + pLat) / 2;
+      wgsLng = (mLng + pLng) / 2;
+      var tmp = this.gcj_encrypt(wgsLat, wgsLng);
+      dLat = tmp.lat - gcjLat;
+      dLng = tmp.lng - gcjLng;
+      if (Math.abs(dLat) < threshold && Math.abs(dLng) < threshold) break;
+
+      if (dLat > 0) pLat = wgsLat;
+      else mLat = wgsLat;
+      if (dLng > 0) pLng = wgsLng;
+      else mLng = wgsLng;
+
+      if (++i > 10000) break;
+    }
+    //console.log(i);
+    return {
+      lat: wgsLat,
+      lng: wgsLng
+    };
+  },
+  /**
+   * GCJ-02 to BD-09 涓浗鏍囧噯鍧愭爣杞櫨搴﹀潗鏍�(绮剧‘)
+   * @param  {number} gcjLat  涓浗鏍囧噯鍧愭爣绾害
+   * @param  {number} gcjLng  涓浗鏍囧噯鍧愭爣缁忓害
+   * @return {object}         杩斿洖鐧惧害缁忕含搴﹀璞�
+   */
+  chinaToBaidu: function (gcjLat, gcjLng) {
+    var x = gcjLng,
+      y = gcjLat;
+    var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
+    var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
+    var bdLng = z * Math.cos(theta) + 0.0065;
+    var bdLat = z * Math.sin(theta) + 0.006;
+    return {
+      lat: bdLat,
+      lng: bdLng
+    };
+  },
+  /**
+   * BD-09 to GCJ-02 鐧惧害鍧愭爣杞腑鍥芥爣鍑嗗潗鏍�
+   * @param  {number} bdLat  鐧惧害鍧愭爣绾害
+   * @param  {number} bdLng  鐧惧害鍧愭爣缁忓害
+   * @return {object}        杩斿洖涓浗鏍囧噯缁忕含搴﹀璞�
+   */
+  baiduToChina: function (bdLat, bdLng) {
+    var x = bdLng - 0.0065,
+      y = bdLat - 0.006;
+    var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
+    var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
+    var gcjLng = z * Math.cos(theta);
+    var gcjLat = z * Math.sin(theta);
+    return {
+      lat: gcjLat,
+      lng: gcjLng
+    };
+  },
+  /**
+   * BD-09 to GCJ-02 鐧惧害鍧愭爣杞琯ps鍧愭爣
+   * @param  {number} bdLat  鐧惧害鍧愭爣绾害
+   * @param  {number} bdLng  鐧惧害鍧愭爣缁忓害
+   * @return {object}        杩斿洖gps缁忕含搴﹀璞�
+   */
+  baiduToGPS: function (bdLat, bdLng) {
+    let china = this.baiduToChina(bdLat, bdLng);
+    return this.chinaToGPS(china.lat, china.lng);
+  },
+  /**
+   * WGS-84 to to BD-09 GPS鍧愭爣杞珺aidu鍧愭爣
+   * @param  {number} gpsLat GPS绾害
+   * @param  {number} gpsLng GPS缁忓害
+   * @return {object}        杩斿洖鐧惧害缁忕含搴﹀璞�
+   */
+  GPSToBaidu: function (gpsLat, gpsLng) {
+    var china = this.GPSToChina(gpsLat, gpsLng);
+    return this.chinaToBaidu(china.lat, china.lng);
+  },
+  /**
+   * WGS-84 to Web mercator GPS鍧愭爣杞ⅷ鍗℃墭鍧愭爣
+   * @param  {number} wgsLat GPS绾害
+   * @param  {number} wgsLng GPS缁忓害
+   * @return {object}        杩斿洖澧ㄥ崱鎵樼粡绾害瀵硅薄
+   */
+  GPSToMercator: function (wgsLat, wgsLng) {
+    var x = (wgsLng * 20037508.34) / 180;
+    var y = Math.log(Math.tan(((90 + wgsLat) * this.PI) / 360)) / (this.PI / 180);
+    y = (y * 20037508.34) / 180;
+    return {
+      lat: y,
+      lng: x
+    };
+    /*
+    if ((Math.abs(wgsLng) > 180 || Math.abs(wgsLat) > 90))
+        return null;
+    var x = 6378137.0 * wgsLng * 0.017453292519943295;
+    var a = wgsLat * 0.017453292519943295;
+    var y = 3189068.5 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));
+    return {'lat' : y, 'lng' : x};
+    //*/
+  },
+  /**
+   * Web mercator to WGS-84 澧ㄥ崱鎵樺潗鏍囪浆GPS鍧愭爣
+   * @param  {number} mercatorLat 澧ㄥ崱鎵樼含搴�
+   * @param  {number} mercatorLng 澧ㄥ崱鎵樼粡搴�
+   * @return {object}             杩斿洖GPS缁忕含搴﹀璞�
+   */
+  mercatorToGPS: function (mercatorLat, mercatorLng) {
+    var x = (mercatorLng / 20037508.34) * 180;
+    var y = (mercatorLat / 20037508.34) * 180;
+    y = (180 / this.PI) * (2 * Math.atan(Math.exp((y * this.PI) / 180)) - this.PI / 2);
+    return {
+      lat: y,
+      lng: x
+    };
+    /*
+    if (Math.abs(mercatorLng) < 180 && Math.abs(mercatorLat) < 90)
+        return null;
+    if ((Math.abs(mercatorLng) > 20037508.3427892) || (Math.abs(mercatorLat) > 20037508.3427892))
+        return null;
+    var a = mercatorLng / 6378137.0 * 57.295779513082323;
+    var x = a - (Math.floor(((a + 180.0) / 360.0)) * 360.0);
+    var y = (1.5707963267948966 - (2.0 * Math.atan(Math.exp((-1.0 * mercatorLat) / 6378137.0)))) * 57.295779513082323;
+    return {'lat' : y, 'lng' : x};
+    //*/
+  },
+  /**
+   * 涓ょ偣涔嬮棿鐨勮窛绂�
+   * @param  {number} latA 璧风偣绾害
+   * @param  {number} lngA 璧风偣缁忓害
+   * @param  {number} latB 缁堢偣绾害
+   * @param  {number} lngB 缁堢偣缁忓害
+   * @return {number}      杩斿洖璺濈(绫�)
+   */
+  distance: function (latA, lngA, latB, lngB) {
+    var earthR = 6371000;
+    var x = Math.cos((latA * this.PI) / 180) * Math.cos((latB * this.PI) / 180) * Math.cos(((lngA - lngB) * this.PI) / 180);
+    var y = Math.sin((latA * this.PI) / 180) * Math.sin((latB * this.PI) / 180);
+    var s = x + y;
+    if (s > 1) s = 1;
+    if (s < -1) s = -1;
+    var alpha = Math.acos(s);
+    var distance = alpha * earthR;
+    return distance;
+  },
+  /**
+   * 鏄惁鍦ㄤ腑鍥戒箣澶�
+   * @param  {number} lat 绾害
+   * @param  {number} lng 缁忓害
+   * @return {boolean]}     杩斿洖缁撴灉鐪熸垨鍋�
+   */
+  outOfChina: function (lat, lng) {
+    if (lat < 72.004 || lat > 137.8347) return true;
+    if (lng < 0.8293 || lng > 55.8271) return true;
+    return false;
+  },
+  transformLat: function (x, y) {
+    var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
+    ret += ((20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0) / 3.0;
+    ret += ((20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin((y / 3.0) * this.PI)) * 2.0) / 3.0;
+    ret += ((160.0 * Math.sin((y / 12.0) * this.PI) + 320 * Math.sin((y * this.PI) / 30.0)) * 2.0) / 3.0;
+    return ret;
+  },
+  transformLng: function (x, y) {
+    var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
+    ret += ((20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0) / 3.0;
+    ret += ((20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin((x / 3.0) * this.PI)) * 2.0) / 3.0;
+    ret += ((150.0 * Math.sin((x / 12.0) * this.PI) + 300.0 * Math.sin((x / 30.0) * this.PI)) * 2.0) / 3.0;
+    return ret;
+  }
+};
diff --git a/web_src/src/components/Login.vue b/web_src/src/components/Login.vue
index 160197a..2a91886 100644
--- a/web_src/src/components/Login.vue
+++ b/web_src/src/components/Login.vue
@@ -29,14 +29,13 @@
   	}
   },
   created(){
-  var that = this;
-  document.onkeydown = function(e) {
-    var key = window.event.keyCode;
-    if (key == 13) {
-      that.login();
+    var that = this;
+    document.onkeydown = function(e) {
+      var key = window.event.keyCode;
+      if (key == 13) {
+        that.login();
+      }
     }
-  }
-
   },
   methods:{
 
@@ -70,6 +69,7 @@
           if (res.data == "success") {
             that.$cookies.set("session", {"username": that.username}) ;
             //鐧诲綍鎴愬姛鍚�
+            that.cancelEnterkeyDefaultAction();
             that.$router.push('/');
           }else{
             that.isLoging = false;
@@ -84,18 +84,23 @@
         that.$message.error(error.response.statusText);
         that.isLoging = false;
       });
-
-
-
     },
-     setCookie: function (cname, cvalue, exdays) {
-                var d = new Date();
-                d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
-                var expires = "expires=" + d.toUTCString();
-                console.info(cname + "=" + cvalue + "; " + expires);
-                document.cookie = cname + "=" + cvalue + "; " + expires;
-                console.info(document.cookie);
-            },
+    setCookie: function (cname, cvalue, exdays) {
+      var d = new Date();
+      d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
+      var expires = "expires=" + d.toUTCString();
+      console.info(cname + "=" + cvalue + "; " + expires);
+      document.cookie = cname + "=" + cvalue + "; " + expires;
+      console.info(document.cookie);
+    },
+    cancelEnterkeyDefaultAction: function() {
+        document.onkeydown = function(e) {
+        var key = window.event.keyCode;
+        if (key == 13) {
+          return false;
+        }
+      }
+    }
   }
 }
 </script>
diff --git a/web_src/src/components/UiHeader.vue b/web_src/src/components/UiHeader.vue
index 0537a87..bafc209 100644
--- a/web_src/src/components/UiHeader.vue
+++ b/web_src/src/components/UiHeader.vue
@@ -34,7 +34,7 @@
             let that = this;
             if (this.alarmNotify) {
                 console.log("鐢宠SSE鎺ㄩ�丄PI璋冪敤锛屾祻瑙堝櫒ID: " + this.$browserId);
-                this.sseSource = new EventSource('/api/emit?browserId=' + this.$browserId); 
+                this.sseSource = new EventSource('/api/emit?browserId=' + this.$browserId);
         	    this.sseSource.addEventListener('message', function(evt) {
                     that.$notify({
                         title: '鏀跺埌鎶ヨ淇℃伅',
@@ -59,7 +59,7 @@
                 this.sseSource.removeEventListener('message', null);
                 this.sseSource.removeEventListener('error', null);
                 this.sseSource.close();
-            } 
+            }
         }
     },
     mounted() {
diff --git a/web_src/src/components/devicePosition.vue b/web_src/src/components/devicePosition.vue
new file mode 100644
index 0000000..f6cf19e
--- /dev/null
+++ b/web_src/src/components/devicePosition.vue
@@ -0,0 +1,388 @@
+<template>
+    <div id="devicePosition" style="height: 100%">
+        <el-container style="height: 100%">
+            <el-header>
+                <uiHeader></uiHeader>
+            </el-header>
+            <el-main>
+                <div style="background-color: #ffffff; position: relative; padding: 1rem 0.5rem 0.5rem 0.5rem; text-align: center;">
+                    <span style="font-size: 1rem; font-weight: 500">璁惧瀹氫綅 ({{ parentChannelId == 0 ? deviceId : parentChannelId }})</span>
+                </div>
+                <div style="background-color: #ffffff; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left; font-size: 14px;">
+                    <el-button icon="el-icon-arrow-left" size="mini" style="margin-right: 1rem" type="primary" @click="showDevice">杩斿洖</el-button>
+                    <!-- <span class="demonstration">浠�</span> -->
+                    <el-date-picker v-model="searchFrom" type="datetime" placeholder="閫夋嫨寮�濮嬫棩鏈熸椂闂�" default-time="00:00:00" size="mini" style="width: 11rem;" align="right" :picker-options="pickerOptions"></el-date-picker>
+                    <el-date-picker v-model="searchTo" type="datetime" placeholder="閫夋嫨缁撴潫鏃ユ湡鏃堕棿" default-time="00:00:00" size="mini" style="width: 11rem;" align="right" :picker-options="pickerOptions"></el-date-picker>
+                    <el-button-group>
+                        <el-button icon="el-icon-search" size="mini" type="primary" @click="showHistoryPath">鍘嗗彶杞ㄨ抗</el-button>
+                        <el-button icon="el-icon-search" size="mini" style="margin-right: 1rem" type="primary" @click="showLatestPosition">鏈�鏂颁綅缃�</el-button>
+                    </el-button-group>
+                    <el-tag style="width: 5rem; text-align: center" size="medium">杩囨湡鏃堕棿</el-tag>
+                    <el-input-number size="mini" v-model="expired" :min="300" :controls="false" style="width: 4rem;"></el-input-number>
+                    <el-tag style="width: 5rem; text-align: center" size="medium">涓婃姤鍛ㄦ湡</el-tag>
+                    <el-input-number size="mini" v-model="interval" :min="1" :controls="false" style="width: 4rem;"></el-input-number>
+                    <el-button-group>
+                        <el-button icon="el-icon-search" size="mini" type="primary" @click="subscribeMobilePosition">浣嶇疆璁㈤槄</el-button>
+                        <el-button icon="el-icon-search" size="mini" type="primary" @click="unSubscribeMobilePosition">鍙栨秷璁㈤槄</el-button>
+                    </el-button-group>
+                    <el-checkbox size="mini" style="margin-right: 1rem; float: right" v-model="autoList" @change="autoListChange" >鑷姩鍒锋柊</el-checkbox>
+                </div>
+                <div class="mapContainer" style="background-color: #ffffff; position: relative; padding: 1rem 0.5rem 0.5rem 0.5rem; text-align: center; height: calc(100% - 10rem);">
+                    <div class="baidumap" id="allmap"></div>
+                </div>
+            </el-main>
+        </el-container>
+    </div>
+</template>
+
+<script>
+import uiHeader from "./UiHeader.vue";
+import moment from "moment";
+import geoTools from "./GeoConvertTools.js";
+export default {
+    name: "devicePosition",
+    components: {
+        uiHeader,
+    },
+    data() {
+        return {
+            pickerOptions: {
+                shortcuts: [{
+                    text: '浠婂ぉ',
+                    onClick(picker) {
+                        picker.$emit('pick', new Date());
+                    }
+                }, {
+                    text: '鏄ㄥぉ',
+                    onClick(picker) {
+                        const date = new Date();
+                        date.setTime(date.getTime() - 3600 * 1000 * 24);
+                        picker.$emit('pick', date);
+                    }
+                }, {
+                    text: '涓�鍛ㄥ墠',
+                    onClick(picker) {
+                        const date = new Date();
+                        date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
+                        picker.$emit('pick', date);
+                    }
+                }]
+            },
+            deviceId: this.$route.params.deviceId,
+            showHistoryPosition: false, //鏄剧ず鍘嗗彶杞ㄨ抗
+            startTime: null,
+            endTime: null,
+            searchFrom: null,
+            searchTo: null,
+            expired: 600,
+            interval: 5,
+            mobilePositionList: [],
+            mapPointList: [],
+            parentChannelId: this.$route.params.parentChannelId,
+            updateLooper: 0, //鏁版嵁鍒锋柊杞鏍囧織
+            total: 0,
+            beforeUrl: "/videoList",
+            isLoging: false,
+            autoList: false,
+        };
+    },
+    mounted() {
+        this.initData();
+        this.initBaiduMap();
+        if (this.autoList) {
+            this.updateLooper = setInterval(this.initData, 5000);
+        }
+    },
+    destroyed() {
+        // this.$destroy("videojs");
+        clearTimeout(this.updateLooper);
+    },
+    methods: {
+        initData: function () {
+            // if (this.parentChannelId == "" || this.parentChannelId == 0) {
+            //     this.getDeviceChannelList();
+            // } else {
+            //     this.showSubchannels();
+            // }
+        },
+        initParam: function () {
+            // this.deviceId = this.$route.params.deviceId;
+            // this.parentChannelId = this.$route.params.parentChannelId;
+            // this.currentPage = parseInt(this.$route.params.page);
+            // this.count = parseInt(this.$route.params.count);
+            // if (this.parentChannelId == "" || this.parentChannelId == 0) {
+            //     this.beforeUrl = "/videoList";
+            // }
+        },
+        initBaiduMap() {
+            this.map = new BMap.Map("allmap"); // 鍒涘缓鍦板浘瀹炰緥
+            let points = [];
+            let point = new BMap.Point(116.231398, 39.567445); // 鍒涘缓鐐瑰潗鏍�
+            this.map.centerAndZoom(point, 5); // 鍒濆鍖栧湴鍥撅紝璁剧疆涓績鐐瑰潗鏍囧拰鍦板浘绾у埆
+            this.map.enableScrollWheelZoom(true); //寮�鍚紶鏍囨粴杞缉鏀�
+            this.map.addControl(new BMap.NavigationControl());
+            this.map.addControl(new BMap.ScaleControl());
+            this.map.addControl(new BMap.OverviewMapControl());
+            this.map.addControl(new BMap.MapTypeControl());
+            //map.setMapStyle({ style: 'midnight' }) //鍦板浘椋庢牸
+        },
+        currentChange: function (val) {
+            // var url = `/${this.$router.currentRoute.name}/${this.deviceId}/${this.parentChannelId}/${this.count}/${val}`;
+            // console.log(url);
+            // this.$router.push(url).then(() => {
+            //     this.initParam();
+            //     this.initData();
+            // });
+        },
+        handleSizeChange: function (val) {
+            // var url = `/${this.$router.currentRoute.name}/${this.$router.params.deviceId}/${this.$router.params.parentChannelId}/${val}/1`;
+            // this.$router.push(url).then(() => {
+            //     this.initParam();
+            //     this.initData();
+            // });
+        },
+        showDevice: function () {
+            this.$router.push(this.beforeUrl).then(() => {
+                this.initParam();
+                this.initData();
+            });
+        },
+        autoListChange: function () {
+            if (this.autoList) {
+                this.updateLooper = setInterval(this.initData, 1500);
+            } else {
+                window.clearInterval(this.updateLooper);
+            }
+        },
+        showHistoryPath: function () {
+            this.map.clearOverlays();
+            this.mapPointList = [];
+            this.mobilePositionList = [];
+            if (!!this.searchFrom) {
+                this.startTime = this.toGBString(this.searchFrom);
+                console.log(this.startTime);
+            } else{
+                this.startTime = null;
+            }
+            if (!!this.searchTo) {
+                this.endTime = this.toGBString(this.searchTo);
+                console.log(this.endTime);
+            } else {
+                this.endTime = null;
+            }
+            let self = this;
+            this.$axios.get(`/api/positions/${this.deviceId}/history`, {
+                params: {
+                    start: self.startTime,
+                    end: self.endTime,
+                },
+            })
+            .then(function (res) {
+                self.total = res.data.length;
+                self.mobilePositionList = res.data;
+                console.log(self.mobilePositionList);
+                if (self.total == 0) {
+                    self.$message({
+                        showClose: true,
+						message: '鏈壘鍒扮鍚堟潯浠剁殑绉诲姩浣嶇疆淇℃伅',
+						type: 'error'
+					});
+                } else {
+                    self.$nextTick(() => {
+                        self.showMarkPoints(self);
+                    });
+                }
+            })
+            .catch(function (error) {
+                console.log(error);
+            });
+        },
+        showLatestPosition: function() {
+            this.map.clearOverlays();
+            this.mapPointList = [];
+            this.mobilePositionList = [];
+            let self = this;
+            this.$axios.get(`/api/positions/${this.deviceId}/latest`)
+            .then(function (res) {
+                console.log(res.data);
+                self.total = res.data.length;
+                self.mobilePositionList.push(res.data);
+                console.log(self.mobilePositionList);
+                if (self.total == 0) {
+                    self.$message({
+                        showClose: true,
+						message: '鏈壘鍒扮鍚堟潯浠剁殑绉诲姩浣嶇疆淇℃伅',
+						type: 'error'
+					});
+                } else {
+                    self.$nextTick(() => {
+                        self.showMarkPoints(self);
+                    });
+                }
+            })
+            .catch(function (error) {
+                console.log(error);
+            });
+        },
+        subscribeMobilePosition: function() {
+            let self = this;
+            this.$axios.get(`/api/positions/${this.deviceId}/subscribe`, {
+                params: {
+                    expires: self.expired,
+                    interval: self.interval,
+                },
+            })
+            .then(function (res) {
+                console.log(res.data);
+            })
+            .catch(function (error) {
+                console.log(error);
+            });
+        },
+        unSubscribeMobilePosition: function() {
+            let self = this;
+            this.$axios.get(`/api/positions/${this.deviceId}/subscribe`, {
+                params: {
+                    expires: 0,
+                    interval: self.interval,
+                },
+            })
+            .then(function (res) {
+                console.log(res.data);
+            })
+            .catch(function (error) {
+                console.log(error);
+            });
+        },
+        toGBString: function (dateTime) {
+            return (
+                dateTime.getFullYear() + 
+                "-" + this.twoDigits(dateTime.getMonth() + 1) +
+                "-" + this.twoDigits(dateTime.getDate()) +
+                "T" + this.twoDigits(dateTime.getHours()) +
+                ":" + this.twoDigits(dateTime.getMinutes()) +
+                ":" + this.twoDigits(dateTime.getSeconds())
+            );
+        },
+        twoDigits: function (num) {
+            if (num < 10) {
+                return "0" + num;
+            } else {
+                return "" + num;
+            }
+        },
+        showMarkPoints: function(self) {
+            let that = self;
+            let npointJ = null;
+            let npointW = null;
+            let point = null;
+            for (let i = 0; i < self.mobilePositionList.length; i++) {
+                if (self.mobilePositionList[i].geodeticSystem == "BD-09") {
+                    npointJ = self.mobilePositionList[i].cnLng;
+                    npointW = self.mobilePositionList[i].cnLat;
+                    point = new BMap.Point(npointJ, npointW);
+                } else {
+                    npointJ = self.mobilePositionList[i].longitude;
+                    npointW = self.mobilePositionList[i].latitude;
+                    let bd2 = geoTools.GPSToBaidu(npointJ, npointW);
+                    point = new BMap.Point(bd2.lat, bd2.lng);
+                }
+                self.mapPointList.push(point);
+                let marker = new BMap.Marker(point); // 鍒涘缓鏍囨敞
+                self.map.addOverlay(marker); // 灏嗘爣娉ㄦ坊鍔犲埌鍦板浘涓�
+                //鎻愮ず淇℃伅  鍙互瑙f瀽 HTML鏍囩浠ュ強CSS
+                let infoWindow = new BMap.InfoWindow(`<p style='text-align:left;font-weight:800'>璁惧: ${self.mobilePositionList[i].deviceId}</p>
+                            <p style='text-align:left;font-weight:0'>鏃堕棿: ${self.mobilePositionList[i].time}</p>`);
+                // 榧犳爣绉讳笂鏍囨敞鐐硅鍙戠敓鐨勪簨
+                marker.addEventListener("mouseover", function () {
+                    this.openInfoWindow(infoWindow);
+                });
+                // 榧犳爣绉诲紑鏍囨敞鐐硅鍙戠敓鐨勪簨
+                marker.addEventListener("mouseout", function () {
+                    this.closeInfoWindow(infoWindow);
+                });
+                // 榧犳爣鐐瑰嚮鏍囨敞鐐硅鍙戠敓鐨勪簨鎯�
+                marker.addEventListener("click", function () {
+                    alert("鐐瑰嚮");
+                });
+            }
+            let view = that.map.getViewport(eval(self.mapPointList));
+            that.map.centerAndZoom(view.center, view.zoom);
+        },
+    },
+};
+</script>
+
+<style>
+.videoList {
+  display: flex;
+  flex-wrap: wrap;
+  align-content: flex-start;
+}
+
+.video-item {
+  position: relative;
+  width: 15rem;
+  height: 10rem;
+  margin-right: 1rem;
+  background-color: #000000;
+}
+
+.video-item-img {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  margin: auto;
+  width: 100%;
+  height: 100%;
+}
+
+.video-item-img:after {
+  content: "";
+  display: inline-block;
+  position: absolute;
+  z-index: 2;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  margin: auto;
+  width: 3rem;
+  height: 3rem;
+  background-image: url("../assets/loading.png");
+  background-size: cover;
+  background-color: #000000;
+}
+
+.video-item-title {
+  position: absolute;
+  bottom: 0;
+  color: #000000;
+  background-color: #ffffff;
+  line-height: 1.5rem;
+  padding: 0.3rem;
+  width: 14.4rem;
+}
+
+.baidumap {
+  width: 100%;
+  height: 100%;
+  border: none;
+  position: absolute;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  margin: auto;
+}
+
+/* 鍘婚櫎鐧惧害鍦板浘鐗堟潈閭h瀛� 鍜� 鐧惧害logo */
+.baidumap > .BMap_cpyCtrl {
+  display: none !important;
+}
+.baidumap > .anchorBL {
+  display: none !important;
+}
+</style>
diff --git a/web_src/src/components/videoList.vue b/web_src/src/components/videoList.vue
index 01dae2c..8a3f1c0 100644
--- a/web_src/src/components/videoList.vue
+++ b/web_src/src/components/videoList.vue
@@ -11,7 +11,7 @@
 						<el-button icon="el-icon-refresh-right" circle size="mini" :loading="getDeviceListLoading" @click="getDeviceList()"></el-button>
 					</div>
 				</div>
-				<devicePlayer ref="devicePlayer"></devicePlayer>
+				<!-- <devicePlayer ref="devicePlayer"></devicePlayer> -->
 				<!--璁惧鍒楄〃-->
 				<el-table :data="deviceList" border style="width: 100%" :height="winHeight">
 					<el-table-column prop="name" label="鍚嶇О" width="180" align="center">
@@ -40,7 +40,7 @@
 					</el-table-column>
 					<el-table-column prop="channelCount" label="閫氶亾鏁�" align="center">
 					</el-table-column>
-					<el-table-column label="鐘舵��" width="180" align="center">
+					<el-table-column label="鐘舵��" width="80" align="center">
 						<template slot-scope="scope">
 							<div slot="reference" class="name-wrapper">
 								<el-tag size="medium" v-if="scope.row.online == 1">鍦ㄧ嚎</el-tag>
@@ -49,11 +49,15 @@
 						</template>
 					</el-table-column>
 
-					<el-table-column label="鎿嶄綔" width="240" align="center" fixed="right">
+					<el-table-column label="鎿嶄綔" width="360" align="center" fixed="right">
 						<template slot-scope="scope">
-							<el-button size="mini" :ref="scope.row.deviceId + 'refbtn' " icon="el-icon-refresh"  @click="refDevice(scope.row)">鍒锋柊閫氶亾</el-button>
-							<el-button size="mini" icon="el-icon-s-open" v-bind:disabled="scope.row.online==0"  type="primary" @click="showChannelList(scope.row)">鏌ョ湅閫氶亾</el-button>
-						</template>
+							<el-button size="mini" :ref="scope.row.deviceId + 'refbtn' " icon="el-icon-refresh"  @click="refDevice(scope.row)">鍒锋柊</el-button>
+							<el-button-group>
+							<el-button size="mini" icon="el-icon-video-camera-solid" v-bind:disabled="scope.row.online==0"  type="primary" @click="showChannelList(scope.row)">閫氶亾</el-button>
+							<el-button size="mini" icon="el-icon-location" v-bind:disabled="scope.row.online==0"  type="primary" @click="showDevicePosition(scope.row)">瀹氫綅</el-button>
+							<el-button size="mini" icon="el-icon-s-tools" v-bind:disabled="scope.row.online==0"  type="primary">鎺у埗</el-button>
+							</el-button-group>
+							</template>
 					</el-table-column>
 				</el-table>
 				<el-pagination
@@ -73,7 +77,7 @@
 </template>
 
 <script>
-	 import uiHeader from './UiHeader.vue'
+	import uiHeader from './UiHeader.vue'
 	export default {
 		name: 'app',
 		components: {
@@ -155,7 +159,10 @@
 				console.log(JSON.stringify(row))
 				this.$router.push(`/channelList/${row.deviceId}/0/15/1`);
 			},
-
+			showDevicePosition: function(row) {
+				console.log(JSON.stringify(row))
+				this.$router.push(`/devicePosition/${row.deviceId}/0/15/1`);
+			},
 
 			//gb28181骞冲彴瀵规帴
 			//鍒锋柊璁惧淇℃伅
@@ -191,18 +198,18 @@
 			},
 			//閫氱煡璁惧涓婁紶濯掍綋娴�
 			sendDevicePush: function(itemData) {
-				let deviceId = this.currentDevice.deviceId;
-				let channelId = itemData.channelId;
-				console.log("閫氱煡璁惧鎺ㄦ祦1锛�" + deviceId + " : " + channelId);
-				let that = this;
-				this.$axios({
-					method: 'get',
-					url: '/api/play/' + deviceId + '/' + channelId
-				}).then(function(res) {
-					let ssrc = res.data.ssrc;
-					that.$refs.devicePlayer.play(ssrc,deviceId,channelId);
-				}).catch(function(e) {
-				});
+				// let deviceId = this.currentDevice.deviceId;
+				// let channelId = itemData.channelId;
+				// console.log("閫氱煡璁惧鎺ㄦ祦1锛�" + deviceId + " : " + channelId);
+				// let that = this;
+				// this.$axios({
+				// 	method: 'get',
+				// 	url: '/api/play/' + deviceId + '/' + channelId
+				// }).then(function(res) {
+				// 	let ssrc = res.data.ssrc;
+				// 	that.$refs.devicePlayer.play(ssrc,deviceId,channelId);
+				// }).catch(function(e) {
+				// });
 			},
       transportChange: function (row) {
         console.log(row);
diff --git a/web_src/src/main.js b/web_src/src/main.js
index 03bb4a4..197fa17 100644
--- a/web_src/src/main.js
+++ b/web_src/src/main.js
@@ -12,21 +12,21 @@
 import { Notification } from 'element-ui';
 import Fingerprint2 from 'fingerprintjs2';
 
-// 生成唯一ID
+// 鐢熸垚鍞竴ID
 Fingerprint2.get(function(components) {
   const values = components.map(function(component,index) {
-    if (index === 0) { //把微信浏览器里UA的wifi或4G等网络替换成空,不然切换网络会ID不一样
+    if (index === 0) { //鎶婂井淇℃祻瑙堝櫒閲孶A鐨剋ifi鎴�4G绛夌綉缁滄浛鎹㈡垚绌�,涓嶇劧鍒囨崲缃戠粶浼欼D涓嶄竴鏍�
       return component.value.replace(/\bNetType\/\w+\b/, '');
     }
     return component.value;
   })
-  //console.log(values)  //使用的浏览器信息npm 
-  // 生成最终id
+  //console.log(values)  //浣跨敤鐨勬祻瑙堝櫒淇℃伅npm
+  // 鐢熸垚鏈�缁坕d
   let port = window.location.port;
   console.log(port);
   const fingerPrint = Fingerprint2.x64hash128(values.join(port), 31)
   Vue.prototype.$browserId = fingerPrint;
-  console.log("唯一标识码:" + fingerPrint);
+  console.log("鍞竴鏍囪瘑鐮侊細" + fingerPrint);
 });
 
 Vue.use(VueClipboard);
diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js
index 658da4e..93ef64d 100644
--- a/web_src/src/router/index.js
+++ b/web_src/src/router/index.js
@@ -4,6 +4,7 @@
 import control from '../components/control.vue'
 import videoList from '../components/videoList.vue'
 import channelList from '../components/channelList.vue'
+import devicePosition from  '../components/devicePosition.vue'
 import login from '../components/Login.vue'
 import parentPlatformList from '../components/ParentPlatformList.vue'
 
@@ -41,5 +42,10 @@
       name: 'parentPlatformList',
       component: parentPlatformList,
     },
+    {
+      path: '/devicePosition/:deviceId/:parentChannelId/:count/:page',
+      name: 'devicePosition',
+      component: devicePosition,
+    },
   ]
 })

--
Gitblit v1.8.0