Merge branch '648540858:wvp-28181-2.0' into wvp-28181-2.0

This commit is contained in:
mk1990
2022-04-26 15:32:23 +08:00
committed by GitHub
78 changed files with 2825 additions and 1979 deletions

View File

@@ -87,9 +87,12 @@ public class VideoManagerConstants {
// 移动位置订阅通知
public static final String VM_MSG_SUBSCRIBE_MOBILE_POSITION = "mobileposition";
// 报警订阅的通知
// 报警订阅的通知收到报警向redis发出通知
public static final String VM_MSG_SUBSCRIBE_ALARM = "alarm";
// 报警通知的发送 收到redis发出的通知转发给其他平台
public static final String VM_MSG_SUBSCRIBE_ALARM_RECEIVE= "alarm_receive";
// 设备状态订阅的通知
public static final String VM_MSG_SUBSCRIBE_DEVICE_STATUS = "device";

View File

@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.service.impl.RedisAlarmMsgListener;
import com.genersoft.iot.vmp.service.impl.RedisGPSMsgListener;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -48,6 +49,9 @@ public class RedisConfig extends CachingConfigurerSupport {
@Autowired
private RedisGPSMsgListener redisGPSMsgListener;
@Autowired
private RedisAlarmMsgListener redisAlarmMsgListener;
@Bean
public JedisPool jedisPool() {
if (StringUtils.isBlank(password)) {
@@ -93,6 +97,7 @@ public class RedisConfig extends CachingConfigurerSupport {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
return container;
}

View File

@@ -29,7 +29,7 @@ public class SipConfig {
Integer registerTimeInterval = 120;
private boolean alarm = false;
private boolean alarm;
public void setIp(String ip) {
this.ip = ip;

View File

@@ -0,0 +1,46 @@
package com.genersoft.iot.vmp.gb28181.bean;
/**
* 通过redis分发报警消息
*/
public class AlarmChannelMessage {
/**
* 国标编号
*/
private String gbId;
/**
* 报警编号
*/
private int alarmSn;
/**
* 报警描述
*/
private String alarmDescription;
public String getGbId() {
return gbId;
}
public void setGbId(String gbId) {
this.gbId = gbId;
}
public int getAlarmSn() {
return alarmSn;
}
public void setAlarmSn(int alarmSn) {
this.alarmSn = alarmSn;
}
public String getAlarmDescription() {
return alarmDescription;
}
public void setAlarmDescription(String alarmDescription) {
this.alarmDescription = alarmDescription;
}
}

View File

@@ -50,7 +50,30 @@ public class DeviceAlarm {
private double latitude;
/**
* 报警类型
* 报警类型,
* 报警方式为2时,不携带 AlarmType为默认的报警设备报警,
* 携带 AlarmType取值及对应报警类型如下:
* 1-视频丢失报警;
* 2-设备防拆报警;
* 3-存储设备磁盘满报警;
* 4-设备高温报警;
* 5-设备低温报警。
* 报警方式为5时,取值如下:
* 1-人工视频报警;
* 2-运动目标检测报警;
* 3-遗留物检测报警;
* 4-物体移除检测报警;
* 5-绊线检测报警;
* 6-入侵检测报警;
* 7-逆行检测报警;
* 8-徘徊检测报警;
* 9-流量统计报警;
* 10-密度检测报警;
* 11-视频异常检测报警;
* 12-快速移动报警。
* 报警方式为6时,取值下:
* 1-存储设备磁盘故障报警;
* 2-存储设备风扇故障报警。
*/
private String alarmType;

View File

@@ -4,7 +4,7 @@ public class DeviceChannel {
/**
* 数据库自ID
* 数据库自ID
*/
private int id;

View File

@@ -25,6 +25,9 @@ public class SubscribeInfo {
this.callId = callIdHeader.getCallId();
}
public SubscribeInfo() {
}
private String id;
private int expires;
private String callId;

View File

@@ -52,7 +52,6 @@ public class KeepaliveTimeoutListenerForPlatform extends RedisKeyExpirationEvent
public void onMessage(Message message, byte[] pattern) {
// 获取失效的key
String expiredKey = message.toString();
logger.debug(expiredKey);
// 平台心跳到期,需要重发, 判断是否已经多次未收到心跳回复, 多次未收到,则重新发起注册, 注册尝试多次未得到回复,则认为平台离线
String PLATFORM_KEEPLIVEKEY_PREFIX = VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetting.getServerId() + "_";
String PLATFORM_REGISTER_PREFIX = VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetting.getServerId() + "_";

View File

@@ -57,10 +57,8 @@ public class OfflineEventListener implements ApplicationListener<OfflineEvent> {
@Override
public void onApplicationEvent(OfflineEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("设备离线事件触发deviceId" + event.getDeviceId() + ",from:" + event.getFrom());
}
logger.info("设备离线事件触发deviceId" + event.getDeviceId() + ",from:" + event.getFrom());
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetting.getServerId() + "_" + event.getDeviceId();

View File

@@ -59,10 +59,8 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> {
@Override
public void onApplicationEvent(OnlineEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("设备上线事件触发deviceId" + event.getDevice().getDeviceId() + ",from:" + event.getFrom());
}
logger.info("设备上线事件触发deviceId" + event.getDevice().getDeviceId() + ",from:" + event.getFrom());
Device device = event.getDevice();
if (device == null) return;
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetting.getServerId() + "_" + event.getDevice().getDeviceId();

View File

@@ -31,10 +31,8 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
private Map<String, RecordEndEventHandler> handlerMap = new HashMap<>();
@Override
public void onApplicationEvent(RecordEndEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("录像查询完成事件触发deviceId{}, channelId: {}, 录像数量{}条", event.getRecordInfo().getDeviceId(),
event.getRecordInfo().getChannelId(), event.getRecordInfo().getSumNum() );
}
logger.info("录像查询完成事件触发deviceId{}, channelId: {}, 录像数量{}条", event.getRecordInfo().getDeviceId(),
event.getRecordInfo().getChannelId(), event.getRecordInfo().getSumNum() );
if (handlerMap.size() > 0) {
for (RecordEndEventHandler recordEndEventHandler : handlerMap.values()) {
recordEndEventHandler.handler(event.getRecordInfo());

View File

@@ -64,7 +64,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
if (subscribe == null) {
logger.debug("发送订阅消息时发现订阅信息已经不存在");
logger.info("发送订阅消息时发现订阅信息已经不存在");
return;
}
}else {

View File

@@ -1,8 +1,7 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
@@ -336,4 +335,13 @@ public interface ISIPCommander {
* @param cmdString 前端控制指令串
*/
boolean dragZoomCmd(Device device, String channelId, String cmdString);
/**
* 向设备发送报警NOTIFY消息 用于互联结构下,此时将设备当成一个平级平台看待
* @param device 设备
* @param deviceAlarm 报警信息信息
* @return
*/
boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm);
}

View File

@@ -1,9 +1,6 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
@@ -75,6 +72,14 @@ public interface ISIPCommanderForPlatform {
*/
boolean sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo);
/**
* 向上级回复报警消息
* @param parentPlatform 平台信息
* @param deviceAlarm 报警信息信息
* @return
*/
boolean sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm);
/**
* 回复catalog事件-增加/更新
* @param parentPlatform

View File

@@ -5,10 +5,7 @@ import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback;
import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
@@ -23,6 +20,7 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.message.MessageFactoryImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.stack.SIPDialog;
import org.slf4j.Logger;
@@ -35,10 +33,7 @@ import org.springframework.util.StringUtils;
import javax.sip.*;
import javax.sip.address.SipURI;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.ViaHeader;
import javax.sip.header.*;
import javax.sip.message.Request;
import java.lang.reflect.Field;
import java.text.ParseException;
@@ -1777,4 +1772,101 @@ public class SIPCommander implements ISIPCommander {
e.printStackTrace();
}
}
@Override
public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) {
if (device == null) {
return false;
}
logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
try {
String characterSet = device.getCharset();
StringBuffer deviceStatusXml = new StringBuffer(600);
deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
deviceStatusXml.append("<Notify>\r\n");
deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
deviceStatusXml.append("<info>\r\n");
deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
deviceStatusXml.append("</info>\r\n");
deviceStatusXml.append("</Notify>\r\n");
CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
: udpSipProvider.getNewCallId();
String tm = Long.toString(System.currentTimeMillis());
Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);
transmitRequest(device, request);
} catch (SipException | ParseException e) {
e.printStackTrace();
return false;
} catch (InvalidArgumentException e) {
throw new RuntimeException(e);
}
return true;
}
private void sendNotify(Device device, String catalogXmlContent,
SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent )
throws NoSuchFieldException, IllegalAccessException, SipException, ParseException {
MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
String characterSet = device.getCharset();
// 设置编码, 防止中文乱码
messageFactory.setDefaultContentEncodingCharset(characterSet);
Dialog dialog = subscribeInfo.getDialog();
if (dialog == null || !dialog.getState().equals(DialogState.CONFIRMED)) return;
SIPRequest notifyRequest = (SIPRequest)dialog.createRequest(Request.NOTIFY);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
notifyRequest.setContent(catalogXmlContent, contentTypeHeader);
SubscriptionStateHeader subscriptionState = sipFactory.createHeaderFactory()
.createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE);
notifyRequest.addHeader(subscriptionState);
EventHeader event = sipFactory.createHeaderFactory().createEventHeader(subscribeInfo.getEventType());
if (subscribeInfo.getEventId() != null) {
event.setEventId(subscribeInfo.getEventId());
}
notifyRequest.addHeader(event);
SipURI sipURI = (SipURI) notifyRequest.getRequestURI();
if (subscribeInfo.getTransaction() != null) {
SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();
sipURI.setHost(request.getRemoteAddress().getHostAddress());
sipURI.setPort(request.getRemotePort());
}else {
sipURI.setHost(device.getIp());
sipURI.setPort(device.getPort());
}
ClientTransaction transaction = null;
if ("TCP".equals(device.getTransport())) {
transaction = tcpSipProvider.getNewClientTransaction(notifyRequest);
} else if ("UDP".equals(device.getTransport())) {
transaction = udpSipProvider.getNewClientTransaction(notifyRequest);
}
// 添加错误订阅
if (errorEvent != null) {
sipSubscribe.addErrorSubscribe(subscribeInfo.getCallId(), errorEvent);
}
// 添加订阅
if (okEvent != null) {
sipSubscribe.addOkSubscribe(subscribeInfo.getCallId(), okEvent);
}
if (transaction == null) {
logger.error("平台{}的Transport错误{}",device.getDeviceId(), device.getTransport());
return;
}
dialog.sendRequest(transaction);
}
}

View File

@@ -435,6 +435,48 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
return true;
}
@Override
public boolean sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) {
if (parentPlatform == null) {
return false;
}
logger.info("[发送 报警订阅] {}/{}->{},{}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
try {
String characterSet = parentPlatform.getCharacterSet();
StringBuffer deviceStatusXml = new StringBuffer(600);
deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
deviceStatusXml.append("<Notify>\r\n");
deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
deviceStatusXml.append("<info>\r\n");
deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
deviceStatusXml.append("</info>\r\n");
deviceStatusXml.append("</Notify>\r\n");
CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
: udpSipProvider.getNewCallId();
String tm = Long.toString(System.currentTimeMillis());
Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), "FromPtz" + tm, callIdHeader);
transmitRequest(parentPlatform, request);
} catch (SipException | ParseException e) {
e.printStackTrace();
return false;
} catch (InvalidArgumentException e) {
throw new RuntimeException(e);
}
return true;
}
@Override
public boolean sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) {
if (parentPlatform == null || deviceChannels == null || deviceChannels.size() == 0 || subscribeInfo == null) {
@@ -495,11 +537,16 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
event.setEventId(subscribeInfo.getEventId());
}
notifyRequest.addHeader(event);
SipURI sipURI = (SipURI) notifyRequest.getRequestURI();
SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();
sipURI.setHost(request.getRemoteAddress().getHostAddress());
sipURI.setPort(request.getRemotePort());
if (subscribeInfo.getTransaction() != null) {
SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();
sipURI.setHost(request.getRemoteAddress().getHostAddress());
sipURI.setPort(request.getRemotePort());
}else {
sipURI.setHost(parentPlatform.getServerIP());
sipURI.setPort(parentPlatform.getServerPort());
}
ClientTransaction transaction = null;
if ("TCP".equals(parentPlatform.getTransport())) {
transaction = tcpSipProvider.getNewClientTransaction(notifyRequest);

View File

@@ -13,6 +13,7 @@ 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.event.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
@@ -149,11 +150,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
logger.info("[收到 移动位置订阅]{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(),
mobilePosition.getLongitude(), mobilePosition.getLatitude());
mobilePosition.setReportSource("Mobile Position");
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());
// 默认来源坐标系为WGS-84处理
Double[] gcj02Point = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
logger.info("GCJ02坐标" + gcj02Point[0] + ", " + gcj02Point[1]);
mobilePosition.setGeodeticSystem("GCJ-02");
mobilePosition.setCnLng(gcj02Point[0] + "");
mobilePosition.setCnLat(gcj02Point[1] + "");
if (!userSetting.getSavePositionHistory()) {
storager.clearMobilePositionsByDeviceId(deviceId);
}
@@ -223,12 +225,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
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());
// 默认来源坐标系为WGS-84处理
Double[] gcj02Point = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
logger.info("GCJ02坐标:" + gcj02Point[0] + ", " + gcj02Point[1]);
mobilePosition.setGeodeticSystem("GCJ-02");
mobilePosition.setCnLng(gcj02Point[0] + "");
mobilePosition.setCnLat(gcj02Point[1] + "");
if (!userSetting.getSavePositionHistory()) {
storager.clearMobilePositionsByDeviceId(deviceId);
}

View File

@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
import com.alibaba.fastjson.JSON;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -8,8 +9,10 @@ import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.service.IDeviceAlarmService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.GpsUtil;
import org.dom4j.Element;
@@ -20,7 +23,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import javax.sip.message.Response;
import java.text.ParseException;
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.*;
@@ -45,6 +53,9 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
@Autowired
private IVideoManagerStorage storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IDeviceAlarmService deviceAlarmService;
@@ -58,11 +69,22 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
@Override
public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
if (!sipConfig.isAlarm()) {
return;
logger.info("收到来自设备[{}]的报警通知", device.getDeviceId());
// 回复200 OK
try {
responseAck(evt, Response.OK);
} catch (SipException e) {
throw new RuntimeException(e);
} catch (InvalidArgumentException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
Element deviceIdElement = rootElement.element("DeviceID");
String channelId = deviceIdElement.getText().toString();
DeviceAlarm deviceAlarm = new DeviceAlarm();
deviceAlarm.setDeviceId(device.getDeviceId());
deviceAlarm.setChannelId(channelId);
@@ -93,12 +115,12 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
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());
// 默认来源坐标系为WGS-84处理
Double[] gcj02Point = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
logger.info("GCJ02坐标:" + gcj02Point[0] + ", " + gcj02Point[1]);
mobilePosition.setGeodeticSystem("GCJ-02");
mobilePosition.setCnLng(gcj02Point[0] + "");
mobilePosition.setCnLat(gcj02Point[1] + "");
if (!userSetting.getSavePositionHistory()) {
storager.clearMobilePositionsByDeviceId(device.getDeviceId());
}
@@ -110,9 +132,24 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
deviceAlarm.setAlarmType(getText(rootElement.element("Info"), "AlarmType"));
}
}
if (channelId.equals(sipConfig.getId())) {
// 发送给平台的报警信息。 发送redis通知
AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
alarmChannelMessage.setGbId(channelId);
redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
return;
}
logger.debug("存储报警信息、报警分类");
// 存储报警信息、报警分类
deviceAlarmService.add(deviceAlarm);
if (sipConfig.isAlarm()) {
deviceAlarmService.add(deviceAlarm);
}
if (offLineDetector.isOnline(device.getDeviceId())) {
publisher.deviceAlarmEventPublish(deviceAlarm);
@@ -120,7 +157,59 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
}
@Override
public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) {
public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
logger.info("收到来自平台[{}]的报警通知", parentPlatform.getServerGBId());
// 回复200 OK
try {
responseAck(evt, Response.OK);
} catch (SipException e) {
throw new RuntimeException(e);
} catch (InvalidArgumentException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
Element deviceIdElement = rootElement.element("DeviceID");
String channelId = deviceIdElement.getText().toString();
DeviceAlarm deviceAlarm = new DeviceAlarm();
deviceAlarm.setDeviceId(parentPlatform.getServerGBId());
deviceAlarm.setChannelId(channelId);
deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
if (getText(rootElement, "AlarmDescription") == null) {
deviceAlarm.setAlarmDescription("");
} else {
deviceAlarm.setAlarmDescription(getText(rootElement, "AlarmDescription"));
}
if (NumericUtil.isDouble(getText(rootElement, "Longitude"))) {
deviceAlarm.setLongitude(Double.parseDouble(getText(rootElement, "Longitude")));
} else {
deviceAlarm.setLongitude(0.00);
}
if (NumericUtil.isDouble(getText(rootElement, "Latitude"))) {
deviceAlarm.setLatitude(Double.parseDouble(getText(rootElement, "Latitude")));
} else {
deviceAlarm.setLatitude(0.00);
}
if (!StringUtils.isEmpty(deviceAlarm.getAlarmMethod())) {
if (deviceAlarm.getAlarmMethod().equals("5")) {
deviceAlarm.setAlarmType(getText(rootElement.element("Info"), "AlarmType"));
}
}
if (channelId.equals(parentPlatform.getDeviceGBId())) {
// 发送给平台的报警信息。 发送redis通知
AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
alarmChannelMessage.setGbId(channelId);
redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
return;
}
}
}

View File

@@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.GpsUtil;
@@ -79,12 +80,12 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
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());
// 默认来源坐标系为WGS-84处理
Double[] gcj02Point = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
logger.info("GCJ02坐标:" + gcj02Point[0] + ", " + gcj02Point[1]);
mobilePosition.setGeodeticSystem("GCJ-02");
mobilePosition.setCnLng(gcj02Point[0] + "");
mobilePosition.setCnLat(gcj02Point[1] + "");
if (!userSetting.getSavePositionHistory()) {
storager.clearMobilePositionsByDeviceId(device.getDeviceId());
}

View File

@@ -12,6 +12,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -199,12 +200,12 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
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());
// 默认来源坐标系为WGS-84处理
Double[] gcj02Point = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
logger.info("GCJ02坐标:" + gcj02Point[0] + ", " + gcj02Point[1]);
mobilePosition.setGeodeticSystem("GCJ-02");
mobilePosition.setCnLng(gcj02Point[0] + "");
mobilePosition.setCnLat(gcj02Point[1] + "");
if (!userSetting.getSavePositionHistory()) {
storager.clearMobilePositionsByDeviceId(deviceId);
}

View File

@@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.GpsUtil;
@@ -79,12 +80,12 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
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());
// 默认来源坐标系为WGS-84处理
Double[] gcj02Point = Coordtransform.WGS84ToGCJ02(mobilePosition.getLongitude(), mobilePosition.getLatitude());
logger.info("GCJ02坐标:" + gcj02Point[0] + ", " + gcj02Point[1]);
mobilePosition.setGeodeticSystem("GCJ-02");
mobilePosition.setCnLng(gcj02Point[0] + "");
mobilePosition.setCnLat(gcj02Point[1] + "");
if (!userSetting.getSavePositionHistory()) {
storager.clearMobilePositionsByDeviceId(device.getDeviceId());
}

View File

@@ -0,0 +1,126 @@
package com.genersoft.iot.vmp.gb28181.utils;
/**
* 坐标转换
* 一个提供了百度坐标BD09、国测局坐标火星坐标GCJ02、和WGS84坐标系之间的转换的工具类
* 参考https://github.com/wandergis/coordtransform 写的Java版本
* @author Xinconan
* @date 2016-03-18
* @url https://github.com/xinconan/coordtransform
*/
public class Coordtransform {
private static double x_PI = 3.14159265358979324 * 3000.0 / 180.0;
private static double PI = 3.1415926535897932384626;
private static double a = 6378245.0;
private static double ee = 0.00669342162296594323;
/**
* 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
* 即 百度 转 谷歌、高德
* @param bd_lon
* @param bd_lat
* @return Double[lon,lat]
*/
public static Double[] BD09ToGCJ02(Double bd_lon,Double bd_lat){
double x = bd_lon - 0.0065;
double y = bd_lat - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI);
Double[] arr = new Double[2];
arr[0] = z * Math.cos(theta);
arr[1] = z * Math.sin(theta);
return arr;
}
/**
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
* 即谷歌、高德 转 百度
* @param gcj_lon
* @param gcj_lat
* @return Double[lon,lat]
*/
public static Double[] GCJ02ToBD09(Double gcj_lon,Double gcj_lat){
double z = Math.sqrt(gcj_lon * gcj_lon + gcj_lat * gcj_lat) + 0.00002 * Math.sin(gcj_lat * x_PI);
double theta = Math.atan2(gcj_lat, gcj_lon) + 0.000003 * Math.cos(gcj_lon * x_PI);
Double[] arr = new Double[2];
arr[0] = z * Math.cos(theta) + 0.0065;
arr[1] = z * Math.sin(theta) + 0.006;
return arr;
}
/**
* WGS84转GCJ02
* @param wgs_lon
* @param wgs_lat
* @return Double[lon,lat]
*/
public static Double[] WGS84ToGCJ02(Double wgs_lon,Double wgs_lat){
if(outOfChina(wgs_lon, wgs_lat)){
return new Double[]{wgs_lon,wgs_lat};
}
double dlat = transformlat(wgs_lon - 105.0, wgs_lat - 35.0);
double dlng = transformlng(wgs_lon - 105.0, wgs_lat - 35.0);
double radlat = wgs_lat / 180.0 * PI;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
Double[] arr = new Double[2];
arr[0] = wgs_lon + dlng;
arr[1] = wgs_lat + dlat;
return arr;
}
/**
* GCJ02转WGS84
* @param gcj_lon
* @param gcj_lat
* @return Double[lon,lat]
*/
public static Double[] GCJ02ToWGS84(Double gcj_lon,Double gcj_lat){
if(outOfChina(gcj_lon, gcj_lat)){
return new Double[]{gcj_lon,gcj_lat};
}
double dlat = transformlat(gcj_lon - 105.0, gcj_lat - 35.0);
double dlng = transformlng(gcj_lon - 105.0, gcj_lat - 35.0);
double radlat = gcj_lat / 180.0 * PI;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
double mglat = gcj_lat + dlat;
double mglng = gcj_lon + dlng;
return new Double[]{gcj_lon * 2 - mglng, gcj_lat * 2 - mglat};
}
private static Double transformlat(double lng, double lat) {
double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
private static Double transformlng(double lng,double lat) {
double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
/**
* outOfChina
* @描述: 判断是否在国内,不在国内则不做偏移
* @param lng
* @param lat
* @return {boolean}
*/
private static boolean outOfChina(Double lng,Double lat) {
return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
};
}

View File

@@ -25,6 +25,7 @@ public class SipUtils {
* */
public static String getChannelIdFromHeader(Request request) {
Header subject = request.getHeader("subject");
if (subject == null) return null;
return ((Subject) subject).getSubject().split(":")[0];
}

View File

@@ -211,6 +211,14 @@ public class XmlUtil {
deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
}
deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
String parentId = XmlUtil.getText(itemDevice, "ParentID");
if (parentId != null && parentId.contains("/")) {
String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1);
deviceChannel.setParentId(lastParentId);
}else {
deviceChannel.setParentId(parentId);
}
if (XmlUtil.getText(itemDevice, "SafetyWay") == null
|| XmlUtil.getText(itemDevice, "SafetyWay") == "") {
deviceChannel.setSafetyWay(0);

View File

@@ -0,0 +1,69 @@
package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson.JSON;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
@Component
public class RedisAlarmMsgListener implements MessageListener {
private final static Logger logger = LoggerFactory.getLogger(RedisAlarmMsgListener.class);
@Autowired
private ISIPCommander commander;
@Autowired
private ISIPCommanderForPlatform commanderForPlatform;
@Autowired
private IVideoManagerStorage storage;
private final SimpleDateFormat formatForGB = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
@Override
public void onMessage(Message message, byte[] bytes) {
logger.info("收到来自REDIS的ALARM通知 {}", new String(message.getBody()));
AlarmChannelMessage alarmChannelMessage = JSON.parseObject(message.getBody(), AlarmChannelMessage.class);
if (alarmChannelMessage == null) {
logger.warn("[REDIS的ALARM通知]消息解析失败");
return;
}
String gbId = alarmChannelMessage.getGbId();
Device device = storage.queryVideoDevice(gbId);
ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId);
DeviceAlarm deviceAlarm = new DeviceAlarm();
deviceAlarm.setChannelId(gbId);
deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription());
deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
deviceAlarm.setAlarmPriority("1");
deviceAlarm.setAlarmTime(formatForGB.format(System.currentTimeMillis()));
deviceAlarm.setAlarmType("1");
deviceAlarm.setLongitude(0);
deviceAlarm.setLatitude(0);
if (device != null && platform == null) {
commander.sendAlarmMessage(device, deviceAlarm);
}else if (device == null && platform != null){
commanderForPlatform.sendAlarmMessage(platform, deviceAlarm);
}else {
logger.warn("无法确定" + gbId + "是平台还是设备");
}
}
}

View File

@@ -139,6 +139,12 @@ public interface IRedisCatchStorage {
*/
void sendStreamChangeMsg(String type, JSONObject jsonObject);
/**
* 发送报警消息
* @param msg 消息内容
*/
void sendAlarmMsg(AlarmChannelMessage msg);
/**
* 添加流信息到redis
* @param mediaServerItem

View File

@@ -5,7 +5,6 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import com.github.pagehelper.PageInfo;
@@ -94,12 +93,6 @@ public interface IVideoManagerStorage {
public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit);
/**
* 获取某个设备的通道树
* @param deviceId 设备ID
* @return
*/
List<DeviceChannelTree> tree(String deviceId);
/**
* 获取某个设备的通道列表
@@ -180,7 +173,7 @@ public interface IVideoManagerStorage {
* @param count
* @return
*/
PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count);
PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, Boolean online, int page, int count);
/**
@@ -285,7 +278,7 @@ public interface IVideoManagerStorage {
* @param startTime
* @param endTime
*/
public List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime);
public List<MobilePosition> queryMobilePositions(String deviceId, String channelId, String startTime, String endTime);
/**
* 查询最新移动位置

View File

@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
@@ -236,21 +235,6 @@ public interface DeviceChannelMapper {
@Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1")
List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId);
@Select(" SELECT\n" +
" id,\n" +
" channelId,\n" +
" deviceId,\n" +
" parentId,\n" +
" status,\n" +
" name as title,\n" +
" channelId as \"value\",\n" +
" channelId as \"key\",\n" +
" longitude,\n" +
" latitude\n" +
" from device_channel\n" +
" where deviceId = #{deviceId}")
List<DeviceChannelTree> tree(String deviceId);
@Delete(value = {" <script>" +
"DELETE " +
"from " +

View File

@@ -16,12 +16,12 @@ public interface DeviceMobilePositionMapper {
@Select(value = {" <script>" +
"SELECT * FROM device_mobile_position" +
" WHERE deviceId = #{deviceId} " +
" WHERE deviceId = #{deviceId} and channelId = #{channelId} " +
"<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);
List<MobilePosition> queryPositionByDeviceIdAndTime(String deviceId, String channelId, String startTime, String endTime);
@Select("SELECT * FROM device_mobile_position WHERE deviceId = #{deviceId}" +
" ORDER BY time DESC LIMIT 1")

View File

@@ -639,4 +639,10 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
}
@Override
public void sendAlarmMsg(AlarmChannelMessage msg) {
String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
logger.info("[redis 报警通知] {}: {}", key, JSON.toJSON(msg));
redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
}
}

View File

@@ -13,8 +13,6 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.*;
import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
import com.genersoft.iot.vmp.utils.node.ForestNodeMerger;
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
@@ -354,10 +352,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, null, query, hasSubChannel, online, start, limit);
}
@Override
public List<DeviceChannelTree> tree(String deviceId) {
return ForestNodeMerger.merge(deviceChannelMapper.tree(deviceId));
}
@Override
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) {
@@ -365,9 +359,9 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
}
@Override
public PageInfo<DeviceChannel> querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, String online, int page, int count) {
public PageInfo<DeviceChannel> querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, int page, int count) {
PageHelper.startPage(page, count);
List<DeviceChannel> all = deviceChannelMapper.queryChannels(deviceId, parentChannelId, null, null, null);
List<DeviceChannel> all = deviceChannelMapper.queryChannels(deviceId, parentChannelId, query, hasSubChannel, online);
return new PageInfo<>(all);
}
@@ -504,8 +498,8 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
* @param endTime
*/
@Override
public synchronized List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime) {
return deviceMobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, startTime, endTime);
public synchronized List<MobilePosition> queryMobilePositions(String deviceId, String channelId, String startTime, String endTime) {
return deviceMobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, channelId, startTime, endTime);
}
@Override

View File

@@ -1,74 +0,0 @@
package com.genersoft.iot.vmp.utils.node;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.ArrayList;
import java.util.List;
/**
* 节点基类
*
*/
public class BaseNode<T> implements INode<T> {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
protected String channelId;
/**
* 父节点ID
*/
protected String parentId;
/**
* 子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
protected List<T> children = new ArrayList<T>();
/**
* 是否有子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Boolean hasChildren;
/**
* 是否有子孙节点
*
* @return Boolean
*/
@Override
public Boolean getHasChildren() {
if (children.size() > 0) {
return true;
} else {
return this.hasChildren;
}
}
@Override
public String getChannelId() {
return channelId;
}
@Override
public String getParentId() {
return parentId;
}
@Override
public List<T> getChildren() {
return children;
}
public void setChildren(List<T> children) {
this.children = children;
}
public void setHasChildren(Boolean hasChildren) {
this.hasChildren = hasChildren;
}
}

View File

@@ -1,31 +0,0 @@
package com.genersoft.iot.vmp.utils.node;
/**
* 森林节点类
*
*/
public class ForestNode extends BaseNode<ForestNode> {
private static final long serialVersionUID = 1L;
/**
* 节点内容
*/
private Object content;
public ForestNode(String id, String parentId, Object content) {
this.channelId = id;
this.parentId = parentId;
this.content = content;
}
public Object getContent() {
return content;
}
public void setContent(Object content) {
this.content = content;
}
}

View File

@@ -1,68 +0,0 @@
package com.genersoft.iot.vmp.utils.node;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 森林管理类
*
* @author smallchill
*/
public class ForestNodeManager<T extends INode<T>> {
/**
* 森林的所有节点
*/
private final ImmutableMap<String, T> nodeMap;
/**
* 森林的父节点ID
*/
private final Map<String, Object> parentIdMap = Maps.newHashMap();
public ForestNodeManager(List<T> nodes) {
nodeMap = Maps.uniqueIndex(nodes, INode::getChannelId);
}
/**
* 根据节点ID获取一个节点
*
* @param id 节点ID
* @return 对应的节点对象
*/
public INode<T> getTreeNodeAt(String id) {
if (nodeMap.containsKey(id)) {
return nodeMap.get(id);
}
return null;
}
/**
* 增加父节点ID
*
* @param parentId 父节点ID
*/
public void addParentId(String parentId) {
parentIdMap.put(parentId, "");
}
/**
* 获取树的根节点(一个森林对应多颗树)
*
* @return 树的根节点集合
*/
public List<T> getRoot() {
List<T> roots = new ArrayList<>();
nodeMap.forEach((key, node) -> {
if (node.getParentId() == null || parentIdMap.containsKey(node.getChannelId())) {
roots.add(node);
}
});
return roots;
}
}

View File

@@ -1,51 +0,0 @@
package com.genersoft.iot.vmp.utils.node;
import com.genersoft.iot.vmp.utils.CollectionUtil;
import java.util.List;
/**
* 森林节点归并类
*
*/
public class ForestNodeMerger {
/**
* 将节点数组归并为一个森林多棵树填充节点的children域
* 时间复杂度为O(n^2)
*
* @param items 节点域
* @return 多棵树的根节点集合
*/
public static <T extends INode<T>> List<T> merge(List<T> items) {
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
items.forEach(forestNode -> {
if (forestNode.getParentId() != null) {
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
if (node != null) {
node.getChildren().add(forestNode);
} else {
forestNodeManager.addParentId(forestNode.getChannelId());
}
}
});
return forestNodeManager.getRoot();
}
public static <T extends INode<T>> List<T> merge(List<T> items, String[] parentIds) {
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
items.forEach(forestNode -> {
if (forestNode.getParentId() != null) {
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
if (CollectionUtil.contains(parentIds, forestNode.getChannelId())){
forestNodeManager.addParentId(forestNode.getChannelId());
} else {
if (node != null){
node.getChildren().add(forestNode);
}
}
}
});
return forestNodeManager.getRoot();
}
}

View File

@@ -1,42 +0,0 @@
package com.genersoft.iot.vmp.utils.node;
import java.io.Serializable;
import java.util.List;
/**
*
* 节点
*/
public interface INode<T> extends Serializable {
/**
* 主键
*
* @return String
*/
String getChannelId();
/**
* 父主键
*
* @return String
*/
String getParentId();
/**
* 子孙节点
*
* @return List<T>
*/
List<T> getChildren();
/**
* 是否有子孙节点
*
* @return Boolean
*/
default Boolean getHasChildren() {
return false;
}
}

View File

@@ -1,42 +0,0 @@
package com.genersoft.iot.vmp.utils.node;
/**
* 树型节点类
*
*/
public class TreeNode extends BaseNode<TreeNode> {
private static final long serialVersionUID = 1L;
private String title;
private String key;
private String value;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -1,121 +0,0 @@
package com.genersoft.iot.vmp.vmanager.bean;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.utils.node.INode;
import io.swagger.annotations.ApiModel;
import java.util.ArrayList;
import java.util.List;
@ApiModel(value = "DeviceChannelTree对象", description = "DeviceChannelTree对象")
public class DeviceChannelTree extends DeviceChannel implements INode<DeviceChannelTree> {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private int id;
/**
* 父节点ID
*/
private String parentId;
private String parentName;
private String title;
private String key;
private String value;
/**
* 子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<DeviceChannelTree> children;
/**
* 是否有子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Boolean hasChildren;
@Override
public List<DeviceChannelTree> getChildren() {
if (this.children == null) {
this.children = new ArrayList<>();
}
return this.children;
}
@Override
public Boolean getHasChildren() {
if (children.size() > 0) {
return true;
} else {
return this.hasChildren;
}
}
@Override
public int getId() {
return id;
}
@Override
public void setId(int id) {
this.id = id;
}
@Override
public String getParentId() {
return parentId;
}
@Override
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public void setChildren(List<DeviceChannelTree> children) {
this.children = children;
}
public void setHasChildren(Boolean hasChildren) {
this.hasChildren = hasChildren;
}
}

View File

@@ -1,56 +0,0 @@
package com.genersoft.iot.vmp.vmanager.bean;
import com.genersoft.iot.vmp.utils.node.TreeNode;
public class DeviceChannelTreeNode extends TreeNode {
private Integer status;
private String deviceId;
private String channelId;
private Double lng;
private Double lat;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public Double getLng() {
return lng;
}
public void setLng(Double lng) {
this.lng = lng;
}
public Double getLat() {
return lat;
}
public void setLat(Double lat) {
this.lat = lat;
}
}

View File

@@ -10,6 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.util.StringUtil;
import io.swagger.annotations.Api;
@@ -65,10 +66,11 @@ public class MobilePositionController {
@ApiImplicitParam(name = "start", value = "开始时间", required = true, dataTypeClass = String.class),
@ApiImplicitParam(name = "end", value = "结束时间", required = true, dataTypeClass = String.class),
})
@GetMapping("/history/{deviceId}")
public ResponseEntity<List<MobilePosition>> positions(@PathVariable String deviceId,
@RequestParam(required = false) String start,
@RequestParam(required = false) String end) {
@GetMapping("/history/{deviceId}/{channelId}")
public ResponseEntity<WVPResult<List<MobilePosition>>> positions(@PathVariable String deviceId,
@PathVariable String channelId,
@RequestParam(required = false) String start,
@RequestParam(required = false) String end) {
// if (logger.isDebugEnabled()) {
// logger.debug("查询设备" + deviceId + "的历史轨迹");
// }
@@ -79,9 +81,11 @@ public class MobilePositionController {
if (StringUtil.isEmpty(end)) {
end = null;
}
List<MobilePosition> result = storager.queryMobilePositions(deviceId, start, end);
return new ResponseEntity<>(result, HttpStatus.OK);
WVPResult<List<MobilePosition>> wvpResult = new WVPResult<>();
wvpResult.setCode(0);
List<MobilePosition> result = storager.queryMobilePositions(deviceId, channelId, start, end);
wvpResult.setData(result);
return new ResponseEntity<>(wvpResult, HttpStatus.OK);
}
/**

View File

@@ -1,8 +1,14 @@
package com.genersoft.iot.vmp.vmanager.gb28181.alarm;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.service.IDeviceAlarmService;
import com.genersoft.iot.vmp.service.IGbStreamService;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.Api;
@@ -31,7 +37,17 @@ public class AlarmController {
@Autowired
private IDeviceAlarmService deviceAlarmService;
@Autowired
private ISIPCommander commander;
@Autowired
private ISIPCommanderForPlatform commanderForPlatform;
@Autowired
private IVideoManagerStorage storage;
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private SimpleDateFormat formatForGB = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
/**
* 分页查询报警
@@ -133,5 +149,51 @@ public class AlarmController {
return new ResponseEntity<WVPResult<String>>(wvpResult, HttpStatus.OK);
}
/**
* 测试向上级/设备发送模拟报警通知
*
* @param deviceId 报警id
* @return
*/
@ApiOperation("测试向上级/设备发送模拟报警通知")
@GetMapping("/test/notify/alarm")
@ApiImplicitParams({
@ApiImplicitParam(name="deviceId", value = "deviceId", required = true ,dataTypeClass = Integer.class)
})
public ResponseEntity<WVPResult<String>> delete(
@RequestParam(required = false) String deviceId
) {
if (StringUtils.isEmpty(deviceId)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Device device = storage.queryVideoDevice(deviceId);
ParentPlatform platform = storage.queryParentPlatByServerGBId(deviceId);
DeviceAlarm deviceAlarm = new DeviceAlarm();
deviceAlarm.setChannelId(deviceId);
deviceAlarm.setAlarmDescription("test");
deviceAlarm.setAlarmMethod("1");
deviceAlarm.setAlarmPriority("1");
deviceAlarm.setAlarmTime(formatForGB.format(System.currentTimeMillis()));
deviceAlarm.setAlarmType("1");
deviceAlarm.setLongitude(115.33333);
deviceAlarm.setLatitude(39.33333);
if (device != null && platform == null) {
commander.sendAlarmMessage(device, deviceAlarm);
}else if (device == null && platform != null){
commanderForPlatform.sendAlarmMessage(platform, deviceAlarm);
}else {
WVPResult wvpResult = new WVPResult();
wvpResult.setCode(0);
wvpResult.setMsg("无法确定" + deviceId + "是平台还是设备");
return new ResponseEntity<WVPResult<String>>(wvpResult, HttpStatus.OK);
}
WVPResult wvpResult = new WVPResult();
wvpResult.setCode(0);
wvpResult.setMsg("success");
return new ResponseEntity<WVPResult<String>>(wvpResult, HttpStatus.OK);
}
}

View File

@@ -9,7 +9,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask;
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
@@ -17,14 +16,12 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.kxml2.wap.wv.WV;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -235,7 +232,7 @@ public class DeviceQuery {
@ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class),
@ApiImplicitParam(name="count", value = "每页条数", required = true, dataTypeClass = Integer.class),
@ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class),
@ApiImplicitParam(name="online", value = "是否在线", dataTypeClass = String.class),
@ApiImplicitParam(name="online", value = "是否在线", dataTypeClass = Boolean.class),
@ApiImplicitParam(name="channelType", value = "通道类型, 子目录", dataTypeClass = Boolean.class),
})
@GetMapping("/sub_channels/{deviceId}/{channelId}/channels")
@@ -244,7 +241,7 @@ public class DeviceQuery {
int page,
int count,
@RequestParam(required = false) String query,
@RequestParam(required = false) String online,
@RequestParam(required = false) Boolean online,
@RequestParam(required = false) Boolean channelType){
// if (logger.isDebugEnabled()) {
@@ -450,11 +447,6 @@ public class DeviceQuery {
return result;
}
@GetMapping("/{deviceId}/tree")
@ApiOperation(value = "通道树形结构", notes = "通道树形结构")
public WVPResult<List<DeviceChannelTree>> tree(@PathVariable String deviceId) {
return WVPResult.Data(storager.tree(deviceId));
}
@GetMapping("/{deviceId}/sync_status")
@ApiOperation(value = "获取通道同步进度", notes = "获取通道同步进度")

View File

@@ -63,7 +63,7 @@ public class GBRecordController {
public DeferredResult<ResponseEntity<RecordInfo>> recordinfo(@PathVariable String deviceId,@PathVariable String channelId, String startTime, String endTime){
if (logger.isDebugEnabled()) {
logger.debug(String.format("录像信息查询 API调用deviceId%s startTime%s startTime%s",deviceId, startTime, endTime));
logger.debug(String.format("录像信息查询 API调用deviceId%s startTime%s endTime%s",deviceId, startTime, endTime));
}
Device device = storager.queryVideoDevice(deviceId);