From abbfef2aef1694b2b2f2b775b216f320ad645482 Mon Sep 17 00:00:00 2001
From: Lawrence <1934378145@qq.com>
Date: Wed, 20 Jan 2021 23:31:00 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8A=A5=E8=AD=A6=E5=93=8D?=
 =?UTF-8?q?=E5=BA=94=E3=80=81=E5=8F=91=E5=B8=83=E5=92=8CSSE=E6=8E=A8?=
 =?UTF-8?q?=E9=80=81=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md                                     |  1 +
 .../iot/vmp/gb28181/bean/DeviceAlarm.java     | 10 ++--
 .../iot/vmp/gb28181/event/EventPublisher.java | 12 ++++
 .../vmp/gb28181/event/alarm/AlarmEvent.java   | 26 +++++++++
 .../event/alarm/AlarmEventListener.java       | 47 ++++++++++++++++
 .../request/impl/MessageRequestProcessor.java | 39 ++++++++++---
 .../vmp/storager/dao/DeviceChannelMapper.java |  1 +
 .../vmanager/SEEController/SEEController.java | 32 +++++++++++
 web_src/src/components/UiHeader.vue           | 56 ++++++++++++++++++-
 web_src/src/main.js                           |  3 +
 10 files changed, 211 insertions(+), 16 deletions(-)
 create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java
 create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java
 create mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/SEEController/SEEController.java

diff --git a/README.md b/README.md
index 19bcd515c..724cfef7d 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ https://gitee.com/18010473990/wvp-GB28181.git
 11. 支持公网部署, 支持wvp与zlm分开部署   
 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. 报警信息处理,支持向前端推送报警信息
 
 # 待实现: 
 上级级联  
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java
index d91a1c7f7..35cb6bf4a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java
@@ -11,7 +11,7 @@ public class DeviceAlarm {
 	/**
 	 * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情-
 	 */
-	private String alarmPriorit;
+	private String alarmPriority;
 
 	/**
 	 * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,
@@ -53,12 +53,12 @@ public class DeviceAlarm {
 		this.deviceId = deviceId;
 	}
 
-	public String getAlarmPriorit() {
-		return alarmPriorit;
+	public String getAlarmPriority() {
+		return alarmPriority;
 	}
 
-	public void setAlarmPriorit(String alarmPriorit) {
-		this.alarmPriorit = alarmPriorit;
+	public void setAlarmPriority(String alarmPriority) {
+		this.alarmPriority = alarmPriority;
 	}
 
 	public String getAlarmMethod() {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
index ebf043051..50f6bc0c0 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
@@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Component;
 
+import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
+import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent;
 import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent;
 import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent;
 
@@ -31,4 +33,14 @@ public class EventPublisher {
 		outEvent.setFrom(from);
         applicationEventPublisher.publishEvent(outEvent);
     }
+	
+	/**
+	 * 设备报警事件
+	 * @param deviceAlarm
+	 */
+	public void deviceAlarmEventPublish(DeviceAlarm deviceAlarm) {
+		AlarmEvent alarmEvent = new AlarmEvent(this);
+		alarmEvent.setAlarmInfo(deviceAlarm);
+		applicationEventPublisher.publishEvent(alarmEvent);
+	}
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java
new file mode 100644
index 000000000..fecc8b1b6
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java
@@ -0,0 +1,26 @@
+package com.genersoft.iot.vmp.gb28181.event.alarm;
+
+import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @description: 报警事件
+ * @author: lawrencehj
+ * @data: 2021-01-20
+ */
+
+public class AlarmEvent extends ApplicationEvent {
+    public AlarmEvent(Object source) {
+        super(source);
+    }
+
+    private DeviceAlarm deviceAlarm;
+
+    public DeviceAlarm getAlarmInfo() {
+        return deviceAlarm;
+    }
+    
+    public void setAlarmInfo(DeviceAlarm deviceAlarm) {
+        this.deviceAlarm = deviceAlarm;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java
new file mode 100644
index 000000000..1b2f67231
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java
@@ -0,0 +1,47 @@
+package com.genersoft.iot.vmp.gb28181.event.alarm;
+
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @description: 报警事件监听
+ * @author: lawrencehj
+ * @data: 2021-01-20
+ */
+
+@Component
+public class AlarmEventListener implements ApplicationListener<AlarmEvent> {
+
+    private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class);
+
+    private static SseEmitter emitter = new SseEmitter();
+
+    public void addSseEmitters(SseEmitter sseEmitter) {
+        emitter = sseEmitter;
+    }
+
+    @Override
+    public void onApplicationEvent(AlarmEvent event) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("设备报警事件触发,deviceId:" + event.getAlarmInfo().getDeviceId() + ", "
+                    + event.getAlarmInfo().getAlarmDescription());
+        }
+        try {
+            String msg = "<strong>设备编码:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>"
+                        + "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>"
+                        + "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>"
+                        + "<br><strong>定位经度:</strong> <i>" + event.getAlarmInfo().getLongitude() + "</i>"
+                        + "<br><strong>定位纬度:</strong> <i>" + event.getAlarmInfo().getLatitude() + "</i>";
+            emitter.send(msg);
+        } catch (IOException e) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("SSE 通道已关闭");
+            }
+            // e.printStackTrace();
+        }
+    }
+}
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 fbdc7e264..73e67e58a 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
@@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
 import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
