Commit abbfef2aef1694b2b2f2b775b216f320ad645482
1 parent
2b850ba7
添加报警响应、发布和SSE推送功能
Showing
10 changed files
with
211 additions
and
16 deletions
README.md
| @@ -41,6 +41,7 @@ https://gitee.com/18010473990/wvp-GB28181.git | @@ -41,6 +41,7 @@ https://gitee.com/18010473990/wvp-GB28181.git | ||
| 41 | 11. 支持公网部署, 支持wvp与zlm分开部署 | 41 | 11. 支持公网部署, 支持wvp与zlm分开部署 |
| 42 | 12. 支持播放h265, g.711格式的流 | 42 | 12. 支持播放h265, g.711格式的流 |
| 43 | 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)) | 43 | 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)) |
| 44 | +14. 报警信息处理,支持向前端推送报警信息 | ||
| 44 | 45 | ||
| 45 | # 待实现: | 46 | # 待实现: |
| 46 | 上级级联 | 47 | 上级级联 |
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java
| @@ -11,7 +11,7 @@ public class DeviceAlarm { | @@ -11,7 +11,7 @@ public class DeviceAlarm { | ||
| 11 | /** | 11 | /** |
| 12 | * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- | 12 | * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- |
| 13 | */ | 13 | */ |
| 14 | - private String alarmPriorit; | 14 | + private String alarmPriority; |
| 15 | 15 | ||
| 16 | /** | 16 | /** |
| 17 | * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, | 17 | * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, |
| @@ -53,12 +53,12 @@ public class DeviceAlarm { | @@ -53,12 +53,12 @@ public class DeviceAlarm { | ||
| 53 | this.deviceId = deviceId; | 53 | this.deviceId = deviceId; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | - public String getAlarmPriorit() { | ||
| 57 | - return alarmPriorit; | 56 | + public String getAlarmPriority() { |
| 57 | + return alarmPriority; | ||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | - public void setAlarmPriorit(String alarmPriorit) { | ||
| 61 | - this.alarmPriorit = alarmPriorit; | 60 | + public void setAlarmPriority(String alarmPriority) { |
| 61 | + this.alarmPriority = alarmPriority; | ||
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | public String getAlarmMethod() { | 64 | public String getAlarmMethod() { |
src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
| @@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired; | @@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired; | ||
| 4 | import org.springframework.context.ApplicationEventPublisher; | 4 | import org.springframework.context.ApplicationEventPublisher; |
| 5 | import org.springframework.stereotype.Component; | 5 | import org.springframework.stereotype.Component; |
| 6 | 6 | ||
| 7 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; | ||
| 8 | +import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent; | ||
| 7 | import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent; | 9 | import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent; |
| 8 | import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent; | 10 | import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent; |
| 9 | 11 | ||
| @@ -31,4 +33,14 @@ public class EventPublisher { | @@ -31,4 +33,14 @@ public class EventPublisher { | ||
| 31 | outEvent.setFrom(from); | 33 | outEvent.setFrom(from); |
| 32 | applicationEventPublisher.publishEvent(outEvent); | 34 | applicationEventPublisher.publishEvent(outEvent); |
| 33 | } | 35 | } |
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 设备报警事件 | ||
| 39 | + * @param deviceAlarm | ||
| 40 | + */ | ||
| 41 | + public void deviceAlarmEventPublish(DeviceAlarm deviceAlarm) { | ||
| 42 | + AlarmEvent alarmEvent = new AlarmEvent(this); | ||
| 43 | + alarmEvent.setAlarmInfo(deviceAlarm); | ||
| 44 | + applicationEventPublisher.publishEvent(alarmEvent); | ||
| 45 | + } | ||
| 34 | } | 46 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.gb28181.event.alarm; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; | ||
| 4 | +import org.springframework.context.ApplicationEvent; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @description: 报警事件 | ||
| 8 | + * @author: lawrencehj | ||
| 9 | + * @data: 2021-01-20 | ||
| 10 | + */ | ||
| 11 | + | ||
| 12 | +public class AlarmEvent extends ApplicationEvent { | ||
| 13 | + public AlarmEvent(Object source) { | ||
| 14 | + super(source); | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + private DeviceAlarm deviceAlarm; | ||
| 18 | + | ||
| 19 | + public DeviceAlarm getAlarmInfo() { | ||
| 20 | + return deviceAlarm; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + public void setAlarmInfo(DeviceAlarm deviceAlarm) { | ||
| 24 | + this.deviceAlarm = deviceAlarm; | ||
| 25 | + } | ||
| 26 | +} |
src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.gb28181.event.alarm; | ||
| 2 | + | ||
| 3 | +import org.springframework.context.ApplicationListener; | ||
| 4 | +import org.springframework.stereotype.Component; | ||
| 5 | +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
| 6 | +import java.io.IOException; | ||
| 7 | +import org.slf4j.Logger; | ||
| 8 | +import org.slf4j.LoggerFactory; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * @description: 报警事件监听 | ||
| 12 | + * @author: lawrencehj | ||
| 13 | + * @data: 2021-01-20 | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +@Component | ||
| 17 | +public class AlarmEventListener implements ApplicationListener<AlarmEvent> { | ||
| 18 | + | ||
| 19 | + private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class); | ||
| 20 | + | ||
| 21 | + private static SseEmitter emitter = new SseEmitter(); | ||
| 22 | + | ||
| 23 | + public void addSseEmitters(SseEmitter sseEmitter) { | ||
| 24 | + emitter = sseEmitter; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + @Override | ||
| 28 | + public void onApplicationEvent(AlarmEvent event) { | ||
| 29 | + if (logger.isDebugEnabled()) { | ||
| 30 | + logger.debug("设备报警事件触发,deviceId:" + event.getAlarmInfo().getDeviceId() + ", " | ||
| 31 | + + event.getAlarmInfo().getAlarmDescription()); | ||
| 32 | + } | ||
| 33 | + try { | ||
| 34 | + String msg = "<strong>设备编码:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>" | ||
| 35 | + + "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>" | ||
| 36 | + + "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>" | ||
| 37 | + + "<br><strong>定位经度:</strong> <i>" + event.getAlarmInfo().getLongitude() + "</i>" | ||
| 38 | + + "<br><strong>定位纬度:</strong> <i>" + event.getAlarmInfo().getLatitude() + "</i>"; | ||
| 39 | + emitter.send(msg); | ||
| 40 | + } catch (IOException e) { | ||
| 41 | + if (logger.isDebugEnabled()) { | ||
| 42 | + logger.debug("SSE 通道已关闭"); | ||
| 43 | + } | ||
| 44 | + // e.printStackTrace(); | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | +} |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
| @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; | @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; | ||
| 21 | 21 | ||
| 22 | import com.genersoft.iot.vmp.common.VideoManagerConstants; | 22 | import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| 23 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 23 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 24 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; | ||
| 24 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | 25 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 25 | import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; | 26 | import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; |
| 26 | import com.genersoft.iot.vmp.gb28181.bean.RecordItem; | 27 | import com.genersoft.iot.vmp.gb28181.bean.RecordItem; |
| @@ -289,19 +290,41 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | @@ -289,19 +290,41 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { | ||
| 289 | // storager.queryChannel(deviceId) | 290 | // storager.queryChannel(deviceId) |
| 290 | return; | 291 | return; |
| 291 | } | 292 | } |
| 292 | - device.setName(XmlUtil.getText(rootElement, "DeviceName")); | ||
| 293 | - device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer")); | ||
| 294 | - device.setModel(XmlUtil.getText(rootElement, "Model")); | ||
| 295 | - device.setFirmware(XmlUtil.getText(rootElement, "Firmware")); | ||
| 296 | - if (StringUtils.isEmpty(device.getStreamMode())) { | ||
| 297 | - device.setStreamMode("UDP"); | 293 | + |
| 294 | + DeviceAlarm deviceAlarm = new DeviceAlarm(); | ||
| 295 | + deviceAlarm.setDeviceId(deviceId); | ||
| 296 | + deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority")); | ||
| 297 | + deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod")); | ||
| 298 | + deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime")); | ||
| 299 | + if (XmlUtil.getText(rootElement, "AlarmDescription") == null) { | ||
| 300 | + deviceAlarm.setAlarmDescription(""); | ||
| 301 | + } else { | ||
| 302 | + deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription")); | ||
| 298 | } | 303 | } |
| 299 | - storager.updateDevice(device); | 304 | + if (XmlUtil.getText(rootElement, "Longitude") == null || XmlUtil.getText(rootElement, "Longitude") == "") { |
| 305 | + deviceAlarm.setLongitude(0.00); | ||
| 306 | + } else { | ||
| 307 | + deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); | ||
| 308 | + } | ||
| 309 | + if (XmlUtil.getText(rootElement, "Latitude") == null || XmlUtil.getText(rootElement, "Latitude") =="") { | ||
| 310 | + deviceAlarm.setLatitude(0.00); | ||
| 311 | + } else { | ||
| 312 | + deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); | ||
| 313 | + } | ||
| 314 | + | ||
| 315 | + // device.setName(XmlUtil.getText(rootElement, "DeviceName")); | ||
| 316 | + // device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer")); | ||
| 317 | + // device.setModel(XmlUtil.getText(rootElement, "Model")); | ||
| 318 | + // device.setFirmware(XmlUtil.getText(rootElement, "Firmware")); | ||
| 319 | + // if (StringUtils.isEmpty(device.getStreamMode())) { | ||
| 320 | + // device.setStreamMode("UDP"); | ||
| 321 | + // } | ||
| 322 | + // storager.updateDevice(device); | ||
| 300 | //cmder.catalogQuery(device, null); | 323 | //cmder.catalogQuery(device, null); |
| 301 | // 回复200 OK | 324 | // 回复200 OK |
| 302 | responseAck(evt); | 325 | responseAck(evt); |
| 303 | if (offLineDetector.isOnline(deviceId)) { | 326 | if (offLineDetector.isOnline(deviceId)) { |
| 304 | - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); | 327 | + publisher.deviceAlarmEventPublish(deviceAlarm); |
| 305 | } | 328 | } |
| 306 | } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { | 329 | } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { |
| 307 | // } catch (DocumentException e) { | 330 | // } catch (DocumentException e) { |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
| @@ -59,6 +59,7 @@ public interface DeviceChannelMapper { | @@ -59,6 +59,7 @@ public interface DeviceChannelMapper { | ||
| 59 | " WHERE 1=1 " + | 59 | " WHERE 1=1 " + |
| 60 | " <if test=\"hasSubChannel == true\" > AND subCount >0</if>" + | 60 | " <if test=\"hasSubChannel == true\" > AND subCount >0</if>" + |
| 61 | " <if test=\"hasSubChannel == false\" > AND subCount=0</if>" + | 61 | " <if test=\"hasSubChannel == false\" > AND subCount=0</if>" + |
| 62 | + " ORDER BY channelId ASC" + | ||
| 62 | " </script>"}) | 63 | " </script>"}) |
| 63 | List<DeviceChannel> queryChannelsByDeviceId(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); | 64 | List<DeviceChannel> queryChannelsByDeviceId(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); |
| 64 | 65 |
src/main/java/com/genersoft/iot/vmp/vmanager/SEEController/SEEController.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.SEEController; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; | ||
| 4 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 5 | +import org.springframework.stereotype.Controller; | ||
| 6 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 7 | +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * @description: SSE推送 | ||
| 11 | + * @author: lawrencehj | ||
| 12 | + * @data: 2021-01-20 | ||
| 13 | + */ | ||
| 14 | + | ||
| 15 | +@Controller | ||
| 16 | +@RequestMapping("/api") | ||
| 17 | +public class SEEController { | ||
| 18 | + @Autowired | ||
| 19 | + AlarmEventListener alarmEventListener; | ||
| 20 | + | ||
| 21 | + //设置响应 | ||
| 22 | + @RequestMapping("/emit") | ||
| 23 | + public SseEmitter emit() { | ||
| 24 | + SseEmitter sseEmitter = new SseEmitter(0L); | ||
| 25 | + try { | ||
| 26 | + alarmEventListener.addSseEmitters(sseEmitter); | ||
| 27 | + }catch (Exception e){ | ||
| 28 | + sseEmitter.completeWithError(e); | ||
| 29 | + } | ||
| 30 | + return sseEmitter; | ||
| 31 | + } | ||
| 32 | +} |
web_src/src/components/UiHeader.vue
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | <el-menu-item index="/">控制台</el-menu-item> | 4 | <el-menu-item index="/">控制台</el-menu-item> |
| 5 | <el-menu-item index="/videoList">设备列表</el-menu-item> | 5 | <el-menu-item index="/videoList">设备列表</el-menu-item> |
| 6 | <!-- <el-menu-item index="/videoReplay">录像回看</el-menu-item> --> | 6 | <!-- <el-menu-item index="/videoReplay">录像回看</el-menu-item> --> |
| 7 | - <!-- <el-menu-item index="4">级联设置</el-menu-item> --> | 7 | + <el-switch v-model="alarmNotify" active-text="报警信息推送" style="display: block float: right" @change="sseControl"></el-switch> |
| 8 | <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item> | 8 | <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item> |
| 9 | </el-menu> | 9 | </el-menu> |
| 10 | </div> | 10 | </div> |
| @@ -13,14 +13,64 @@ | @@ -13,14 +13,64 @@ | ||
| 13 | <script> | 13 | <script> |
| 14 | export default { | 14 | export default { |
| 15 | name: "UiHeader", | 15 | name: "UiHeader", |
| 16 | + components: { Notification }, | ||
| 17 | + data() { | ||
| 18 | + return { | ||
| 19 | + alarmNotify: true, | ||
| 20 | + sseSource: null, | ||
| 21 | + }; | ||
| 22 | + }, | ||
| 16 | methods:{ | 23 | methods:{ |
| 17 | 24 | ||
| 18 | loginout(){ | 25 | loginout(){ |
| 19 | // 删除cookie,回到登录页面 | 26 | // 删除cookie,回到登录页面 |
| 20 | this.$cookies.remove("session"); | 27 | this.$cookies.remove("session"); |
| 21 | this.$router.push('/login'); | 28 | this.$router.push('/login'); |
| 29 | + this.sseSource.close(); | ||
| 22 | }, | 30 | }, |
| 23 | - } | ||
| 24 | -} | 31 | + beforeunloadHandler() { |
| 32 | + this.sseSource.close(); | ||
| 33 | + }, | ||
| 34 | + sseControl() { | ||
| 35 | + let that = this; | ||
| 36 | + if (this.alarmNotify) { | ||
| 37 | + this.sseSource = new EventSource('/api/emit'); | ||
| 38 | + this.sseSource.addEventListener('message', function(evt) { | ||
| 39 | + that.$notify({ | ||
| 40 | + title: '收到报警信息', | ||
| 41 | + dangerouslyUseHTMLString: true, | ||
| 42 | + message: evt.data, | ||
| 43 | + type: 'warning' | ||
| 44 | + }); | ||
| 45 | + console.log("收到信息:" + evt.data); | ||
| 46 | + }); | ||
| 47 | + this.sseSource.addEventListener('open', function(e) { | ||
| 48 | + console.log("SSE连接打开."); | ||
| 49 | + }, false); | ||
| 50 | + this.sseSource.addEventListener('error', function(e) { | ||
| 51 | + if (e.target.readyState == EventSource.CLOSED) { | ||
| 52 | + console.log("SSE连接关闭"); | ||
| 53 | + } else { | ||
| 54 | + console.log(e.target.readyState); | ||
| 55 | + } | ||
| 56 | + }, false); | ||
| 57 | + } else { | ||
| 58 | + this.sseSource.removeEventListener('open', null); | ||
| 59 | + this.sseSource.removeEventListener('message', null); | ||
| 60 | + this.sseSource.removeEventListener('error', null); | ||
| 61 | + this.sseSource.close(); | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + }, | ||
| 65 | + mounted() { | ||
| 66 | + window.addEventListener('beforeunload', e => this.beforeunloadHandler(e)) | ||
| 67 | + // window.addEventListener('unload', e => this.unloadHandler(e)) | ||
| 68 | + this.sseControl(); | ||
| 69 | + }, | ||
| 70 | + destroyed() { | ||
| 71 | + window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e)) | ||
| 72 | + // window.removeEventListener('unload', e => this.unloadHandler(e)) | ||
| 73 | + }, | ||
| 74 | + } | ||
| 25 | 75 | ||
| 26 | </script> | 76 | </script> |
web_src/src/main.js
| @@ -9,10 +9,13 @@ import VueCookies from 'vue-cookies'; | @@ -9,10 +9,13 @@ import VueCookies from 'vue-cookies'; | ||
| 9 | import echarts from 'echarts'; | 9 | import echarts from 'echarts'; |
| 10 | 10 | ||
| 11 | import VueClipboard from 'vue-clipboard2' | 11 | import VueClipboard from 'vue-clipboard2' |
| 12 | +import { Notification } from 'element-ui'; | ||
| 13 | + | ||
| 12 | Vue.use(VueClipboard) | 14 | Vue.use(VueClipboard) |
| 13 | Vue.use(ElementUI); | 15 | Vue.use(ElementUI); |
| 14 | Vue.use(VueCookies); | 16 | Vue.use(VueCookies); |
| 15 | Vue.prototype.$axios = axios; | 17 | Vue.prototype.$axios = axios; |
| 18 | +Vue.prototype.$notify = Notification; | ||
| 16 | 19 | ||
| 17 | axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : ""; | 20 | axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : ""; |
| 18 | 21 |