Merge branch 'main' into main2

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
This commit is contained in:
648540858
2023-02-10 16:25:33 +08:00
95 changed files with 2184 additions and 1050 deletions

View File

@@ -1,20 +1,24 @@
package com.genersoft.iot.vmp;
import java.util.logging.LogManager;
import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
import com.genersoft.iot.vmp.storager.impl.RedisCatchStorageImpl;
import com.genersoft.iot.vmp.utils.GitUtil;
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import java.util.Collections;
/**
* 启动类
*/
@@ -22,7 +26,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@EnableDruidSupport
public class VManageBootstrap extends LogManager {
public class VManageBootstrap extends SpringBootServletInitializer {
private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
@@ -41,6 +45,21 @@ public class VManageBootstrap extends LogManager {
context.close();
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(VManageBootstrap.class);
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.setSessionTrackingModes(
Collections.singleton(SessionTrackingMode.COOKIE)
);
SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
sessionCookieConfig.setHttpOnly(true);
}
}

View File

@@ -0,0 +1,77 @@
package com.genersoft.iot.vmp.common.enums;
import org.dom4j.Element;
import org.springframework.util.ObjectUtils;
/**
* @author gaofuwang
* @date 2023/01/18/ 10:09:00
* @since 1.0
*/
public enum DeviceControlType {
/**
* 云台控制
* 上下左右,预置位,扫描,辅助功能,巡航
*/
PTZ("PTZCmd","云台控制"),
/**
* 远程启动
*/
TELE_BOOT("TeleBoot","远程启动"),
/**
* 录像控制
*/
RECORD("RecordCmd","录像控制"),
/**
* 布防撤防
*/
GUARD("GuardCmd","布防撤防"),
/**
* 告警控制
*/
ALARM("AlarmCmd","告警控制"),
/**
* 强制关键帧
*/
I_FRAME("IFameCmd","强制关键帧"),
/**
* 拉框放大
*/
DRAG_ZOOM_IN("DragZoomIn","拉框放大"),
/**
* 拉框缩小
*/
DRAG_ZOOM_OUT("DragZoomOut","拉框缩小"),
/**
* 看守位
*/
HOME_POSITION("HomePosition","看守位");
private final String val;
private final String desc;
DeviceControlType(String val, String desc) {
this.val = val;
this.desc = desc;
}
public String getVal() {
return val;
}
public String getDesc() {
return desc;
}
public static DeviceControlType typeOf(Element rootElement) {
for (DeviceControlType item : DeviceControlType.values()) {
if (!ObjectUtils.isEmpty(rootElement.element(item.val)) || !ObjectUtils.isEmpty(rootElement.elements(item.val))) {
return item;
}
}
return null;
}
}

View File

@@ -10,6 +10,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -23,6 +24,7 @@ import java.io.IOException;
* @author lin
*/
@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true)
@Component
public class ApiAccessFilter extends OncePerRequestFilter {
private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class);
@@ -48,7 +50,7 @@ public class ApiAccessFilter extends OncePerRequestFilter {
filterChain.doFilter(servletRequest, servletResponse);
if (uriName != null && userSetting.getLogInDatebase()) {
if (uriName != null && userSetting != null && userSetting.getLogInDatebase() != null && userSetting.getLogInDatebase()) {
LogDto logDto = new LogDto();
logDto.setName(uriName);

View File

@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
@@ -15,11 +14,9 @@ import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.ConnectException;
@@ -77,9 +74,7 @@ public class ProxyServletConfig {
} catch (IOException ioException) {
if (ioException instanceof ConnectException) {
logger.error("zlm 连接失败");
} else if (ioException instanceof ClientAbortException) {
logger.error("zlm: 用户已中断连接,代理终止");
} else {
} else {
logger.error("zlm 代理失败: ", e);
}
} catch (RuntimeException exception){
@@ -195,9 +190,7 @@ public class ProxyServletConfig {
} catch (IOException ioException) {
if (ioException instanceof ConnectException) {
logger.error("录像服务 连接失败");
} else if (ioException instanceof ClientAbortException) {
logger.error("录像服务:用户已中断连接,代理终止");
} else {
}else {
logger.error("录像服务 代理失败: ", e);
}
} catch (RuntimeException exception){

View File

@@ -0,0 +1,30 @@
package com.genersoft.iot.vmp.conf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class ServiceInfo implements ApplicationListener<WebServerInitializedEvent> {
private final Logger logger = LoggerFactory.getLogger(ServiceInfo.class);
private static int serverPort;
public static int getServerPort() {
return serverPort;
}
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
// 项目启动获取启动的端口号
ServiceInfo.serverPort = event.getWebServer().getPort();
logger.info("项目启动获取启动的端口号: " + ServiceInfo.serverPort);
}
public void setServerPort(int serverPort) {
ServiceInfo.serverPort = serverPort;
}
}

View File

@@ -1,24 +0,0 @@
package com.genersoft.iot.vmp.conf.security;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import java.util.Collections;
public class UrlTokenHandler extends SpringBootServletInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.setSessionTrackingModes(
Collections.singleton(SessionTrackingMode.COOKIE)
);
SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
sessionCookieConfig.setHttpOnly(true);
}
}

View File

@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.bean;
/**
* 通过redis分发报警消息
*/
@@ -8,12 +9,14 @@ public class AlarmChannelMessage {
* 国标编号
*/
private String gbId;
/**
* 报警编号
*/
private int alarmSn;
/**
* 告警类型
*/
private int alarmType;
/**
* 报警描述
@@ -36,6 +39,14 @@ public class AlarmChannelMessage {
this.alarmSn = alarmSn;
}
public int getAlarmType() {
return alarmType;
}
public void setAlarmType(int alarmType) {
this.alarmType = alarmType;
}
public String getAlarmDescription() {
return alarmDescription;
}

View File

@@ -37,4 +37,18 @@ public enum DeviceAlarmMethod {
public int getVal() {
return val;
}
/**
* 查询是否匹配类型
* @param code
* @return
*/
public static DeviceAlarmMethod typeOf(int code) {
for (DeviceAlarmMethod item : DeviceAlarmMethod.values()) {
if (code==item.getVal()) {
return item;
}
}
return null;
}
}

View File

@@ -0,0 +1,143 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.gb28181.utils.MessageElement;
/**
* 设备信息查询响应
*
* @author Y.G
* @version 1.0
* @date 2022/6/28 14:55
*/
public class DragZoomRequest {
/**
* 序列号
*/
@MessageElement("SN")
private String sn;
@MessageElement("DeviceID")
private String deviceId;
@MessageElement(value = "DragZoomIn")
private DragZoom dragZoomIn;
@MessageElement(value = "DragZoomOut")
private DragZoom dragZoomOut;
/**
* 基本参数
*/
public static class DragZoom {
/**
* 播放窗口长度像素值
*/
@MessageElement("Length")
protected Integer length;
/**
* 播放窗口宽度像素值
*/
@MessageElement("Width")
protected Integer width;
/**
* 拉框中心的横轴坐标像素值
*/
@MessageElement("MidPointX")
protected Integer midPointX;
/**
* 拉框中心的纵轴坐标像素值
*/
@MessageElement("MidPointY")
protected Integer midPointY;
/**
* 拉框长度像素值
*/
@MessageElement("LengthX")
protected Integer lengthX;
/**
* 拉框宽度像素值
*/
@MessageElement("LengthY")
protected Integer lengthY;
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
this.length = length;
}
public Integer getWidth() {
return width;
}
public void setWidth(Integer width) {
this.width = width;
}
public Integer getMidPointX() {
return midPointX;
}
public void setMidPointX(Integer midPointX) {
this.midPointX = midPointX;
}
public Integer getMidPointY() {
return midPointY;
}
public void setMidPointY(Integer midPointY) {
this.midPointY = midPointY;
}
public Integer getLengthX() {
return lengthX;
}
public void setLengthX(Integer lengthX) {
this.lengthX = lengthX;
}
public Integer getLengthY() {
return lengthY;
}
public void setLengthY(Integer lengthY) {
this.lengthY = lengthY;
}
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public DragZoom getDragZoomIn() {
return dragZoomIn;
}
public void setDragZoomIn(DragZoom dragZoomIn) {
this.dragZoomIn = dragZoomIn;
}
public DragZoom getDragZoomOut() {
return dragZoomOut;
}
public void setDragZoomOut(DragZoom dragZoomOut) {
this.dragZoomOut = dragZoomOut;
}
}

View File

@@ -0,0 +1,94 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.gb28181.utils.MessageElement;
/**
* 设备信息查询响应
*
* @author Y.G
* @version 1.0
* @date 2022/6/28 14:55
*/
public class HomePositionRequest {
/**
* 序列号
*/
@MessageElement("SN")
private String sn;
@MessageElement("DeviceID")
private String deviceId;
@MessageElement(value = "HomePosition")
private HomePosition homePosition;
/**
* 基本参数
*/
public static class HomePosition {
/**
* 播放窗口长度像素值
*/
@MessageElement("Enabled")
protected String enabled;
/**
* 播放窗口宽度像素值
*/
@MessageElement("ResetTime")
protected String resetTime;
/**
* 拉框中心的横轴坐标像素值
*/
@MessageElement("PresetIndex")
protected String presetIndex;
public String getEnabled() {
return enabled;
}
public void setEnabled(String enabled) {
this.enabled = enabled;
}
public String getResetTime() {
return resetTime;
}
public void setResetTime(String resetTime) {
this.resetTime = resetTime;
}
public String getPresetIndex() {
return presetIndex;
}
public void setPresetIndex(String presetIndex) {
this.presetIndex = presetIndex;
}
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public HomePosition getHomePosition() {
return homePosition;
}
public void setHomePosition(HomePosition homePosition) {
this.homePosition = homePosition;
}
}

View File

@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.bean;
import java.time.Instant;
import java.util.List;
@@ -20,6 +21,8 @@ public class RecordInfo {
private int sumNum;
private int count;
private Instant lastTime;
private List<RecordItem> recordList;
@@ -79,4 +82,12 @@ public class RecordInfo {
public void setLastTime(Instant lastTime) {
this.lastTime = lastTime;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}

View File

@@ -1,8 +1,10 @@
package com.genersoft.iot.vmp.gb28181.event.record;
import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@@ -20,25 +22,46 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class);
private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();
public interface RecordEndEventHandler{
void handler(RecordInfo recordInfo);
}
private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();
@Override
public void onApplicationEvent(RecordEndEvent event) {
logger.info("录像查询完成事件触发deviceId{}, channelId: {}, 录像数量{}条", event.getRecordInfo().getDeviceId(),
event.getRecordInfo().getChannelId(), event.getRecordInfo().getSumNum() );
String deviceId = event.getRecordInfo().getDeviceId();
String channelId = event.getRecordInfo().getChannelId();
int count = event.getRecordInfo().getCount();
int sumNum = event.getRecordInfo().getSumNum();
logger.info("录像查询完成事件触发deviceId{}, channelId: {}, 录像数量{}/{}条", event.getRecordInfo().getDeviceId(),
event.getRecordInfo().getChannelId(), count,sumNum);
if (handlerMap.size() > 0) {
for (RecordEndEventHandler recordEndEventHandler : handlerMap.values()) {
recordEndEventHandler.handler(event.getRecordInfo());
RecordEndEventHandler handler = handlerMap.get(deviceId + channelId);
if (handler !=null){
handler.handler(event.getRecordInfo());
if (count ==sumNum){
handlerMap.remove(deviceId + channelId);
}
}
}
handlerMap.clear();
}
/**
* 添加
* @param device
* @param channelId
* @param recordEndEventHandler
*/
public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) {
handlerMap.put(device + channelId, recordEndEventHandler);
}
/**
* 添加
* @param device
* @param channelId
*/
public void delEndEventHandler(String device, String channelId) {
handlerMap.remove(device + channelId);
}
}

View File

@@ -109,12 +109,18 @@ public class CatalogDataCatch {
for (String deviceId : keys) {
CatalogData catalogData = data.get(deviceId);
if ( catalogData.getLastTime().isBefore(instantBefore5S)) { // 超过五秒收不到消息任务超时, 只更新这一部分数据
if ( catalogData.getLastTime().isBefore(instantBefore5S)) {
// 超过五秒收不到消息任务超时, 只更新这一部分数据, 收到数据与声明的总数一致,则重置通道数据,数据不全则只对收到的数据做更新操作
if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) {
storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
if (catalogData.getTotal() == catalogData.getChannelList().size()) {
storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
}else {
storager.updateChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
}
String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "";
catalogData.setErrorMsg(errorMsg);
if (catalogData.getTotal() != catalogData.getChannelList().size()) {
String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "";
catalogData.setErrorMsg(errorMsg);
}
}else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) {
String errorMsg = "同步失败,等待回复超时";

View File

@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.gb28181.session;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEventListener;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
@@ -23,14 +24,17 @@ public class RecordDataCatch {
@Autowired
private DeferredResultHolder deferredResultHolder;
@Autowired
private RecordEndEventListener recordEndEventListener;
public int put(String deviceId, String sn, int sumNum, List<RecordItem> recordItems) {
public int put(String deviceId,String channelId, String sn, int sumNum, List<RecordItem> recordItems) {
String key = deviceId + sn;
RecordInfo recordInfo = data.get(key);
if (recordInfo == null) {
recordInfo = new RecordInfo();
recordInfo.setDeviceId(deviceId);
recordInfo.setChannelId(channelId);
recordInfo.setSn(sn.trim());
recordInfo.setSumNum(sumNum);
recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>()));
@@ -67,6 +71,7 @@ public class RecordDataCatch {
msg.setKey(msgKey);
msg.setData(recordInfo);
deferredResultHolder.invokeAllResult(msg);
recordEndEventListener.delEndEventHandler(recordInfo.getDeviceId(),recordInfo.getChannelId());
data.remove(key);
}
}

View File

@@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
import com.genersoft.iot.vmp.utils.JsonUtil;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import gov.nist.javax.sip.message.SIPResponse;
import org.springframework.beans.factory.annotation.Autowired;
@@ -134,7 +135,7 @@ public class VideoStreamSessionManager {
List<SsrcTransaction> result= new ArrayList<>();
for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
String key = (String)ssrcTransactionKeys.get(i);
SsrcTransaction ssrcTransaction = (SsrcTransaction)RedisUtil.get(key);
SsrcTransaction ssrcTransaction = JsonUtil.redisJsonToObject(key, SsrcTransaction.class);
result.add(ssrcTransaction);
}
return result;

View File

@@ -47,61 +47,65 @@ public class SIPSender {
}
public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException {
ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
String transport = "UDP";
if (viaHeader == null) {
logger.warn("[消息头缺失] ViaHeader 使用默认的UDP方式处理数据");
}else {
transport = viaHeader.getTransport();
}
if (message.getHeader(UserAgentHeader.NAME) == null) {
try {
message.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
} catch (ParseException e) {
logger.error("添加UserAgentHeader失败", e);
try {
ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
String transport = "UDP";
if (viaHeader == null) {
logger.warn("[消息头缺失] ViaHeader 使用默认的UDP方式处理数据");
}else {
transport = viaHeader.getTransport();
}
}
CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);
// 添加错误订阅
if (errorEvent != null) {
sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
errorEvent.response(eventResult);
sipSubscribe.removeErrorSubscribe(eventResult.callId);
sipSubscribe.removeOkSubscribe(eventResult.callId);
}));
}
// 添加订阅
if (okEvent != null) {
sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
okEvent.response(eventResult);
sipSubscribe.removeOkSubscribe(eventResult.callId);
sipSubscribe.removeErrorSubscribe(eventResult.callId);
});
}
if ("TCP".equals(transport)) {
SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip);
if (tcpSipProvider == null) {
logger.error("[发送信息失败] 未找到tcp://{}的监听信息", ip);
return;
}
if (message instanceof Request) {
tcpSipProvider.sendRequest((Request)message);
}else if (message instanceof Response) {
tcpSipProvider.sendResponse((Response)message);
if (message.getHeader(UserAgentHeader.NAME) == null) {
try {
message.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
} catch (ParseException e) {
logger.error("添加UserAgentHeader失败", e);
}
}
} else if ("UDP".equals(transport)) {
SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip);
if (sipProvider == null) {
logger.error("[发送信息失败] 未找到udp://{}的监听信息", ip);
return;
CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);
// 添加错误订阅
if (errorEvent != null) {
sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
errorEvent.response(eventResult);
sipSubscribe.removeErrorSubscribe(eventResult.callId);
sipSubscribe.removeOkSubscribe(eventResult.callId);
}));
}
if (message instanceof Request) {
sipProvider.sendRequest((Request)message);
}else if (message instanceof Response) {
sipProvider.sendResponse((Response)message);
// 添加订阅
if (okEvent != null) {
sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
okEvent.response(eventResult);
sipSubscribe.removeOkSubscribe(eventResult.callId);
sipSubscribe.removeErrorSubscribe(eventResult.callId);
});
}
if ("TCP".equals(transport)) {
SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip);
if (tcpSipProvider == null) {
logger.error("[发送信息失败] 未找到tcp://{}的监听信息", ip);
return;
}
if (message instanceof Request) {
tcpSipProvider.sendRequest((Request)message);
}else if (message instanceof Response) {
tcpSipProvider.sendResponse((Response)message);
}
} else if ("UDP".equals(transport)) {
SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip);
if (sipProvider == null) {
logger.error("[发送信息失败] 未找到udp://{}的监听信息", ip);
return;
}
if (message instanceof Request) {
sipProvider.sendRequest((Request)message);
}else if (message instanceof Response) {
sipProvider.sendResponse((Response)message);
}
}
} finally {
logger.info("[SEND]:SUCCESS:{}", message);
}
}

View File

@@ -182,7 +182,7 @@ public interface ISIPCommander {
* @param channelId 预览通道
* @param recordCmdStr 录像命令Record / StopRecord
*/
void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
/**
* 远程启动控制命令
@@ -196,7 +196,7 @@ public interface ISIPCommander {
*
* @param device 视频设备
*/
void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
/**
* 报警复位命令
@@ -205,7 +205,7 @@ public interface ISIPCommander {
* @param alarmMethod 报警方式(可选)
* @param alarmType 报警类型(可选)
*/
void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
/**
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
@@ -214,17 +214,19 @@ public interface ISIPCommander {
* @param channelId 预览通道
*/
void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException;
/**
* 看守位控制命令
*
* @param device 视频设备
* @param enabled 看守位使能1 = 开启0 = 关闭
* @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
* @param presetIndex 调用预置位编号开启看守位时使用取值范围0~255
*
* @param device 视频设备
* @param channelId 通道id非通道则是设备本身
* @param frontCmd 上级平台的指令,如果存在则直接下发
* @param enabled 看守位使能1 = 开启0 = 关闭
* @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
* @param presetIndex 调用预置位编号开启看守位时使用取值范围0~255
*/
void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
/**
* 设备配置命令
*

View File

@@ -69,12 +69,11 @@ public interface ISIPCommanderForPlatform {
* 向上级回复DeviceInfo查询信息
*
* @param parentPlatform 平台信息
* @param sn
* @param fromTag
* @param sn SN
* @param fromTag FROM头的tag信息
* @return
*/
void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag)
throws SipException, InvalidArgumentException, ParseException;
void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
/**
* 向上级回复DeviceStatus查询信息

View File

@@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
@@ -711,7 +712,7 @@ public class SIPCommander implements ISIPCommander {
* @param recordCmdStr 录像命令Record / StopRecord
*/
@Override
public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
StringBuffer cmdXml = new StringBuffer(200);
String charset = device.getCharset();
cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
@@ -729,7 +730,7 @@ public class SIPCommander implements ISIPCommander {
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
}
/**
@@ -763,7 +764,7 @@ public class SIPCommander implements ISIPCommander {
* @param guardCmdStr "SetGuard"/"ResetGuard"
*/
@Override
public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
StringBuffer cmdXml = new StringBuffer(200);
String charset = device.getCharset();
@@ -778,7 +779,7 @@ public class SIPCommander implements ISIPCommander {
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
}
/**
@@ -787,7 +788,7 @@ public class SIPCommander implements ISIPCommander {
* @param device 视频设备
*/
@Override
public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
StringBuffer cmdXml = new StringBuffer(200);
String charset = device.getCharset();
@@ -814,7 +815,7 @@ public class SIPCommander implements ISIPCommander {
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
}
/**
@@ -850,12 +851,14 @@ public class SIPCommander implements ISIPCommander {
* 看守位控制命令
*
* @param device 视频设备
* @param channelId 通道id非通道则是设备本身
* @param frontCmd 上级平台的指令,如果存在则直接下发
* @param enabled 看守位使能1 = 开启0 = 关闭
* @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
* @param presetIndex 调用预置位编号开启看守位时使用取值范围0~255
*/
@Override
public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
StringBuffer cmdXml = new StringBuffer(200);
String charset = device.getCharset();
@@ -890,7 +893,7 @@ public class SIPCommander implements ISIPCommander {
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
}
/**

View File

@@ -81,6 +81,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
@Autowired
private VideoStreamSessionManager streamSession;
@Autowired
private DynamicTask dynamicTask;
@Override
public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
register(parentPlatform, null, null, errorEvent, okEvent, false, true);
@@ -129,13 +132,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
String characterSet = parentPlatform.getCharacterSet();
StringBuffer keepaliveXml = new StringBuffer(200);
keepaliveXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
keepaliveXml.append("<Notify>\r\n");
keepaliveXml.append("<CmdType>Keepalive</CmdType>\r\n");
keepaliveXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
keepaliveXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
keepaliveXml.append("<Status>OK</Status>\r\n");
keepaliveXml.append("</Notify>\r\n");
keepaliveXml.append("<?xml version=\"1.0\" encoding=\"")
.append(characterSet).append("\"?>\r\n")
.append("<Notify>\r\n")
.append("<CmdType>Keepalive</CmdType>\r\n")
.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
.append("<Status>OK</Status>\r\n")
.append("</Notify>\r\n");
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
@@ -153,7 +157,6 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
* 向上级回复通道信息
* @param channel 通道信息
* @param parentPlatform 平台信息
* @return
*/
@Override
public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException {
@@ -180,18 +183,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
if ( parentPlatform ==null) {
return ;
}
sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0);
sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0, true);
}
private String getCatalogXml(List<DeviceChannel> channels, String sn, ParentPlatform parentPlatform, int size) {
String characterSet = parentPlatform.getCharacterSet();
StringBuffer catalogXml = new StringBuffer(600);
catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n");
catalogXml.append("<Response>\r\n");
catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
catalogXml.append("<SN>" +sn + "</SN>\r\n");
catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
catalogXml.append("<SumNum>" + size + "</SumNum>\r\n");
catalogXml.append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n")
.append("<Response>\r\n")
.append("<CmdType>Catalog</CmdType>\r\n")
.append("<SN>" +sn + "</SN>\r\n")
.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
.append("<SumNum>" + size + "</SumNum>\r\n")
.append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
if (channels.size() > 0) {
for (DeviceChannel channel : channels) {
if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
@@ -242,7 +245,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
return catalogXml.toString();
}
private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index) throws SipException, InvalidArgumentException, ParseException {
private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException {
if (index >= channels.size()) {
return;
}
@@ -256,15 +259,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
// callid
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {
int indexNext = index + parentPlatform.getCatalogGroup();
try {
sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
}
});
SIPRequest request = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
String timeoutTaskKey = "catalog_task_" + parentPlatform.getServerGBId() + sn;
String callId = request.getCallIdHeader().getCallId();
if (sendAfterResponse) {
// 默认按照收到200回复后发送下一条 如果超时收不到回复就以30毫秒的间隔直接发送。
dynamicTask.startDelay(timeoutTaskKey, ()->{
sipSubscribe.removeOkSubscribe(callId);
int indexNext = index + parentPlatform.getCatalogGroup();
try {
sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
}
}, 3000);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
dynamicTask.stop(timeoutTaskKey);
}, eventResult -> {
dynamicTask.stop(timeoutTaskKey);
int indexNext = index + parentPlatform.getCatalogGroup();
try {
sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, true);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
}
});
}else {
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
dynamicTask.stop(timeoutTaskKey);
}, null);
dynamicTask.startDelay(timeoutTaskKey, ()->{
int indexNext = index + parentPlatform.getCatalogGroup();
try {
sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage());
}
}, 30);
}
}
/**
@@ -275,7 +312,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
* @return
*/
@Override
public void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
public void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
if (parentPlatform == null) {
return;
}
@@ -285,11 +322,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
deviceInfoXml.append("<Response>\r\n");
deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
deviceInfoXml.append("<DeviceName>" + parentPlatform.getName() + "</DeviceName>\r\n");
deviceInfoXml.append("<Manufacturer>wvp</Manufacturer>\r\n");
deviceInfoXml.append("<Model>wvp-28181-2.0</Model>\r\n");
deviceInfoXml.append("<Firmware>2.0.202107</Firmware>\r\n");
deviceInfoXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
deviceInfoXml.append("<DeviceName>" + device.getName() + "</DeviceName>\r\n");
deviceInfoXml.append("<Manufacturer>" + device.getManufacturer() + "</Manufacturer>\r\n");
deviceInfoXml.append("<Model>" + device.getModel() + "</Model>\r\n");
deviceInfoXml.append("<Firmware>" + device.getFirmware() + "</Firmware>\r\n");
deviceInfoXml.append("<Result>OK</Result>\r\n");
deviceInfoXml.append("</Response>\r\n");
@@ -314,15 +351,15 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
String statusStr = (status==1)?"ONLINE":"OFFLINE";
String characterSet = parentPlatform.getCharacterSet();
StringBuffer deviceStatusXml = new StringBuffer(600);
deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
deviceStatusXml.append("<Response>\r\n");
deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
deviceStatusXml.append("<SN>" +sn + "</SN>\r\n");
deviceStatusXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
deviceStatusXml.append("<Result>OK</Result>\r\n");
deviceStatusXml.append("<Online>"+statusStr+"</Online>\r\n");
deviceStatusXml.append("<Status>OK</Status>\r\n");
deviceStatusXml.append("</Response>\r\n");
deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Response>\r\n")
.append("<CmdType>DeviceStatus</CmdType>\r\n")
.append("<SN>" +sn + "</SN>\r\n")
.append("<DeviceID>" + channelId + "</DeviceID>\r\n")
.append("<Result>OK</Result>\r\n")
.append("<Online>"+statusStr+"</Online>\r\n")
.append("<Status>OK</Status>\r\n")
.append("</Response>\r\n");
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
@@ -341,18 +378,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
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>MobilePosition</CmdType>\r\n");
deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
deviceStatusXml.append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n");
deviceStatusXml.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n");
deviceStatusXml.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n");
deviceStatusXml.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n");
deviceStatusXml.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n");
deviceStatusXml.append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n");
deviceStatusXml.append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n");
deviceStatusXml.append("</Notify>\r\n");
deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Notify>\r\n")
.append("<CmdType>MobilePosition</CmdType>\r\n")
.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
.append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n")
.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n")
.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n")
.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n")
.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n")
.append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n")
.append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n")
.append("</Notify>\r\n");
sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> {
logger.error("发送NOTIFY通知消息失败。错误{} {}", eventResult.statusCode, eventResult.msg);
@@ -369,21 +406,21 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
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");
deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Notify>\r\n")
.append("<CmdType>Alarm</CmdType>\r\n")
.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n")
.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")
.append("<info>\r\n")
.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n")
.append("</info>\r\n")
.append("</Notify>\r\n");
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
@@ -442,13 +479,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
StringBuffer catalogXml = new StringBuffer(600);
String characterSet = parentPlatform.getCharacterSet();
catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
catalogXml.append("<Notify>\r\n");
catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
catalogXml.append("<SumNum>1</SumNum>\r\n");
catalogXml.append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Notify>\r\n")
.append("<CmdType>Catalog</CmdType>\r\n")
.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
.append("<SumNum>1</SumNum>\r\n")
.append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
if (channels.size() > 0) {
for (DeviceChannel channel : channels) {
if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
@@ -469,16 +506,16 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
if (channel.getParental() == 0) {
// 通道项
catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");
catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");
catalogXml.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n")
.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n")
.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n")
.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性
catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");
catalogXml.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n");
catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n")
.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n")
.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n")
.append("<Address>" + channel.getAddress() + "</Address>\r\n");
}
if (!"presence".equals(subscribeInfo.getEventType())) {
catalogXml.append("<Event>" + type + "</Event>\r\n");
@@ -488,8 +525,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
catalogXml.append("</Item>\r\n");
}
}
catalogXml.append("</DeviceList>\r\n");
catalogXml.append("</Notify>\r\n");
catalogXml.append("</DeviceList>\r\n")
.append("</Notify>\r\n");
return catalogXml.toString();
}
@@ -535,26 +572,26 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
String characterSet = parentPlatform.getCharacterSet();
StringBuffer catalogXml = new StringBuffer(600);
catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
catalogXml.append("<Notify>\r\n");
catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
catalogXml.append("<SumNum>1</SumNum>\r\n");
catalogXml.append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Notify>\r\n")
.append("<CmdType>Catalog</CmdType>\r\n")
.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
.append("<SumNum>1</SumNum>\r\n")
.append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
if (channels.size() > 0) {
for (DeviceChannel channel : channels) {
if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
channel.setParentId(parentPlatform.getDeviceGBId());
}
catalogXml.append("<Item>\r\n");
catalogXml.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n");
catalogXml.append("<Event>" + type + "</Event>\r\n");
catalogXml.append("</Item>\r\n");
catalogXml.append("<Item>\r\n")
.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n")
.append("<Event>" + type + "</Event>\r\n")
.append("</Item>\r\n");
}
}
catalogXml.append("</DeviceList>\r\n");
catalogXml.append("</Notify>\r\n");
catalogXml.append("</DeviceList>\r\n")
.append("</Notify>\r\n");
return catalogXml.toString();
}
@Override
@@ -564,12 +601,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}
String characterSet = parentPlatform.getCharacterSet();
StringBuffer recordXml = new StringBuffer(600);
recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
recordXml.append("<Response>\r\n");
recordXml.append("<CmdType>RecordInfo</CmdType>\r\n");
recordXml.append("<SN>" +recordInfo.getSn() + "</SN>\r\n");
recordXml.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n");
recordXml.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Response>\r\n")
.append("<CmdType>RecordInfo</CmdType>\r\n")
.append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n")
.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
if (recordInfo.getRecordList() == null ) {
recordXml.append("<RecordList Num=\"0\">\r\n");
}else {
@@ -578,12 +615,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
for (RecordItem recordItem : recordInfo.getRecordList()) {
recordXml.append("<Item>\r\n");
if (deviceChannel != null) {
recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n");
recordXml.append("<Name>" + recordItem.getName() + "</Name>\r\n");
recordXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n");
recordXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n");
recordXml.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n");
recordXml.append("<Type>" + recordItem.getType() + "</Type>\r\n");
recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n")
.append("<Name>" + recordItem.getName() + "</Name>\r\n")
.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n")
.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n")
.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n")
.append("<Type>" + recordItem.getType() + "</Type>\r\n");
if (!ObjectUtils.isEmpty(recordItem.getFileSize())) {
recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n");
}
@@ -596,8 +633,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}
}
recordXml.append("</RecordList>\r\n");
recordXml.append("</Response>\r\n");
recordXml.append("</RecordList>\r\n")
.append("</Response>\r\n");
// callid
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
@@ -616,13 +653,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
String characterSet = parentPlatform.getCharacterSet();
StringBuffer mediaStatusXml = new StringBuffer(200);
mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
mediaStatusXml.append("<Notify>\r\n");
mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
mediaStatusXml.append("</Notify>\r\n");
mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Notify>\r\n")
.append("<CmdType>MediaStatus</CmdType>\r\n")
.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n")
.append("<NotifyType>121</NotifyType>\r\n")
.append("</Notify>\r\n");
SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, mediaStatusXml.toString(),
sendRtpItem);

View File

@@ -16,7 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import javax.sip.*;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.ExpiresHeader;
@@ -42,15 +41,6 @@ public abstract class SIPRequestProcessorParent {
@Autowired
private SIPSender sipSender;
public AddressFactory getAddressFactory() {
try {
return SipFactory.getInstance().createAddressFactory();
} catch (PeerUnavailableException e) {
e.printStackTrace();
}
return null;
}
public HeaderFactory getHeaderFactory() {
try {
return SipFactory.getInstance().createHeaderFactory();

View File

@@ -275,7 +275,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
return;
} else {
logger.info("通道不存在返回404");
logger.info("通道不存在返回404: {}", channelId);
try {
// 通道不存在发404资源不存在
responseAck(request, Response.NOT_FOUND);

View File

@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
import com.genersoft.iot.vmp.conf.ServiceInfo;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
@@ -82,6 +83,19 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
RequestEventExt evtExt = (RequestEventExt) evt;
String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
logger.info("[注册请求] 开始处理: {}", requestAddress);
// MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
// QueryExp protocol = Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"));
//// ObjectName name = new ObjectName("*:type=Connector,*");
// ObjectName name = new ObjectName("*:*");
// Set<ObjectName> objectNames = beanServer.queryNames(name, protocol);
// for (ObjectName objectName : objectNames) {
// String catalina = objectName.getDomain();
// if ("Catalina".equals(catalina)) {
// System.out.println(objectName.getKeyProperty("port"));
// }
// }
System.out.println(ServiceInfo.getServerPort());
SIPRequest request = (SIPRequest)evt.getRequest();
Response response = null;
boolean passwordCorrect = false;

View File

@@ -1,8 +1,11 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd;
import com.genersoft.iot.vmp.VManageBootstrap;
import com.genersoft.iot.vmp.common.enums.DeviceControlType;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DragZoomRequest;
import com.genersoft.iot.vmp.gb28181.bean.HomePositionRequest;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -19,17 +22,14 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.sip.*;
import javax.sip.address.SipURI;
import javax.sip.header.HeaderAddress;
import javax.sip.header.ToHeader;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.Iterator;
import java.util.List;
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.*;
@Component
public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
@@ -81,7 +81,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
} catch (InvalidArgumentException | ParseException | SipException e) {
logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
}
taskExecutor.execute(()->{
taskExecutor.execute(() -> {
// 远程启动
// try {
// Thread.sleep(3000);
@@ -101,13 +101,12 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
// logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
// }
});
} else {
// 远程启动指定设备
}
}
// 云台/前端控制命令
if (!ObjectUtils.isEmpty(getText(rootElement,"PTZCmd")) && !parentPlatform.getServerGBId().equals(targetGBId)) {
String cmdString = getText(rootElement,"PTZCmd");
DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
logger.info("[接受deviceControl命令] 命令: {}", deviceControlType);
if (!ObjectUtils.isEmpty(deviceControlType) && !parentPlatform.getServerGBId().equals(targetGBId)) {
//判断是否存在该通道
Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
if (deviceForPlatform == null) {
try {
@@ -117,25 +116,240 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
}
return;
}
try {
cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> {
// 失败的回复
try {
responseAck(request, eventResult.statusCode, eventResult.msg);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());
}
}, eventResult -> {
// 成功的回复
try {
responseAck(request, eventResult.statusCode);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());
}
});
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 云台/前端: {}", e.getMessage());
switch (deviceControlType) {
case PTZ:
handlePtzCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.PTZ);
break;
case ALARM:
handleAlarmCmd(deviceForPlatform, rootElement, request);
break;
case GUARD:
handleGuardCmd(deviceForPlatform, rootElement, request, DeviceControlType.GUARD);
break;
case RECORD:
handleRecordCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.RECORD);
break;
case I_FRAME:
handleIFameCmd(deviceForPlatform, request, channelId);
break;
case TELE_BOOT:
handleTeleBootCmd(deviceForPlatform, request);
break;
case DRAG_ZOOM_IN:
handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_IN);
break;
case DRAG_ZOOM_OUT:
handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_OUT);
break;
case HOME_POSITION:
handleHomePositionCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.HOME_POSITION);
break;
default:
break;
}
}
}
/**
* 处理云台指令
*
* @param device 设备
* @param channelId 通道id
* @param rootElement
* @param request
*/
private void handlePtzCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
String cmdString = getText(rootElement, type.getVal());
try {
cmder.fronEndCmd(device, channelId, cmdString,
errorResult -> onError(request, errorResult),
okResult -> onOk(request, okResult));
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 云台/前端: {}", e.getMessage());
}
}
/**
* 处理强制关键帧
*
* @param device 设备
* @param channelId 通道id
*/
private void handleIFameCmd(Device device, SIPRequest request, String channelId) {
try {
cmder.iFrameCmd(device, channelId);
responseAck(request, Response.OK);
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage());
}
}
/**
* 处理重启命令
*
* @param device 设备信息
*/
private void handleTeleBootCmd(Device device, SIPRequest request) {
try {
cmder.teleBootCmd(device);
responseAck(request, Response.OK);
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 重启: {}", e.getMessage());
}
}
/**
* 处理拉框控制***
*
* @param device 设备信息
* @param channelId 通道id
* @param rootElement 根节点
* @param type 消息类型
*/
private void handleDragZoom(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
try {
DragZoomRequest dragZoomRequest = loadElement(rootElement, DragZoomRequest.class);
DragZoomRequest.DragZoom dragZoom = dragZoomRequest.getDragZoomIn();
if (dragZoom == null) {
dragZoom = dragZoomRequest.getDragZoomOut();
}
StringBuffer cmdXml = new StringBuffer(200);
cmdXml.append("<" + type.getVal() + ">\r\n");
cmdXml.append("<Length>" + dragZoom.getLength() + "</Length>\r\n");
cmdXml.append("<Width>" + dragZoom.getWidth() + "</Width>\r\n");
cmdXml.append("<MidPointX>" + dragZoom.getMidPointX() + "</MidPointX>\r\n");
cmdXml.append("<MidPointY>" + dragZoom.getMidPointY() + "</MidPointY>\r\n");
cmdXml.append("<LengthX>" + dragZoom.getLengthX() + "</LengthX>\r\n");
cmdXml.append("<LengthY>" + dragZoom.getLengthY() + "</LengthY>\r\n");
cmdXml.append("</" + type.getVal() + ">\r\n");
cmder.dragZoomCmd(device, channelId, cmdXml.toString());
responseAck(request, Response.OK);
} catch (Exception e) {
logger.error("[命令发送失败] 拉框控制: {}", e.getMessage());
}
}
/**
* 处理看守位命令***
*
* @param device 设备信息
* @param channelId 通道id
* @param rootElement 根节点
* @param request 请求信息
* @param type 消息类型
*/
private void handleHomePositionCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
try {
HomePositionRequest homePosition = loadElement(rootElement, HomePositionRequest.class);
//获取整个消息主体,我们只需要修改请求头即可
HomePositionRequest.HomePosition info = homePosition.getHomePosition();
cmder.homePositionCmd(device, channelId, info.getEnabled(), info.getResetTime(), info.getPresetIndex(),
errorResult -> onError(request, errorResult),
okResult -> onOk(request, okResult));
} catch (Exception e) {
logger.error("[命令发送失败] 看守位设置: {}", e.getMessage());
}
}
/**
* 处理告警消息***
*
* @param device 设备信息
* @param rootElement 根节点
* @param request 请求信息
*/
private void handleAlarmCmd(Device device, Element rootElement, SIPRequest request) {
//告警方法
String alarmMethod = "";
//告警类型
String alarmType = "";
List<Element> info = rootElement.elements("Info");
if (info != null) {
for (Element element : info) {
alarmMethod = getText(element, "AlarmMethod");
alarmType = getText(element, "AlarmType");
}
}
try {
cmder.alarmCmd(device, alarmMethod, alarmType,
errorResult -> onError(request, errorResult),
okResult -> onOk(request, okResult));
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 告警消息: {}", e.getMessage());
}
}
/**
* 处理录像控制
*
* @param device 设备信息
* @param channelId 通道id
* @param rootElement 根节点
* @param request 请求信息
* @param type 消息类型
*/
private void handleRecordCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
//获取整个消息主体,我们只需要修改请求头即可
String cmdString = getText(rootElement, type.getVal());
try {
cmder.recordCmd(device, channelId, cmdString,
errorResult -> onError(request, errorResult),
okResult -> onOk(request, okResult));
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 录像控制: {}", e.getMessage());
}
}
/**
* 处理报警布防/撤防命令
*
* @param device 设备信息
* @param rootElement 根节点
* @param request 请求信息
* @param type 消息类型
*/
private void handleGuardCmd(Device device, Element rootElement, SIPRequest request, DeviceControlType type) {
//获取整个消息主体,我们只需要修改请求头即可
String cmdString = getText(rootElement, type.getVal());
try {
cmder.guardCmd(device, cmdString,
errorResult -> onError(request, errorResult),
okResult -> onOk(request, okResult));
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 布防/撤防命令: {}", e.getMessage());
}
}
/**
* 错误响应处理
*
* @param request 请求
* @param eventResult 响应结构
*/
private void onError(SIPRequest request, SipSubscribe.EventResult eventResult) {
// 失败的回复
try {
responseAck(request, eventResult.statusCode, eventResult.msg);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 回复: {}", e.getMessage());
}
}
/**
* 成功响应处理
*
* @param request 请求
* @param eventResult 响应结构
*/
private void onOk(SIPRequest request, SipSubscribe.EventResult eventResult) {
// 成功的回复
try {
responseAck(request, eventResult.statusCode);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 回复: {}", e.getMessage());
}
}
}

View File

@@ -181,11 +181,14 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
}
}
logger.info("[收到报警通知]内容:{}", JSON.toJSONString(deviceAlarm));
if ("7".equals(deviceAlarm.getAlarmMethod()) ) {
// 作者自用判断其他小伙伴需要此消息可以自行修改但是不要提在pr里
if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) {
// 发送给平台的报警信息。 发送redis通知
logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm));
AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
alarmChannelMessage.setGbId(channelId);
redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
continue;
@@ -264,6 +267,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
alarmChannelMessage.setGbId(channelId);
alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
return;
}

View File

@@ -1,9 +1,10 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
@@ -63,7 +64,6 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem
@Override
public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId();
FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
try {
// 回复200 OK

View File

@@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
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.query.QueryMessageHandler;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import gov.nist.javax.sip.message.SIPRequest;
import org.dom4j.Element;
import org.slf4j.Logger;
@@ -21,6 +22,8 @@ import javax.sip.header.FromHeader;
import javax.sip.message.Response;
import java.text.ParseException;
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
@Component
public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
@@ -32,6 +35,8 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
@Autowired
private SIPCommanderFroPlatform cmderFroPlatform;
@Autowired
private IVideoManagerStorage storager;
@Override
public void afterPropertiesSet() throws Exception {
@@ -52,10 +57,20 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
responseAck((SIPRequest) evt.getRequest(), Response.OK);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage());
return;
}
String sn = rootElement.element("SN").getText();
/*根据WVP原有的数据结构设备和通道是分开放置设备信息都是存放在设备表里通道表里的设备信息不可作为真实信息处理
大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。
我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/
String channelId = getText(rootElement, "DeviceID");
Device device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
if (device ==null){
logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId);
return;
}
try {
cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag());
cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag());
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage());
}

View File

@@ -102,8 +102,9 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
Element recordListElement = rootElementForCharset.element("RecordList");
if (recordListElement == null || sumNum == 0) {
logger.info("无录像数据");
int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, new ArrayList<>());
recordInfo.setCount(count);
eventPublisher.recordEndEventPush(recordInfo);
recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
releaseRequest(take.getDevice().getDeviceId(), sn);
} else {
Iterator<Element> recordListIterator = recordListElement.elementIterator();
@@ -137,12 +138,11 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
recordList.add(record);
}
recordInfo.setRecordList(recordList);
int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, recordList);recordInfo.setCount(count);
logger.info("[国标录像] {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
// 发送消息,如果是上级查询此录像,则会通过这里通知给上级
eventPublisher.recordEndEventPush(recordInfo);
int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);
logger.info("[国标录像] {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
}
if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
releaseRequest(take.getDevice().getDeviceId(), sn);
}

View File

@@ -0,0 +1,17 @@
package com.genersoft.iot.vmp.gb28181.utils;
import java.lang.annotation.*;
/**
* @author gaofuwang
* @version 1.0
* @date 2022/6/28 14:58
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MessageElement {
String value();
String subVal() default "";
}

View File

@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.Device;
@@ -15,12 +16,16 @@ import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.ReflectionUtils;
import javax.sip.RequestEvent;
import javax.sip.message.Request;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
/**
@@ -411,4 +416,76 @@ public class XmlUtil {
}
return deviceChannel;
}
/**
* 新增方法支持内部嵌套
*
* @param element xmlElement
* @param clazz 结果类
* @param <T> 泛型
* @return 结果对象
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws InstantiationException
* @throws IllegalAccessException
*/
public static <T> T loadElement(Element element, Class<T> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Field[] fields = clazz.getDeclaredFields();
T t = clazz.getDeclaredConstructor().newInstance();
for (Field field : fields) {
ReflectionUtils.makeAccessible(field);
MessageElement annotation = field.getAnnotation(MessageElement.class);
if (annotation == null) {
continue;
}
String value = annotation.value();
String subVal = annotation.subVal();
Element element1 = element.element(value);
if (element1 == null) {
continue;
}
if ("".equals(subVal)) {
// 无下级数据
Object fieldVal = element1.isTextOnly() ? element1.getText() : loadElement(element1, field.getType());
Object o = simpleTypeDeal(field.getType(), fieldVal);
ReflectionUtils.setField(field, t, o);
} else {
// 存在下级数据
ArrayList<Object> list = new ArrayList<>();
Type genericType = field.getGenericType();
if (!(genericType instanceof ParameterizedType)) {
continue;
}
Class<?> aClass = (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
for (Element element2 : element1.elements(subVal)) {
list.add(loadElement(element2, aClass));
}
ReflectionUtils.setField(field, t, list);
}
}
return t;
}
/**
* 简单类型处理
*
* @param tClass
* @param val
* @return
*/
private static Object simpleTypeDeal(Class<?> tClass, Object val) {
if (tClass.equals(String.class)) {
return val.toString();
}
if (tClass.equals(Integer.class)) {
return Integer.valueOf(val.toString());
}
if (tClass.equals(Double.class)) {
return Double.valueOf(val.toString());
}
if (tClass.equals(Long.class)) {
return Long.valueOf(val.toString());
}
return val;
}
}

View File

@@ -36,7 +36,7 @@ public class ZLMRESTfulUtils {
// 设置连接超时时间
httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS);
// 设置读取超时时间
httpClientBuilder.readTimeout(15,TimeUnit.SECONDS);
httpClientBuilder.readTimeout(10,TimeUnit.SECONDS);
// 设置连接池
httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
if (logger.isDebugEnabled()) {
@@ -189,6 +189,7 @@ public class ZLMRESTfulUtils {
FileOutputStream outStream = new FileOutputStream(snapFile);
outStream.write(Objects.requireNonNull(response.body()).bytes());
outStream.flush();
outStream.close();
} else {
logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message()));

View File

@@ -0,0 +1,36 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
public class HookResult {
private int code;
private String msg;
public HookResult() {
}
public HookResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static HookResult SUCCESS(){
return new HookResult(0, "success");
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}

View File

@@ -0,0 +1,44 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
public class HookResultForOnPublish extends HookResult{
private boolean enable_audio;
private boolean enable_mp4;
private int mp4_max_second;
public HookResultForOnPublish() {
}
public static HookResultForOnPublish SUCCESS(){
return new HookResultForOnPublish(0, "success");
}
public HookResultForOnPublish(int code, String msg) {
setCode(code);
setMsg(msg);
}
public boolean isEnable_audio() {
return enable_audio;
}
public void setEnable_audio(boolean enable_audio) {
this.enable_audio = enable_audio;
}
public boolean isEnable_mp4() {
return enable_mp4;
}
public void setEnable_mp4(boolean enable_mp4) {
this.enable_mp4 = enable_mp4;
}
public int getMp4_max_second() {
return mp4_max_second;
}
public void setMp4_max_second(int mp4_max_second) {
this.mp4_max_second = mp4_max_second;
}
}

View File

@@ -152,6 +152,10 @@ public class GbStreamServiceImpl implements IGbStreamService {
@Override
public void sendCatalogMsg(GbStream gbStream, String type) {
if (gbStream == null || type == null) {
logger.warn("[发送目录订阅]类型流信息或类型为NULL");
return;
}
List<GbStream> gbStreams = new ArrayList<>();
if (gbStream.getGbId() != null) {
gbStreams.add(gbStream);

View File

@@ -33,6 +33,7 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.utils.JsonUtil;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import okhttp3.OkHttpClient;
@@ -241,7 +242,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
for (Object mediaServerKey : mediaServerKeys) {
String key = (String) mediaServerKey;
MediaServerItem mediaServerItem = (MediaServerItem) RedisUtil.get(key);
MediaServerItem mediaServerItem = JsonUtil.redisJsonToObject(key, MediaServerItem.class);
if (Objects.isNull(mediaServerItem)) {
continue;
}
// 检查状态
Double aDouble = RedisUtil.zScore(onlineKey, mediaServerItem.getId());
if (aDouble != null) {
@@ -293,7 +297,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
return null;
}
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId;
return (MediaServerItem)RedisUtil.get(key);
return JsonUtil.redisJsonToObject(key, MediaServerItem.class);
}
@@ -410,8 +414,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain());
serverItem.setSsrcConfig(ssrcConfig);
}else {
MediaServerItem mediaServerItemInRedis = (MediaServerItem)RedisUtil.get(key);
serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
MediaServerItem mediaServerItemInRedis = JsonUtil.redisJsonToObject(key, MediaServerItem.class);
if (Objects.nonNull(mediaServerItemInRedis)) {
serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
}
}
RedisUtil.set(key, serverItem);
resetOnlineServerItem(serverItem);

View File

@@ -184,7 +184,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
@Override
public boolean stop(String app, String streamId) {
StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId);
gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL);
if (streamPushItem != null) {
gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL);
}
platformGbStreamMapper.delByAppAndStream(app, streamId);
gbStreamMapper.del(app, streamId);

View File

@@ -62,16 +62,16 @@ public class RedisAlarmMsgListener implements MessageListener {
}
String gbId = alarmChannelMessage.getGbId();
DeviceAlarm deviceAlarm = new DeviceAlarm();
deviceAlarm.setCreateTime(DateUtil.getNow());
deviceAlarm.setChannelId(gbId);
deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription());
deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
deviceAlarm.setAlarmPriority("1");
deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
deviceAlarm.setAlarmType("1");
deviceAlarm.setLongitude(0D);
deviceAlarm.setLatitude(0D);
DeviceAlarm deviceAlarm = new DeviceAlarm();
deviceAlarm.setCreateTime(DateUtil.getNow());
deviceAlarm.setChannelId(gbId);
deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription());
deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
deviceAlarm.setAlarmPriority("1");
deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
deviceAlarm.setLongitude(0);
deviceAlarm.setLatitude(0);
if (ObjectUtils.isEmpty(gbId)) {
// 发送给所有的上级

View File

@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.storager;
import com.genersoft.iot.vmp.gb28181.bean.*;
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.gb28181.platform.bean.ChannelReduce;
@@ -186,7 +185,13 @@ public interface IVideoManagerStorage {
Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId);
/**
* 针对deviceinfo指令的查询接口
* @param platformId 平台id
* @param channelId 通道id
* @return 设备信息
*/
Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId);
/**
* 添加Mobile Position设备移动位置
* @param mobilePosition
@@ -324,6 +329,8 @@ public interface IVideoManagerStorage {
*/
boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList);
boolean updateChannels(String deviceId, List<DeviceChannel> deviceChannelList);
/**
* 获取目录信息
* @param platformId

View File

@@ -1,9 +1,10 @@
package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
@@ -20,7 +21,7 @@ public interface DeviceAlarmMapper {
int add(DeviceAlarm alarm);
@Select(value = {" <script>" +
@Select( value = {" <script>" +
" SELECT * FROM device_alarm " +
" WHERE 1=1 " +
" <if test=\"deviceId != null\" > AND deviceId = #{deviceId}</if>" +

View File

@@ -107,4 +107,14 @@ public interface PlatformChannelMapper {
"DELETE FROM platform_gb_channel WHERE platformId=#{platformId} and catalogId=#{catalogId}" +
"</script>")
int delChannelForGBByCatalogId(String platformId, String catalogId);
@Select("select dc.channelId deviceId,dc.name,d.manufacturer,d.model,d.firmware\n" +
"from platform_gb_channel pgc\n" +
" left join device_channel dc on dc.id = pgc.deviceChannelId\n" +
" left join device d on dc.deviceId = d.deviceId\n" +
"where dc.channelId = #{channelId} and pgc.platformId=#{platformId}")
List<Device> queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId);
@Select("SELECT pgc.platformId FROM platform_gb_channel pgc left join device_channel dc on dc.id = pgc.deviceChannelId WHERE dc.channelId='${channelId}'")
List<String> queryParentPlatformByChannelId(String channelId);
}

View File

@@ -17,6 +17,7 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.utils.JsonUtil;
import com.genersoft.iot.vmp.utils.SystemInfoUtils;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import org.slf4j.Logger;
@@ -157,7 +158,10 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
}
for (Object player : players) {
String key = (String) player;
StreamInfo streamInfo = (StreamInfo) RedisUtil.get(key);
StreamInfo streamInfo = JsonUtil.redisJsonToObject(key, StreamInfo.class);
if (Objects.isNull(streamInfo)) {
continue;
}
streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo);
}
return streamInfos;
@@ -624,8 +628,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
public ThirdPartyGB queryMemberNoGBId(String queryKey) {
String key = VideoManagerConstants.WVP_STREAM_GB_ID_PREFIX + queryKey;
JSONObject jsonObject = (JSONObject)RedisUtil.get(key);
return jsonObject.to(ThirdPartyGB.class);
return JsonUtil.redisJsonToObject(key, ThirdPartyGB.class);
}
@Override
@@ -664,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
public Device getDevice(String deviceId) {
String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId;
return (Device)RedisUtil.get(key);
return JsonUtil.redisJsonToObject(key, Device.class);
}
@Override
@@ -676,7 +679,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
public GPSMsgInfo getGpsMsgInfo(String gbId) {
String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gbId;
return (GPSMsgInfo)RedisUtil.get(key);
return JsonUtil.redisJsonToObject(key, GPSMsgInfo.class);
}
@Override
@@ -686,9 +689,9 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
List<Object> keys = RedisUtil.scan(scanKey);
for (Object o : keys) {
String key = (String) o;
GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) RedisUtil.get(key);
if (!gpsMsgInfo.isStored()) { // 只取没有存过得
result.add((GPSMsgInfo) RedisUtil.get(key));
GPSMsgInfo gpsMsgInfo = JsonUtil.redisJsonToObject(key, GPSMsgInfo.class);
if (Objects.nonNull(gpsMsgInfo) && !gpsMsgInfo.isStored()) { // 只取没有存过得
result.add(JsonUtil.redisJsonToObject(key, GPSMsgInfo.class));
}
}
@@ -710,7 +713,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) {
String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ;
return (StreamAuthorityInfo) RedisUtil.get(key);
return JsonUtil.redisJsonToObject(key, StreamAuthorityInfo.class);
}
@@ -721,7 +724,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
List<Object> keys = RedisUtil.scan(scanKey);
for (Object o : keys) {
String key = (String) o;
result.add((StreamAuthorityInfo) RedisUtil.get(key));
result.add(JsonUtil.redisJsonToObject(key, StreamAuthorityInfo.class));
}
return result;
}
@@ -735,7 +738,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
List<Object> keys = RedisUtil.scan(scanKey);
if (keys.size() > 0) {
String key = (String) keys.get(0);
result = (OnStreamChangedHookParam)RedisUtil.get(key);
result = JsonUtil.redisJsonToObject(key, OnStreamChangedHookParam.class);
}
return result;
@@ -827,7 +830,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
public void sendAlarmMsg(AlarmChannelMessage msg) {
String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE;
logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg));
RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
}

View File

@@ -126,6 +126,15 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
if (allChannelMap.get(deviceChannel.getChannelId()).getStatus() !=deviceChannel.getStatus()){
List<String> strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId());
if (!CollectionUtils.isEmpty(strings)){
strings.forEach(platformId->{
eventPublisher.catalogEventPublish(platformId, deviceChannel, deviceChannel.getStatus()==1?CatalogEvent.ON:CatalogEvent.OFF);
});
}
}
}
channels.add(deviceChannel);
if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) {
@@ -187,6 +196,119 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
}
@Override
public boolean updateChannels(String deviceId, List<DeviceChannel> deviceChannelList) {
if (CollectionUtils.isEmpty(deviceChannelList)) {
return false;
}
List<DeviceChannel> allChannels = deviceChannelMapper.queryAllChannels(deviceId);
Map<String,DeviceChannel> allChannelMap = new ConcurrentHashMap<>();
if (allChannels.size() > 0) {
for (DeviceChannel deviceChannel : allChannels) {
allChannelMap.put(deviceChannel.getChannelId(), deviceChannel);
}
}
List<DeviceChannel> addChannels = new ArrayList<>();
List<DeviceChannel> updateChannels = new ArrayList<>();
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
// 数据去重
StringBuilder stringBuilder = new StringBuilder();
Map<String, Integer> subContMap = new HashMap<>();
if (deviceChannelList.size() > 0) {
// 数据去重
Set<String> gbIdSet = new HashSet<>();
for (DeviceChannel deviceChannel : deviceChannelList) {
if (!gbIdSet.contains(deviceChannel.getChannelId())) {
gbIdSet.add(deviceChannel.getChannelId());
if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
updateChannels.add(deviceChannel);
}else {
addChannels.add(deviceChannel);
}
if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) {
if (subContMap.get(deviceChannel.getParentId()) == null) {
subContMap.put(deviceChannel.getParentId(), 1);
}else {
Integer count = subContMap.get(deviceChannel.getParentId());
subContMap.put(deviceChannel.getParentId(), count++);
}
}
}else {
stringBuilder.append(deviceChannel.getChannelId()).append(",");
}
}
if (addChannels.size() > 0) {
for (DeviceChannel channel : addChannels) {
if (subContMap.get(channel.getChannelId()) != null){
channel.setSubCount(subContMap.get(channel.getChannelId()));
}
}
}
if (updateChannels.size() > 0) {
for (DeviceChannel channel : updateChannels) {
if (subContMap.get(channel.getChannelId()) != null){
channel.setSubCount(subContMap.get(channel.getChannelId()));
}
}
}
}
if (stringBuilder.length() > 0) {
logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder);
}
if(CollectionUtils.isEmpty(updateChannels) && CollectionUtils.isEmpty(addChannels) ){
logger.info("通道更新,数据为空={}" , deviceChannelList);
return false;
}
try {
int limitCount = 300;
boolean result = false;
if (addChannels.size() > 0) {
if (addChannels.size() > limitCount) {
for (int i = 0; i < addChannels.size(); i += limitCount) {
int toIndex = i + limitCount;
if (i + limitCount > addChannels.size()) {
toIndex = addChannels.size();
}
result = result || deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)) < 0;
}
}else {
result = result || deviceChannelMapper.batchAdd(addChannels) < 0;
}
}
if (updateChannels.size() > 0) {
if (updateChannels.size() > limitCount) {
for (int i = 0; i < updateChannels.size(); i += limitCount) {
int toIndex = i + limitCount;
if (i + limitCount > updateChannels.size()) {
toIndex = updateChannels.size();
}
result = result || deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex)) < 0;
}
}else {
result = result || deviceChannelMapper.batchUpdate(updateChannels) < 0;
}
}
if (result) {
//事务回滚
dataSourceTransactionManager.rollback(transactionStatus);
}else {
//手动提交
dataSourceTransactionManager.commit(transactionStatus);
}
return true;
}catch (Exception e) {
e.printStackTrace();
dataSourceTransactionManager.rollback(transactionStatus);
return false;
}
}
@Override
public void deviceChannelOnline(String deviceId, String channelId) {
deviceChannelMapper.online(deviceId, channelId);
@@ -464,6 +586,20 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
}
@Override
public Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId) {
List<Device> devices = platformChannelMapper.queryDeviceInfoByPlatformIdAndChannelId(platformId, channelId);
if (devices.size() > 1) {
// 出现长度大于0的时候肯定是国标通道的ID重复了
logger.warn("国标ID存在重复{}", channelId);
}
if (devices.size() == 0) {
return null;
}else {
return devices.get(0);
}
}
/**
* 查询最新移动位置
* @param deviceId

View File

@@ -0,0 +1,36 @@
package com.genersoft.iot.vmp.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import java.util.Objects;
/**
* JsonUtil
*
* @author KunLong-Luo
* @version 1.0.0
* @since 2023/2/2 15:24
*/
public final class JsonUtil {
private JsonUtil() {
}
/**
* safe json type conversion
*
* @param key redis key
* @param clazz cast type
* @param <T>
* @return result type
*/
public static <T> T redisJsonToObject(String key, Class<T> clazz) {
Object jsonObject = RedisUtil.get(key);
if (Objects.isNull(jsonObject)) {
return null;
}
return clazz.cast(jsonObject);
}
}