@@ -289,19 +290,41 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
 				// storager.queryChannel(deviceId)
 				return;
 			}
-			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");
+
+			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"));
 			}
-			storager.updateDevice(device);
+			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.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
+				publisher.deviceAlarmEventPublish(deviceAlarm);
 			}
 		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
 			// } catch (DocumentException e) {
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
index 57dc96d60..11b728a81 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
@@ -59,6 +59,7 @@ public interface DeviceChannelMapper {
             " WHERE 1=1 " +
             " <if test=\"hasSubChannel == true\" >  AND subCount >0</if>" +
             " <if test=\"hasSubChannel == false\" >  AND subCount=0</if>" +
+            " ORDER BY channelId ASC" +
             " </script>"})
     List<DeviceChannel> queryChannelsByDeviceId(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online);
 
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/SEEController/SEEController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/SEEController/SEEController.java
new file mode 100644
index 000000000..689b967b8
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/SEEController/SEEController.java
@@ -0,0 +1,32 @@
+package com.genersoft.iot.vmp.vmanager.SEEController;
+
+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.RequestMapping;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+/**
+ * @description: SSE推送
+ * @author: lawrencehj
+ * @data: 2021-01-20
+ */
+
+@Controller
+@RequestMapping("/api")
+public class SEEController {
+    @Autowired 
+    AlarmEventListener alarmEventListener;
+    
+   	//设置响应
+    @RequestMapping("/emit")
+    public SseEmitter emit() {
+        SseEmitter sseEmitter = new SseEmitter(0L);
+        try {
+            alarmEventListener.addSseEmitters(sseEmitter);
+        }catch (Exception e){
+            sseEmitter.completeWithError(e);
+        }
+        return sseEmitter;
+    }
+}
diff --git a/web_src/src/components/UiHeader.vue b/web_src/src/components/UiHeader.vue
index 832d69efe..f16beba86 100644
--- a/web_src/src/components/UiHeader.vue
+++ b/web_src/src/components/UiHeader.vue
@@ -4,7 +4,7 @@
             <el-menu-item index="/">控制台</el-menu-item>
             <el-menu-item index="/videoList">设备列表</el-menu-item>
             <!-- <el-menu-item index="/videoReplay">录像回看</el-menu-item> -->
-            <!-- <el-menu-item index="4">级联设置</el-menu-item> -->
+            <el-switch v-model="alarmNotify"  active-text="报警信息推送" style="display: block float: right" @change="sseControl"></el-switch>
             <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>
         </el-menu>
 	</div>
@@ -13,14 +13,64 @@
 <script>
 export default {
     name: "UiHeader",
+    components: { Notification },
+    data() {
+        return {
+            alarmNotify: true,
+            sseSource: null,
+        };
+    },
     methods:{
 
   	    loginout(){
             // 删除cookie,回到登录页面
             this.$cookies.remove("session");
             this.$router.push('/login');
+            this.sseSource.close();
         },
-    }
-}
+        beforeunloadHandler() {
+            this.sseSource.close();
+        },
+        sseControl() {
+            let that = this;
+            if (this.alarmNotify) {
+                this.sseSource = new EventSource('/api/emit'); 
+        	    this.sseSource.addEventListener('message', function(evt) {
+                    that.$notify({
+                        title: '收到报警信息',
+                        dangerouslyUseHTMLString: true,
+                        message: evt.data,
+                        type: 'warning'
+                    });
+	                console.log("收到信息:" + evt.data);
+        	    });
+	            this.sseSource.addEventListener('open', function(e) {
+        	        console.log("SSE连接打开.");
+	            }, false);
+        	    this.sseSource.addEventListener('error', function(e) {
+	                if (e.target.readyState == EventSource.CLOSED) {
+	                    console.log("SSE连接关闭");
+        	        } else {
+	                    console.log(e.target.readyState);
+        	        }
+	            }, false);
+            } else {
+                this.sseSource.removeEventListener('open', null);
+                this.sseSource.removeEventListener('message', null);
+                this.sseSource.removeEventListener('error', null);
+                this.sseSource.close();
+            } 
+        }
+    },
+    mounted() {
+        window.addEventListener('beforeunload', e => this.beforeunloadHandler(e))
+        // window.addEventListener('unload', e => this.unloadHandler(e))
+        this.sseControl();
+    },
+    destroyed() {
+        window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))
+        // window.removeEventListener('unload', e => this.unloadHandler(e))
+    },
+ }
 
 </script>
diff --git a/web_src/src/main.js b/web_src/src/main.js
index 6f88c2a99..ae2baeef2 100644
--- a/web_src/src/main.js
+++ b/web_src/src/main.js
@@ -9,10 +9,13 @@ import VueCookies from 'vue-cookies';
 import echarts from 'echarts';
 
 import VueClipboard from 'vue-clipboard2'
+import { Notification } from 'element-ui';
+
 Vue.use(VueClipboard)
 Vue.use(ElementUI);
 Vue.use(VueCookies);
 Vue.prototype.$axios = axios;
+Vue.prototype.$notify = Notification;
 
 axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : "";