From d3fa1dd2262fc311661aa675e4a30b2423c7b130 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: 星期四, 04 二月 2021 10:35:02 +0800
Subject: [PATCH] Merge pull request #47 from lawrencehj/master

---
 src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java                                         |   68 +
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java                |  513 +++++++
 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                    |   94 +
 web_src/index.html                                                                             |    1 
 web_src/src/components/Login.vue                                                               |   41 
 web_src/src/components/devicePosition.vue                                                      |  388 ++++++
 web_src/src/main.js                                                                            |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java         |   47 
 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/resources/application-dev.yml                                                         |   20 
 web_src/src/components/UiHeader.vue                                                            |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java                                 |  119 +
 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java                          |  121 ++
 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java                           |   87 +
 src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java                                        |   18 
 web_src/src/components/videoList.vue                                                           |   47 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java  |  385 ++++++
 src/main/resources/wvp.sqlite                                                                  |    0 
 src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java                  |    2 
 web_src/package.json                                                                           |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/BaiduPoint.java                               |   24 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java        |   14 
 src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java                |   47 
 README.md                                                                                      |    8 
 src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java                        |   27 
 src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java                            |   70 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java                  |   20 
 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java                         |  238 ++++
 web_src/src/router/index.js                                                                    |    6 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java |  402 +++++-
 36 files changed, 3,260 insertions(+), 249 deletions(-)

diff --git a/README.md b/README.md
index 724cfef..03ee276 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,14 @@
 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. 绉诲姩浣嶇疆鏌ヨ鍜屾樉绀猴紝鍙�氳繃閰嶇疆鏂囦欢璁剧疆绉诲姩浣嶇疆鍘嗗彶鏄惁瀛樺偍
 
 # 寰呭疄鐜帮細 
 涓婄骇绾ц仈  
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/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/transmit/SIPProcessorFactory.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
index b50cc95..b863865 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
@@ -26,6 +26,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;
@@ -132,7 +133,6 @@
 			processor.setRequestEvent(evt);
 			return processor;
 		} else if (Request.MESSAGE.equals(method)) {
-
 			MessageRequestProcessor processor = new MessageRequestProcessor();
 			processor.setRequestEvent(evt);
 			processor.setTcpSipProvider(getTcpSipProvider());
@@ -145,13 +145,27 @@
 			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;
 		}
 	}
 	
 	public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) {
-
 		Response response = evt.getResponse();
 		CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
 		String method = cseqHeader.getMethod();
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 5fd8cbc..edf8679 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
@@ -17,7 +17,15 @@
 @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/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
index 67fd996..30fbfe9 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
@@ -115,24 +115,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 +151,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 +170,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 +216,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 8f2c0dd..c89af83 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
@@ -164,4 +164,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..c174455 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
@@ -34,6 +34,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 +237,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 +274,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 +387,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 +487,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 +579,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 +670,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 +710,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 +797,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 +896,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 +926,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 +961,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 +975,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 +1063,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 +1093,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 73e67e5..c865ad3 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
@@ -10,19 +10,15 @@
 import javax.sip.message.Request;
 import javax.sip.message.Response;
 
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-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.beans.factory.annotation.Autowired;
-
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.common.StreamInfo;
 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.bean.RecordInfo;
 import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
 import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
@@ -32,17 +28,30 @@
 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.DateUtil;
+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;
-import com.genersoft.iot.vmp.common.StreamInfo;
 /**
  * @Description:MESSAGE璇锋眰澶勭悊鍣�
  * @author: swwheihei
  * @date: 2020骞�5鏈�3鏃� 涓嬪崍5:32:41
  */
+
 public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
+
+	private UserSetup userSetup = (UserSetup) SpringBeanFactory.getBean("userSetup");
 
 	private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
 
@@ -70,9 +79,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璇锋眰
@@ -91,12 +103,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);
@@ -106,16 +128,239 @@
 			}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 (!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");
+			String result = XmlUtil.getText(rootElement, "Result");
+			// 鍥炲200 OK
+			responseAck(evt);
+			//if (!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_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");
+			String result = XmlUtil.getText(rootElement, "Result");
+			// 鍥炲200 OK
+			responseAck(evt);
+			//if (!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_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");
+			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_PRESETQUERY);
+				msg.setData(json);
+				deferredResultHolder.invokeResult(msg);
+			} else {
+				// 姝ゅ鏄笂绾у彂鍑虹殑DeviceControl鎸囦护
+			}
+		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 澶勭悊DeviceInfo璁惧淇℃伅Message
 	 * 
 	 * @param evt
 	 */
@@ -123,7 +368,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) {
@@ -180,11 +425,11 @@
 					if (channelDeviceElement == null) {
 						continue;
 					}
-					String channelDeviceId = channelDeviceElement.getText();
+					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.getText().toString() : "ON";
+					String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
 					DeviceChannel deviceChannel = new DeviceChannel();
 					deviceChannel.setName(channelName);
 					deviceChannel.setChannelId(channelDeviceId);
@@ -238,15 +483,15 @@
 						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 {
+					if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {
 						deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
-					}
-					if (XmlUtil.getText(itemDevice, "Latitude") == null || XmlUtil.getText(itemDevice, "Latitude") =="") {
-						deviceChannel.setLatitude(0.00);
 					} 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);
@@ -274,8 +519,7 @@
 	}
 
 	/***
-	 * 鏀跺埌alarm璁惧鎶ヨ淇℃伅 澶勭悊
-	 * 
+	 * alarm璁惧鎶ヨ淇℃伅澶勭悊
 	 * @param evt
 	 */
 	private void processMessageAlarm(RequestEvent evt) {
@@ -283,48 +527,72 @@
 			Element rootElement = getRootElement(evt);
 			Element deviceIdElement = rootElement.element("DeviceID");
 			String deviceId = deviceIdElement.getText().toString();
+			// 鍥炲200 OK
+			responseAck(evt);
 
 			Device device = storager.queryVideoDevice(deviceId);
 			if (device == null) {
-				// TODO 涔熷彲鑳芥槸閫氶亾
-				// 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"));
-			}
-			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);
-			//cmder.catalogQuery(device, null);
-			// 鍥炲200 OK
-			responseAck(evt);
-			if (offLineDetector.isOnline(deviceId)) {
-				publisher.deviceAlarmEventPublish(deviceAlarm);
+			if (rootElement.getName().equals("Notify")) {	// 澶勭悊鎶ヨ閫氱煡
+				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 (!XmlUtil.isEmpty(deviceAlarm.getAlarmMethod())) {
+					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: 闇�瑕佸疄鐜板瓨鍌ㄦ姤璀︿俊鎭�佹姤璀﹀垎绫�
+	
+				if (offLineDetector.isOnline(deviceId)) {
+					publisher.deviceAlarmEventPublish(deviceAlarm);
+				}
+			} else if (rootElement.getName().equals("Response")) {	// 澶勭悊鎶ヨ鏌ヨ鍝嶅簲
+				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_ALARM);
+				msg.setData(json);
+				deferredResultHolder.invokeResult(msg);
 			}
 		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
 			// } catch (DocumentException e) {
@@ -350,14 +618,13 @@
 				} else {
 				}
 			}
-
 		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
 			e.printStackTrace();
 		}
 	}
 
 	/***
-	 * 鏀跺埌catalog璁惧鐩綍鍒楄〃璇锋眰 澶勭悊 TODO 杩囨湡鏃堕棿鏆傛椂鍐欐180绉掞紝鍚庣画涓嶥eferredResult瓒呮椂鏃堕棿淇濇寔涓�鑷�
+	 * 澶勭悊RecordInfo璁惧褰曞儚鍒楄〃Message璇锋眰 TODO 杩囨湡鏃堕棿鏆傛椂鍐欐180绉掞紝鍚庣画涓嶥eferredResult瓒呮椂鏃堕棿淇濇寔涓�鑷�
 	 * 
 	 * @param evt
 	 */
@@ -444,12 +711,9 @@
 			// 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("澶勭悊瀹屾垚锛岃繑鍥炵粨鏋�");
@@ -458,7 +722,11 @@
 		}
 	}
 
-
+	/**
+	 * 鏀跺埌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..8c7d969
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java
@@ -0,0 +1,385 @@
+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 SIPCommander cmder;
+
+	private IVideoManagerStorager storager;
+
+	private IRedisCatchStorage redisCatchStorage;
+
+	private EventPublisher publisher;
+
+	private RedisUtil redis;
+
+	private DeferredResultHolder deferredResultHolder;
+
+	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) {
+		this.cmder = cmder;
+	}
+
+	public void setStorager(IVideoManagerStorager storager) {
+		this.storager = storager;
+	}
+
+	public void setPublisher(EventPublisher publisher) {
+		this.publisher = publisher;
+	}
+
+	public void setRedis(RedisUtil redis) {
+		this.redis = redis;
+	}
+
+	public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) {
+		this.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/utils/NumericUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java
new file mode 100644
index 0000000..f9fbfb1
--- /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 an Integer!"); 
+            return true;
+        } catch (Exception e) { 
+            System.out.println(str + " Is not an Integer!"); 
+            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 Number!"); 
+            return true;
+        } catch (Exception e) { 
+            System.out.println(str + " Is not Number!"); 
+            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/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
index 6563754..c0eda00 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
@@ -4,6 +4,7 @@
 
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
 import com.github.pagehelper.PageInfo;
 
 /**    
@@ -151,4 +152,30 @@
 	 */
 	void cleanChannelsForDevice(String deviceId);
 
+	/**
+	 * 娣诲姞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/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
index e11c141..cc8320c 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
@@ -3,8 +3,10 @@
 import java.util.*;
 
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+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.DeviceMobilePositionMapper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.models.auth.In;
@@ -27,7 +29,10 @@
     private DeviceMapper deviceMapper;
 
 	@Autowired
-    private DeviceChannelMapper deviceChannelMapper;
+	private DeviceChannelMapper deviceChannelMapper;
+	
+	@Autowired
+	private DeviceMobilePositionMapper deviceMobilePositionMapper;
 
 
 	/**
@@ -200,11 +205,49 @@
 		return deviceMapper.update(device) > 0;
 	}
 
-
+	/**
+	 * 娓呯┖閫氶亾
+	 * @param deviceId
+	 */
 	@Override
 	public void cleanChannelsForDevice(String deviceId) {
 		int result = 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);
+	}
+
+	/**
+	 * 鏌ヨ鏈�鏂扮Щ鍔ㄤ綅缃�
+	 * @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/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..6c4f7a3
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java
@@ -0,0 +1,121 @@
+/**
+ * 璁惧璁剧疆鍛戒护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.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 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..350adaa 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
@@ -17,6 +17,7 @@
 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.utils.XmlUtil;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 
 import javax.sip.message.Response;
@@ -24,9 +25,9 @@
 @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/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/resources/application-dev.yml b/src/main/resources/application-dev.yml
index c235c81..26f5d08 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -35,18 +35,18 @@
 # 浣滀负28181鏈嶅姟鍣ㄧ殑閰嶇疆
 sip:
     # [蹇呴』淇敼] 鏈満鐨処P, 蹇呴』鏄綉鍗′笂鐨処P
-    ip: 192.168.1.44
+    ip: 192.168.0.100
     # [鍙�塢 28181鏈嶅姟鐩戝惉鐨勭鍙�
     port: 5060
     # 鏍规嵁鍥芥爣6.1.2涓瀹氾紝domain瀹滈噰鐢↖D缁熶竴缂栫爜鐨勫墠鍗佷綅缂栫爜銆傚浗鏍囬檮褰旸涓畾涔夊墠8浣嶄负涓績缂栫爜锛堢敱鐪佺骇銆佸競绾с�佸尯绾с�佸熀灞傜紪鍙风粍鎴愶紝鍙傜収GB/T 2260-2007锛�
     # 鍚庝袱浣嶄负琛屼笟缂栫爜锛屽畾涔夊弬鐓ч檮褰旸.3
     # 3701020049鏍囪瘑灞变笢娴庡崡鍘嗕笅鍖� 淇℃伅琛屼笟鎺ュ叆
     # [鍙�塢
-    domain: 3402000000
+    domain: 4401020049
     # [鍙�塢
-    id: 34020000002000000001
+    id: 44010200492000000001
     # [鍙�塢 榛樿璁惧璁よ瘉瀵嗙爜锛屽悗缁墿灞曚娇鐢ㄨ澶囧崟鐙瘑鐮�
-    password: 12345678
+    password: admin123
 
 # 鐧婚檰鐨勭敤鎴峰悕瀵嗙爜
 auth:
@@ -58,7 +58,7 @@
 #zlm鏈嶅姟鍣ㄩ厤缃�
 media:
     # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑鍐呯綉IP
-    ip: 192.168.1.44
+    ip: 192.168.0.100
     # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑鍏綉IP, 鍐呯綉閮ㄧ讲缃┖鍗冲彲
     wanIp:
     # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑hook鎵�浣跨敤鐨処P, 榛樿浣跨敤sip.ip
@@ -70,9 +70,9 @@
     # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑hook.admin_params=secret
     secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
     # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑general.streamNoneReaderDelayMS
-    streamNoneReaderDelayMS:  600000  # 鏃犱汉瑙傜湅澶氫箙鑷姩鍏抽棴娴�, -1琛ㄧず姘镐笉鑷姩鍏抽棴,鍗� 鍏抽棴鎸夐渶鎷夋祦
+    streamNoneReaderDelayMS:  18000  # 鏃犱汉瑙傜湅澶氫箙鑷姩鍏抽棴娴�, -1琛ㄧず姘镐笉鑷姩鍏抽棴,鍗� 鍏抽棴鎸夐渶鎷夋祦
     # [鍙�塢 鑷姩鐐规挱锛� 浣跨敤鍥哄畾娴佸湴鍧�杩涜鎾斁鏃讹紝濡傛灉鏈偣鎾垯鑷姩杩涜鐐规挱, 闇�瑕乺tp.enable=true
-    autoApplyPlay: true
+    autoApplyPlay: false
     # [鍙�塢 閮ㄥ垎璁惧闇�瑕佹墿灞昐DP锛岄渶瑕佹墦寮�姝よ缃�
     seniorSdp: false
     # 鍚敤udp澶氱鍙fā寮�, 璇︾粏瑙i噴鍙傝��: https://github.com/xia-chu/ZLMediaKit/wiki/GB28181%E6%8E%A8%E6%B5%81 涓嬬殑楂橀樁浣跨敤
@@ -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/src/main/resources/wvp.sqlite b/src/main/resources/wvp.sqlite
index 023ea35..ff76bcd 100644
--- a/src/main/resources/wvp.sqlite
+++ b/src/main/resources/wvp.sqlite
Binary files differ
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 282a89d..8b84ef2 100644
--- a/web_src/src/components/UiHeader.vue
+++ b/web_src/src/components/UiHeader.vue
@@ -21,7 +21,6 @@
         };
     },
     methods:{
-
   	    loginout(){
             // 鍒犻櫎cookie锛屽洖鍒扮櫥褰曢〉闈�
             this.$cookies.remove("session");
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..5e0696b 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 b8f8705..e8ff105 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'
 
 const originalPush = VueRouter.prototype.push
@@ -35,5 +36,10 @@
       name: 'channelList',
       component: channelList,
     },
+    {
+      path: '/devicePosition/:deviceId/:parentChannelId/:count/:page',
+      name: 'devicePosition',
+      component: devicePosition,
+    },
   ]
 })

--
Gitblit v1.8.0