View File

@@ -110,7 +110,7 @@ public class DeviceControl {
msg.setKey(key);
msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg));
resultHolder.invokeAllResult(msg);
});
},null);
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
@@ -143,7 +143,7 @@ public class DeviceControl {
msg.setKey(key);
msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg));
resultHolder.invokeResult(msg);
});
},null);
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage());
@@ -192,7 +192,7 @@ public class DeviceControl {
msg.setKey(key);
msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg));
resultHolder.invokeResult(msg);
});
},null);
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 报警复位: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
@@ -274,7 +274,7 @@ public class DeviceControl {
msg.setKey(key);
msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg));
resultHolder.invokeResult(msg);
});
},null);
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 看守位控制: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());

View File

@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.vmanager.gb28181.playback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.conf.exception.ServiceException;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
@@ -64,13 +65,16 @@ public class PlaybackController {
@Autowired
private DeferredResultHolder resultHolder;
@Autowired
private UserSetting userSetting;
@Operation(summary = "开始视频回放")
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "startTime", description = "开始时间", required = true)
@Parameter(name = "endTime", description = "结束时间", required = true)
@GetMapping("/start/{deviceId}/{channelId}")
public DeferredResult<WVPResult<StreamContent>> play(@PathVariable String deviceId, @PathVariable String channelId,
public DeferredResult<WVPResult<StreamContent>> start(@PathVariable String deviceId, @PathVariable String channelId,
String startTime, String endTime) {
if (logger.isDebugEnabled()) {
@@ -79,7 +83,7 @@ public class PlaybackController {
String uuid = UUID.randomUUID().toString();
String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(30000L);
DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
resultHolder.put(key, uuid, result);
WVPResult<StreamContent> wvpResult = new WVPResult<>();

View File

@@ -11,17 +11,13 @@ import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.security.sasl.AuthenticationException;
@@ -90,7 +86,7 @@ public class UserController {
@PostMapping("/add")
@Operation(summary = "停止视频回放")
@Operation(summary = "添加用户")
@Parameter(name = "username", description = "用户名", required = true)
@Parameter(name = "password", description = "密码未md5加密的密码", required = true)
@Parameter(name = "roleId", description = "角色ID", required = true)