mirror of
https://gitee.com/pan648540858/wvp-GB28181-pro.git
synced 2026-06-21 02:27:49 +08:00
支持相机基础信息配置
This commit is contained in:
parent
1fc8848cf9
commit
5a3b831717
@ -1,14 +1,16 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.dom4j.Element;
|
||||
|
||||
/**
|
||||
* 基础配置
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "基础配置")
|
||||
public class BasicParam {
|
||||
public class BasicParam implements DeviceConfigAware {
|
||||
|
||||
@Schema(description = "设备ID")
|
||||
private String deviceId;
|
||||
@ -46,4 +48,20 @@ public class BasicParam {
|
||||
basicParam.setHeartBeatCount(heartBeatCount);
|
||||
return basicParam;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String configType() {
|
||||
return "BasicParam";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromXml(Element element) {
|
||||
setName(XmlUtil.getText(element, "Name"));
|
||||
setExpiration(XmlUtil.getText(element, "Expiration"));
|
||||
setHeartBeatInterval(XmlUtil.getInteger(element, "HeartBeatInterval"));
|
||||
setHeartBeatCount(XmlUtil.getInteger(element, "HeartBeatCount"));
|
||||
setPositionCapability(XmlUtil.getInteger(element, "PositionCapability"));
|
||||
setLongitude(XmlUtil.getDouble(element, "Longitude"));
|
||||
setLatitude(XmlUtil.getDouble(element, "Latitude"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import org.dom4j.Element;
|
||||
|
||||
public interface DeviceConfigAware {
|
||||
|
||||
String configType();
|
||||
|
||||
void fromXml(Element element);
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.dom4j.Element;
|
||||
|
||||
@Data
|
||||
@Schema(description = "SVAC解码配置")
|
||||
public class SVACDecodeConfig implements DeviceConfigAware {
|
||||
|
||||
@Schema(description = "SVC参数")
|
||||
private SVCParam svcParam;
|
||||
|
||||
@Schema(description = "监控专用信息参数")
|
||||
private SurveillanceParam surveillanceParam;
|
||||
|
||||
@Override
|
||||
public String configType() {
|
||||
return "SVACDecodeConfig";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromXml(Element element) {
|
||||
Element svcEl = element.element("SVCParam");
|
||||
if (svcEl != null) {
|
||||
SVCParam s = new SVCParam();
|
||||
s.fromXml(svcEl);
|
||||
setSvcParam(s);
|
||||
}
|
||||
Element survEl = element.element("SurveillanceParam");
|
||||
if (survEl != null) {
|
||||
SurveillanceParam s = new SurveillanceParam();
|
||||
s.fromXml(survEl);
|
||||
setSurveillanceParam(s);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SVCParam {
|
||||
@Schema(description = "空域编码能力,0:不支持,1:1级增强,2:2级增强,3:3级增强")
|
||||
private Integer SVCSpaceSupportMode;
|
||||
|
||||
@Schema(description = "时域编码能力,0:不支持,1:1级增强,2:2级增强,3:3级增强")
|
||||
private Integer SVCTimeSupportMode;
|
||||
|
||||
public void fromXml(Element element) {
|
||||
setSVCSpaceSupportMode(XmlUtil.getInteger(element, "SVCSpaceSupportMode"));
|
||||
setSVCTimeSupportMode(XmlUtil.getInteger(element, "SVCTimeSupportMode"));
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SurveillanceParam {
|
||||
@Schema(description = "绝对时间信息显示开关,0:关闭,1:打开")
|
||||
private Integer TimeShowFlag;
|
||||
|
||||
@Schema(description = "监控事件信息显示开关,0:关闭,1:打开")
|
||||
private Integer EventShowFlag;
|
||||
|
||||
@Schema(description = "报警信息显示开关,0:关闭,1:打开")
|
||||
private Integer AlerShowtFlag;
|
||||
|
||||
public void fromXml(Element element) {
|
||||
setTimeShowFlag(XmlUtil.getInteger(element, "TimeShowFlag"));
|
||||
setEventShowFlag(XmlUtil.getInteger(element, "EventShowFlag"));
|
||||
setAlerShowtFlag(XmlUtil.getInteger(element, "AlerShowtFlag"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.dom4j.Element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "SVAC编码配置")
|
||||
public class SVACEncodeConfig implements DeviceConfigAware {
|
||||
|
||||
@Schema(description = "感兴趣区域参数")
|
||||
private ROIParam roiParam;
|
||||
|
||||
@Schema(description = "SVC参数")
|
||||
private SVCParam svcParam;
|
||||
|
||||
@Schema(description = "监控专用信息参数")
|
||||
private SurveillanceParam surveillanceParam;
|
||||
|
||||
@Schema(description = "音频参数")
|
||||
private AudioParam audioParam;
|
||||
|
||||
@Override
|
||||
public String configType() {
|
||||
return "SVACEncodeConfig";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromXml(Element element) {
|
||||
Element roiEl = element.element("ROIParam");
|
||||
if (roiEl != null) {
|
||||
ROIParam r = new ROIParam();
|
||||
r.fromXml(roiEl);
|
||||
setRoiParam(r);
|
||||
}
|
||||
Element svcEl = element.element("SVCParam");
|
||||
if (svcEl != null) {
|
||||
SVCParam s = new SVCParam();
|
||||
s.fromXml(svcEl);
|
||||
setSvcParam(s);
|
||||
}
|
||||
Element survEl = element.element("SurveillanceParam");
|
||||
if (survEl != null) {
|
||||
SurveillanceParam s = new SurveillanceParam();
|
||||
s.fromXml(survEl);
|
||||
setSurveillanceParam(s);
|
||||
}
|
||||
Element audioEl = element.element("AudioParam");
|
||||
if (audioEl != null) {
|
||||
AudioParam a = new AudioParam();
|
||||
a.fromXml(audioEl);
|
||||
setAudioParam(a);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ROIParam {
|
||||
@Schema(description = "感兴趣区域开关,0:关闭,1:打开")
|
||||
private Integer ROIFlag;
|
||||
|
||||
@Schema(description = "感兴趣区域数量,取值范围0~16")
|
||||
private Integer ROINumber;
|
||||
|
||||
@Schema(description = "感兴趣区域列表")
|
||||
private List<ROIItem> Item;
|
||||
|
||||
@Schema(description = "背景区域编码质量等级,0:一般,1:较好,2:好,3:很好")
|
||||
private Integer BackGroundQP;
|
||||
|
||||
@Schema(description = "背景跳过开关,0:关闭,1:打开")
|
||||
private Integer BackGroundSkipFlag;
|
||||
|
||||
public void fromXml(Element element) {
|
||||
setROIFlag(XmlUtil.getInteger(element, "ROIFlag"));
|
||||
setROINumber(XmlUtil.getInteger(element, "ROINumber"));
|
||||
List<Element> itemElements = element.elements("Item");
|
||||
if (!itemElements.isEmpty()) {
|
||||
List<ROIItem> list = new ArrayList<>();
|
||||
for (Element e : itemElements) {
|
||||
ROIItem item = new ROIItem();
|
||||
item.fromXml(e);
|
||||
list.add(item);
|
||||
}
|
||||
setItem(list);
|
||||
}
|
||||
setBackGroundQP(XmlUtil.getInteger(element, "BackGroundQP"));
|
||||
setBackGroundSkipFlag(XmlUtil.getInteger(element, "BackGroundSkipFlag"));
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ROIItem {
|
||||
@Schema(description = "感兴趣区域编号,取值范围1~16")
|
||||
private Integer ROISeq;
|
||||
|
||||
@Schema(description = "感兴趣区域左上角坐标,取值范围0~19683")
|
||||
private Integer TopLeft;
|
||||
|
||||
@Schema(description = "感兴趣区域右下角坐标,取值范围0~19683")
|
||||
private Integer BottomRight;
|
||||
|
||||
@Schema(description = "ROI区域编码质量等级,0:一般,1:较好,2:好,3:很好")
|
||||
private Integer ROIQP;
|
||||
|
||||
public void fromXml(Element element) {
|
||||
setROISeq(XmlUtil.getInteger(element, "ROISeq"));
|
||||
setTopLeft(XmlUtil.getInteger(element, "TopLeft"));
|
||||
setBottomRight(XmlUtil.getInteger(element, "BottomRight"));
|
||||
setROIQP(XmlUtil.getInteger(element, "ROIQP"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SVCParam {
|
||||
@Schema(description = "空域编码方式,0:基本层,1:1级增强,2:2级增强,3:3级增强")
|
||||
private Integer SVCSpaceDomainMode;
|
||||
|
||||
@Schema(description = "时域编码方式,0:基本层,1:1级增强,2:2级增强,3:3级增强")
|
||||
private Integer SVCTimeDomainMode;
|
||||
|
||||
@Schema(description = "空域编码能力,0:不支持,1:1级增强,2:2级增强,3:3级增强")
|
||||
private Integer SVCSpaceSupportMode;
|
||||
|
||||
@Schema(description = "时域编码能力,0:不支持,1:1级增强,2:2级增强,3:3级增强")
|
||||
private Integer SVCTimeSupportMode;
|
||||
|
||||
public void fromXml(Element element) {
|
||||
setSVCSpaceDomainMode(XmlUtil.getInteger(element, "SVCSpaceDomainMode"));
|
||||
setSVCTimeDomainMode(XmlUtil.getInteger(element, "SVCTimeDomainMode"));
|
||||
setSVCSpaceSupportMode(XmlUtil.getInteger(element, "SVCSpaceSupportMode"));
|
||||
setSVCTimeSupportMode(XmlUtil.getInteger(element, "SVCTimeSupportMode"));
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SurveillanceParam {
|
||||
@Schema(description = "绝对时间信息开关,0:关闭,1:打开")
|
||||
private Integer TimeFlag;
|
||||
|
||||
@Schema(description = "监控事件信息开关,0:关闭,1:打开")
|
||||
private Integer EventFlag;
|
||||
|
||||
@Schema(description = "报警信息开关,0:关闭,1:打开")
|
||||
private Integer AlertFlag;
|
||||
|
||||
public void fromXml(Element element) {
|
||||
setTimeFlag(XmlUtil.getInteger(element, "TimeFlag"));
|
||||
setEventFlag(XmlUtil.getInteger(element, "EventFlag"));
|
||||
setAlertFlag(XmlUtil.getInteger(element, "AlertFlag"));
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class AudioParam {
|
||||
@Schema(description = "声音识别特征参数开关,0:关闭,1:打开")
|
||||
private Integer AudioRecognitionFlag;
|
||||
|
||||
public void fromXml(Element element) {
|
||||
setAudioRecognitionFlag(XmlUtil.getInteger(element, "AudioRecognitionFlag"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.dom4j.Element;
|
||||
|
||||
@Data
|
||||
@Schema(description = "视频参数范围")
|
||||
public class VideoParamOpt implements DeviceConfigAware {
|
||||
|
||||
@Schema(description = "下载倍速范围,各可选参数以 '/' 分隔")
|
||||
private String downloadSpeed;
|
||||
|
||||
@Schema(description = "摄像机支持的分辨率,多个分辨率值以 '/' 分隔")
|
||||
private String resolution;
|
||||
|
||||
@Override
|
||||
public String configType() {
|
||||
return "VideoParamOpt";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromXml(Element element) {
|
||||
setDownloadSpeed(XmlUtil.getText(element, "DownloadSpeed"));
|
||||
setResolution(XmlUtil.getText(element, "Resolution"));
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,7 @@
|
||||
/**
|
||||
* 设备设置命令API接口
|
||||
*
|
||||
* @author lawrencehj
|
||||
* @date 2021年2月2日
|
||||
*/
|
||||
|
||||
package com.genersoft.iot.vmp.gb28181.controller;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.BasicParam;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
@ -32,60 +24,106 @@ public class DeviceConfig {
|
||||
@Autowired
|
||||
private IDeviceService deviceService;
|
||||
|
||||
@GetMapping("/basicParam")
|
||||
@Operation(summary = "基本配置设置命令", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "basicParam", description = "基础配置参数", required = true)
|
||||
public DeferredResult<WVPResult<String>> homePositionApi(BasicParam basicParam) {
|
||||
@GetMapping("/set/basicParam")
|
||||
@Operation(summary = "设置基本配置", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "basicParam", description = "基础配置参数", required = true)
|
||||
public DeferredResult<WVPResult<String>> homePositionApi(BasicParam basicParam) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("基本配置设置命令API调用");
|
||||
}
|
||||
Assert.notNull(basicParam.getDeviceId(), "设备ID必须存在");
|
||||
log.debug("基本配置设置命令API调用");
|
||||
}
|
||||
Assert.notNull(basicParam.getDeviceId(), "设备ID必须存在");
|
||||
|
||||
Device device = deviceService.getDeviceByDeviceId(basicParam.getDeviceId());
|
||||
Assert.notNull(device, "设备不存在");
|
||||
Device device = deviceService.getDeviceByDeviceId(basicParam.getDeviceId());
|
||||
Assert.notNull(device, "设备不存在");
|
||||
|
||||
DeferredResult<WVPResult<String>> deferredResult = new DeferredResult<>();
|
||||
deviceService.deviceBasicConfig(device, basicParam, (code, msg, data) -> {
|
||||
deferredResult.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
DeferredResult<WVPResult<String>> deferredResult = new DeferredResult<>();
|
||||
deviceService.deviceBasicConfig(device, basicParam, (code, msg, data) -> {
|
||||
deferredResult.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
|
||||
deferredResult.onTimeout(() -> {
|
||||
log.warn("[设备配置] 超时, {}", device.getDeviceId());
|
||||
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
|
||||
});
|
||||
return deferredResult;
|
||||
deferredResult.onTimeout(() -> {
|
||||
log.warn("[设备配置] 超时, {}", device.getDeviceId());
|
||||
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
|
||||
});
|
||||
return deferredResult;
|
||||
}
|
||||
|
||||
}
|
||||
@Operation(summary = "查询基本参数配置", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
|
||||
@Parameter(name = "channelId", description = "通道国标编号")
|
||||
@GetMapping("/query/basicParam")
|
||||
public DeferredResult<WVPResult<BasicParam>> queryBasicParam(String deviceId,
|
||||
@RequestParam(required = false) String channelId) {
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
Assert.notNull(device, "设备不存在");
|
||||
DeferredResult<WVPResult<BasicParam>> deferredResult = new DeferredResult<>();
|
||||
deviceService.deviceConfigQuery(device, channelId, BasicParam.class, (code, msg, data) -> {
|
||||
data.setDeviceId(deviceId);
|
||||
data.setChannelId(channelId);
|
||||
deferredResult.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
deferredResult.onTimeout(() -> {
|
||||
log.warn("[获取设备配置] 超时, {}", device.getDeviceId());
|
||||
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
|
||||
});
|
||||
return deferredResult;
|
||||
}
|
||||
|
||||
@Operation(summary = "设备配置查询", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
|
||||
@Parameter(name = "channelId", description = "通道国标编号", required = true)
|
||||
@Parameter(name = "configType", description = "配置类型, 可选值," +
|
||||
"基本参数配置:BasicParam," +
|
||||
"视频参数范围:VideoParamOpt, " +
|
||||
"SVAC编码配置:SVACEncodeConfig, " +
|
||||
"SVAC解码配置:SVACDecodeConfig。" +
|
||||
"可同时查询多个配置类型,各类型以“/”分隔,")
|
||||
@GetMapping("/query")
|
||||
public DeferredResult<WVPResult<Object>> configDownloadApi(String deviceId,String configType,
|
||||
@RequestParam(required = false) String channelId) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("设备配置查询请求API调用");
|
||||
}
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
Assert.notNull(device, "设备不存在");
|
||||
@Operation(summary = "查询视频参数范围", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
|
||||
@Parameter(name = "channelId", description = "通道国标编号")
|
||||
@GetMapping("/query/videoParamOpt")
|
||||
public DeferredResult<WVPResult<VideoParamOpt>> queryVideoParamOpt(String deviceId,
|
||||
@RequestParam(required = false) String channelId) {
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
Assert.notNull(device, "设备不存在");
|
||||
DeferredResult<WVPResult<VideoParamOpt>> deferredResult = new DeferredResult<>();
|
||||
deviceService.deviceConfigQuery(device, channelId, VideoParamOpt.class, (code, msg, data) -> {
|
||||
deferredResult.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
deferredResult.onTimeout(() -> {
|
||||
log.warn("[获取设备配置] 超时, {}", device.getDeviceId());
|
||||
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
|
||||
});
|
||||
return deferredResult;
|
||||
}
|
||||
|
||||
DeferredResult<WVPResult<Object>> deferredResult = new DeferredResult<>();
|
||||
@Operation(summary = "查询SVAC编码配置", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
|
||||
@Parameter(name = "channelId", description = "通道国标编号")
|
||||
@GetMapping("/query/svacEncodeConfig")
|
||||
public DeferredResult<WVPResult<SVACEncodeConfig>> querySVACEncodeConfig(String deviceId,
|
||||
@RequestParam(required = false) String channelId) {
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
Assert.notNull(device, "设备不存在");
|
||||
DeferredResult<WVPResult<SVACEncodeConfig>> deferredResult = new DeferredResult<>();
|
||||
deviceService.deviceConfigQuery(device, channelId, SVACEncodeConfig.class, (code, msg, data) -> {
|
||||
deferredResult.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
deferredResult.onTimeout(() -> {
|
||||
log.warn("[获取设备配置] 超时, {}", device.getDeviceId());
|
||||
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
|
||||
});
|
||||
return deferredResult;
|
||||
}
|
||||
|
||||
deviceService.deviceConfigQuery(device, channelId, configType, (code, msg, data) -> {
|
||||
deferredResult.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
|
||||
deferredResult.onTimeout(() -> {
|
||||
log.warn("[获取设备配置] 超时, {}", device.getDeviceId());
|
||||
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
|
||||
});
|
||||
return deferredResult;
|
||||
}
|
||||
@Operation(summary = "查询SVAC解码配置", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
|
||||
@Parameter(name = "channelId", description = "通道国标编号")
|
||||
@GetMapping("/query/svacDecodeConfig")
|
||||
public DeferredResult<WVPResult<SVACDecodeConfig>> querySVACDecodeConfig(String deviceId,
|
||||
@RequestParam(required = false) String channelId) {
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
Assert.notNull(device, "设备不存在");
|
||||
DeferredResult<WVPResult<SVACDecodeConfig>> deferredResult = new DeferredResult<>();
|
||||
deviceService.deviceConfigQuery(device, channelId, SVACDecodeConfig.class, (code, msg, data) -> {
|
||||
deferredResult.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
deferredResult.onTimeout(() -> {
|
||||
log.warn("[获取设备配置] 超时, {}", device.getDeviceId());
|
||||
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
|
||||
});
|
||||
return deferredResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ public interface IDeviceService {
|
||||
|
||||
void deviceBasicConfig(Device device, BasicParam basicParam, ErrorCallback<String> callback);
|
||||
|
||||
void deviceConfigQuery(Device device, String channelId, String configType, ErrorCallback<Object> callback);
|
||||
<T extends DeviceConfigAware> void deviceConfigQuery(Device device, String channelId, Class<T> configClass, ErrorCallback<T> callback);
|
||||
|
||||
void teleboot(Device device);
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.gb28181.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.CommonCallback;
|
||||
import com.genersoft.iot.vmp.common.enums.ChannelDataType;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
@ -331,7 +332,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
redisCatchStorage.updateDevice(device);
|
||||
try {
|
||||
commander.deviceInfoQuery(device, null);
|
||||
commander.deviceConfigQuery(device, null, "BasicParam", null);
|
||||
commander.deviceConfigQuery(device, null, BasicParam.class, null);
|
||||
} catch (InvalidArgumentException | SipException | ParseException e) {
|
||||
log.error("[命令发送失败] 查询设备信息: {}", e.getMessage());
|
||||
}
|
||||
@ -500,37 +501,37 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
|
||||
private void catalogSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) {
|
||||
log.info("[目录订阅] 到期, 编号: {}", deviceId);
|
||||
Device device = getDeviceByDeviceId(deviceId);
|
||||
if (device == null) {
|
||||
log.info("[目录订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId);
|
||||
return;
|
||||
}
|
||||
if (device.isOnLine() && device.getSubscribeCycleForCatalog() > 0) {
|
||||
log.info("[目录订阅] 到期, 编号: {}", deviceId);
|
||||
addCatalogSubscribe(device, transactionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void mobilPositionSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) {
|
||||
log.info("[移动位置订阅] 到期, 编号: {}", deviceId);
|
||||
Device device = getDeviceByDeviceId(deviceId);
|
||||
if (device == null) {
|
||||
log.info("[移动位置订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId);
|
||||
return;
|
||||
}
|
||||
if (device.isOnLine() && device.getSubscribeCycleForMobilePosition() > 0) {
|
||||
log.info("[移动位置订阅] 到期, 编号: {}", deviceId);
|
||||
addMobilePositionSubscribe(device, transactionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void alarmSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) {
|
||||
log.info("[报警订阅] 到期, 编号: {}", deviceId);
|
||||
Device device = getDeviceByDeviceId(deviceId);
|
||||
if (device == null) {
|
||||
log.info("[移报警订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId);
|
||||
return;
|
||||
}
|
||||
if (device.isOnLine() && device.getSubscribeCycleForAlarm() > 0) {
|
||||
log.info("[报警订阅] 到期, 编号: {}", deviceId);
|
||||
addAlarmSubscribe(device, transactionInfo);
|
||||
}
|
||||
}
|
||||
@ -1160,16 +1161,21 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deviceConfigQuery(Device device, String channelId, String configType, ErrorCallback<Object> callback) {
|
||||
public <T extends DeviceConfigAware> void deviceConfigQuery(Device device, String channelId, Class<T> configClass, ErrorCallback<T> callback) {
|
||||
|
||||
if (!userSetting.getServerId().equals(device.getServerId())) {
|
||||
WVPResult<String> result = redisRpcService.deviceConfigQuery(device.getServerId(), device, channelId, configType);
|
||||
callback.run(result.getCode(), result.getMsg(), result.getData());
|
||||
WVPResult<Object> result = redisRpcService.deviceConfigQuery(device.getServerId(), device, channelId, configClass.getName());
|
||||
if (result.getData() instanceof JSONObject) {
|
||||
T obj = ((JSONObject) result.getData()).toJavaObject(configClass);
|
||||
callback.run(result.getCode(), result.getMsg(), obj);
|
||||
} else {
|
||||
callback.run(result.getCode(), result.getMsg(), null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sipCommander.deviceConfigQuery(device, channelId, configType, callback);
|
||||
sipCommander.deviceConfigQuery(device, channelId, configClass, callback);
|
||||
} catch (InvalidArgumentException | SipException | ParseException e) {
|
||||
log.error("[命令发送失败] 获取设备配置: {}", e.getMessage());
|
||||
callback.run(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage(), null);
|
||||
|
||||
@ -248,9 +248,9 @@ public interface ISIPCommander {
|
||||
*
|
||||
* @param device 视频设备
|
||||
* @param channelId 通道编码(可选)
|
||||
* @param configType 配置类型:
|
||||
* @param configClass 配置类型:
|
||||
*/
|
||||
void deviceConfigQuery(Device device, String channelId, String configType, ErrorCallback<Object> callback) throws InvalidArgumentException, SipException, ParseException;
|
||||
<T extends DeviceConfigAware> void deviceConfigQuery(Device device, String channelId, Class<T> configClass, ErrorCallback<T> callback) throws InvalidArgumentException, SipException, ParseException;
|
||||
|
||||
/**
|
||||
* 查询设备预置位置
|
||||
|
||||
@ -29,6 +29,7 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import gov.nist.javax.sip.message.SIPResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dom4j.Element;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -846,11 +847,13 @@ public class SIPCommander implements ISIPCommander {
|
||||
cmdXml.append("<Control>\r\n");
|
||||
cmdXml.append("<CmdType>" + cmdType + "</CmdType>\r\n");
|
||||
cmdXml.append("<SN>" + sn + "</SN>\r\n");
|
||||
String channelId = basicParam.getChannelId();
|
||||
if (ObjectUtils.isEmpty(channelId)) {
|
||||
channelId = device.getDeviceId();
|
||||
}
|
||||
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
|
||||
// String channelId = basicParam.getChannelId();
|
||||
// if (ObjectUtils.isEmpty(channelId)) {
|
||||
// channelId = device.getDeviceId();
|
||||
// }
|
||||
// 此处经过详细测试 对于这里使用通道ID还是设备ID,海康两者都支持 大华必须是设备ID,宇视都支持,但是不回复消息直接自己就注销重启,
|
||||
// 所以这里取值 device.getDeviceId()
|
||||
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
|
||||
cmdXml.append("<BasicParam>\r\n");
|
||||
if (!ObjectUtils.isEmpty(basicParam.getName())) {
|
||||
cmdXml.append("<Name>" + basicParam.getName() + "</Name>\r\n");
|
||||
@ -870,7 +873,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
cmdXml.append("</Control>\r\n");
|
||||
|
||||
|
||||
MessageEvent<String> messageEvent = MessageEvent.getInstance(cmdType, sn + "", channelId, 1000L, callback);
|
||||
MessageEvent<String> messageEvent = MessageEvent.getInstance(cmdType, sn + "", device.getDeviceId(), 2000L, callback);
|
||||
messageSubscribe.addSubscribe(messageEvent);
|
||||
|
||||
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
|
||||
@ -1073,12 +1076,12 @@ public class SIPCommander implements ISIPCommander {
|
||||
/**
|
||||
* 查询设备配置
|
||||
*
|
||||
* @param device 视频设备
|
||||
* @param channelId 通道编码(可选)
|
||||
* @param configType 配置类型:
|
||||
* @param device 视频设备
|
||||
* @param channelId 通道编码(可选)
|
||||
* @param configClass 配置类型
|
||||
*/
|
||||
@Override
|
||||
public void deviceConfigQuery(Device device, String channelId, String configType, ErrorCallback<Object> callback) throws InvalidArgumentException, SipException, ParseException {
|
||||
public <T extends DeviceConfigAware> void deviceConfigQuery(Device device, String channelId, Class<T> configClass, ErrorCallback<T> callback) throws InvalidArgumentException, SipException, ParseException {
|
||||
|
||||
String cmdType = "ConfigDownload";
|
||||
int sn = (int) ((Math.random() * 9 + 1) * 100000);
|
||||
@ -1093,10 +1096,42 @@ public class SIPCommander implements ISIPCommander {
|
||||
} else {
|
||||
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
|
||||
}
|
||||
|
||||
T sample;
|
||||
try {
|
||||
sample = configClass.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
log.error("[命令发送失败] 实例化配置类: {}", e.getMessage());
|
||||
if (callback != null) {
|
||||
callback.run(ErrorCode.ERROR100.getCode(), "实例化失败: " + e.getMessage(), null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
String configType = sample.configType();
|
||||
cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
|
||||
cmdXml.append("</Query>\r\n");
|
||||
|
||||
MessageEvent<Object> messageEvent = MessageEvent.getInstance(cmdType, sn + "", channelId, 1000L, callback);
|
||||
ErrorCallback<Object> wrappedCallback = (code, msg, data) -> {
|
||||
if (callback != null) {
|
||||
if (code == ErrorCode.SUCCESS.getCode() && data instanceof Element) {
|
||||
Element responseElement = (Element) data;
|
||||
Element configElement = responseElement.element(configType);
|
||||
if (configElement != null) {
|
||||
try {
|
||||
T result = configClass.getDeclaredConstructor().newInstance();
|
||||
result.fromXml(configElement);
|
||||
callback.run(code, msg, result);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
log.error("[设备配置查询] 创建实例失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
callback.run(code, msg, null);
|
||||
}
|
||||
};
|
||||
|
||||
MessageEvent<Object> messageEvent = MessageEvent.getInstance(cmdType, sn + "", channelId, 1000L, wrappedCallback);
|
||||
messageSubscribe.addSubscribe(messageEvent);
|
||||
|
||||
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
|
||||
|
||||
@ -31,6 +31,7 @@ import javax.sip.RequestEvent;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.header.AuthorizationHeader;
|
||||
import javax.sip.header.ContactHeader;
|
||||
import javax.sip.header.ExpiresHeader;
|
||||
import javax.sip.header.FromHeader;
|
||||
import javax.sip.header.ViaHeader;
|
||||
import javax.sip.message.Request;
|
||||
@ -75,177 +76,45 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
sipProcessorObserver.addRequestProcessor(method, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到注册请求 处理
|
||||
*/
|
||||
@Override
|
||||
public void process(RequestEvent evt) {
|
||||
try {
|
||||
SIPRequest request = (SIPRequest) evt.getRequest();
|
||||
Response response = null;
|
||||
boolean passwordCorrect = false;
|
||||
// 注册标志
|
||||
boolean registerFlag = request.getExpires().getExpires() != 0;
|
||||
// 注销成功
|
||||
|
||||
FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
|
||||
AddressImpl address = (AddressImpl) fromHeader.getAddress();
|
||||
SipUri uri = (SipUri) address.getURI();
|
||||
String deviceId = uri.getUser();
|
||||
|
||||
if (userSetting.isDeviceIdStrict()) {
|
||||
// 严格模式下,非20位设备ID不予处理
|
||||
GbCode decode = GbCode.decode(deviceId);
|
||||
if (decode == null) {
|
||||
// 注册失败
|
||||
response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
|
||||
Response response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 调整逻辑,如果为设置公共密码,那么就必须要预设用户信息,否则无法注册。
|
||||
|
||||
ExpiresHeader expiresHeader = request.getExpires();
|
||||
if (expiresHeader == null) {
|
||||
Response response = getMessageFactory().createResponse(Response.BAD_REQUEST, request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean registerFlag = expiresHeader.getExpires() != 0;
|
||||
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
|
||||
RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
|
||||
userSetting.getSipUseSourceIpAsRemoteAddress());
|
||||
String requestAddress = remoteAddressInfo.getIp() + ":" + remoteAddressInfo.getPort();
|
||||
String title = registerFlag ? "[注册请求]" : "[注销请求]";
|
||||
log.info("{} 设备:{}, 开始处理: {}", title, deviceId, requestAddress);
|
||||
String password = null;
|
||||
if (device != null) {
|
||||
if (device.getSipTransactionInfo() != null &&
|
||||
request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
|
||||
log.info("{} 设备:{}, 注册续订: {}", title, device.getDeviceId(), device.getDeviceId());
|
||||
if (registerFlag) {
|
||||
device.setExpires(request.getExpires().getExpires());
|
||||
device.setIp(remoteAddressInfo.getIp());
|
||||
device.setPort(remoteAddressInfo.getPort());
|
||||
device.setHostAddress(IpPortUtil.concatenateIpAndPort(remoteAddressInfo.getIp(), String.valueOf(remoteAddressInfo.getPort())));
|
||||
|
||||
device.setLocalIp(request.getLocalAddress().getHostAddress());
|
||||
Response registerOkResponse = getRegisterOkResponse(request);
|
||||
// 判断TCP还是UDP
|
||||
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
|
||||
String transport = reqViaHeader.getTransport();
|
||||
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
|
||||
device.setRegisterTimeStamp(System.currentTimeMillis());
|
||||
deviceService.online(device);
|
||||
} else {
|
||||
deviceService.offline(device);
|
||||
}
|
||||
return;
|
||||
}else {
|
||||
// 正常注册, 用户信息未设置密码,并且公共密码也未设置,则关闭鉴权
|
||||
if (!ObjectUtils.isEmpty(device.getPassword()) || !ObjectUtils.isEmpty(sipConfig.getPassword())) {
|
||||
password = (!ObjectUtils.isEmpty(device.getPassword())) ? device.getPassword() : sipConfig.getPassword();
|
||||
}
|
||||
// 如果设置了一个无密码的设备,那么这里就会自动跳动,后续会直接注册成功
|
||||
}
|
||||
}else {
|
||||
if (ObjectUtils.isEmpty(sipConfig.getPassword())) {
|
||||
log.info("{} 设备:{}, 地址: {}, 公共密码已经禁用,请添加用户信息后注册", title, deviceId, requestAddress);
|
||||
response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}else {
|
||||
password = sipConfig.getPassword();
|
||||
}
|
||||
}
|
||||
|
||||
AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
|
||||
if (authHead == null && !ObjectUtils.isEmpty(password)) {
|
||||
log.info(title + " 设备:{}, 回复401: {}", deviceId, requestAddress);
|
||||
response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
|
||||
new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验密码是否正确
|
||||
passwordCorrect = ObjectUtils.isEmpty(password) ||
|
||||
new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, password);
|
||||
|
||||
if (!passwordCorrect) {
|
||||
// 注册失败
|
||||
response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
|
||||
response.setReasonPhrase("wrong password");
|
||||
log.info("{} 设备:{}, 密码/SIP服务器ID错误, 回复403: {}", title, deviceId, requestAddress);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 携带授权头并且密码正确
|
||||
response = getMessageFactory().createResponse(Response.OK, request);
|
||||
// 如果主动禁用了Date头,则不添加
|
||||
if (!userSetting.isDisableDateHeader()) {
|
||||
// 添加 date头
|
||||
SIPDateHeader dateHeader = new SIPDateHeader();
|
||||
// 使用自己修改的
|
||||
GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
|
||||
dateHeader.setDate(gbSipDate);
|
||||
response.addHeader(dateHeader);
|
||||
}
|
||||
|
||||
if (request.getExpires() == null) {
|
||||
response = getMessageFactory().createResponse(Response.BAD_REQUEST, request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
// 添加 Contact头
|
||||
response.addHeader(request.getHeader(ContactHeader.NAME));
|
||||
// 添加 Expires头
|
||||
response.addHeader(request.getExpires());
|
||||
|
||||
if (device == null) {
|
||||
device = new Device();
|
||||
device.setStreamMode("TCP-PASSIVE");
|
||||
device.setCharset("GB2312");
|
||||
device.setGeoCoordSys("WGS84");
|
||||
device.setMediaServerId("auto");
|
||||
device.setDeviceId(deviceId);
|
||||
device.setOnLine(false);
|
||||
} else {
|
||||
if (ObjectUtils.isEmpty(device.getStreamMode())) {
|
||||
device.setStreamMode("TCP-PASSIVE");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(device.getCharset())) {
|
||||
device.setCharset("GB2312");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(device.getGeoCoordSys())) {
|
||||
device.setGeoCoordSys("WGS84");
|
||||
}
|
||||
}
|
||||
device.setServerId(userSetting.getServerId());
|
||||
device.setIp(remoteAddressInfo.getIp());
|
||||
device.setPort(remoteAddressInfo.getPort());
|
||||
device.setHostAddress(IpPortUtil.concatenateIpAndPort(remoteAddressInfo.getIp(), String.valueOf(remoteAddressInfo.getPort())));
|
||||
device.setLocalIp(request.getLocalAddress().getHostAddress());
|
||||
if (request.getExpires().getExpires() == 0) {
|
||||
// 注销成功
|
||||
registerFlag = false;
|
||||
} else {
|
||||
// 注册成功
|
||||
device.setExpires(request.getExpires().getExpires());
|
||||
registerFlag = true;
|
||||
// 判断 TCP/UDP
|
||||
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
|
||||
String transport = reqViaHeader.getTransport();
|
||||
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
|
||||
}
|
||||
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
// 注册成功
|
||||
device.setRegisterTimeStamp(System.currentTimeMillis());
|
||||
// 保存到 redis
|
||||
if (registerFlag) {
|
||||
log.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress);
|
||||
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse) response);
|
||||
device.setSipTransactionInfo(sipTransactionInfo);
|
||||
deviceService.online(device);
|
||||
registerHandler(device, request, remoteAddressInfo, deviceId, requestAddress);
|
||||
} else {
|
||||
log.info("[注销成功] deviceId: {}->{}", deviceId, requestAddress);
|
||||
deviceService.offline(device);
|
||||
cancellationHandler(device, request, remoteAddressInfo, deviceId, requestAddress);
|
||||
}
|
||||
redisCatchStorage.updateDeviceRegisterTimeStamp(List.of(device));
|
||||
} catch (SipException | NoSuchAlgorithmException | ParseException e) {
|
||||
log.error("未处理的异常 ", e);
|
||||
}
|
||||
@ -273,4 +142,149 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
|
||||
}
|
||||
|
||||
private void registerHandler(Device device, SIPRequest request, RemoteAddressInfo remoteAddressInfo,
|
||||
String deviceId, String requestAddress) throws SipException, NoSuchAlgorithmException, ParseException {
|
||||
if (device != null && device.getSipTransactionInfo() != null &&
|
||||
request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
|
||||
log.info("[注册续订] 设备:{}", device.getDeviceId());
|
||||
device.setExpires(request.getExpires().getExpires());
|
||||
device.setIp(remoteAddressInfo.getIp());
|
||||
device.setPort(remoteAddressInfo.getPort());
|
||||
device.setHostAddress(IpPortUtil.concatenateIpAndPort(remoteAddressInfo.getIp(), String.valueOf(remoteAddressInfo.getPort())));
|
||||
device.setLocalIp(request.getLocalAddress().getHostAddress());
|
||||
|
||||
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
|
||||
String transport = reqViaHeader.getTransport();
|
||||
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
|
||||
|
||||
Response okResponse = getRegisterOkResponse(request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), okResponse);
|
||||
device.setRegisterTimeStamp(System.currentTimeMillis());
|
||||
deviceService.online(device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device == null && ObjectUtils.isEmpty(sipConfig.getPassword())) {
|
||||
log.info("[注册请求] 设备:{}, 地址: {}, 公共密码已经禁用,请添加用户信息后注册", deviceId, requestAddress);
|
||||
Response response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
String password = device != null && !ObjectUtils.isEmpty(device.getPassword()) ? device.getPassword() : sipConfig.getPassword();
|
||||
|
||||
AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
|
||||
if (!ObjectUtils.isEmpty(password) && authHead == null) {
|
||||
log.info("[注册请求] 设备:{}, 回复401: {}", deviceId, requestAddress);
|
||||
Response response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
|
||||
new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(password) && !new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, password)) {
|
||||
log.info("[注册请求] 设备:{}, 密码/SIP服务器ID错误, 回复403: {}", deviceId, requestAddress);
|
||||
Response response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
|
||||
response.setReasonPhrase("wrong password");
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
|
||||
Response response = getMessageFactory().createResponse(Response.OK, request);
|
||||
if (!userSetting.isDisableDateHeader()) {
|
||||
SIPDateHeader dateHeader = new SIPDateHeader();
|
||||
GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
|
||||
dateHeader.setDate(gbSipDate);
|
||||
response.addHeader(dateHeader);
|
||||
}
|
||||
response.addHeader(request.getHeader(ContactHeader.NAME));
|
||||
response.addHeader(request.getExpires());
|
||||
|
||||
if (device == null) {
|
||||
device = new Device();
|
||||
device.setStreamMode("TCP-PASSIVE");
|
||||
device.setCharset("GB2312");
|
||||
device.setGeoCoordSys("WGS84");
|
||||
device.setMediaServerId("auto");
|
||||
device.setDeviceId(deviceId);
|
||||
device.setOnLine(false);
|
||||
} else {
|
||||
if (ObjectUtils.isEmpty(device.getStreamMode())) {
|
||||
device.setStreamMode("TCP-PASSIVE");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(device.getCharset())) {
|
||||
device.setCharset("GB2312");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(device.getGeoCoordSys())) {
|
||||
device.setGeoCoordSys("WGS84");
|
||||
}
|
||||
}
|
||||
device.setServerId(userSetting.getServerId());
|
||||
device.setIp(remoteAddressInfo.getIp());
|
||||
device.setPort(remoteAddressInfo.getPort());
|
||||
device.setHostAddress(IpPortUtil.concatenateIpAndPort(remoteAddressInfo.getIp(), String.valueOf(remoteAddressInfo.getPort())));
|
||||
device.setLocalIp(request.getLocalAddress().getHostAddress());
|
||||
device.setExpires(request.getExpires().getExpires());
|
||||
|
||||
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
|
||||
String transport = reqViaHeader.getTransport();
|
||||
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
|
||||
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
|
||||
device.setRegisterTimeStamp(System.currentTimeMillis());
|
||||
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse) response);
|
||||
device.setSipTransactionInfo(sipTransactionInfo);
|
||||
deviceService.online(device);
|
||||
redisCatchStorage.updateDeviceRegisterTimeStamp(List.of(device));
|
||||
|
||||
log.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress);
|
||||
}
|
||||
|
||||
private void cancellationHandler(Device device, SIPRequest request, RemoteAddressInfo remoteAddressInfo,
|
||||
String deviceId, String requestAddress) throws SipException, NoSuchAlgorithmException, ParseException {
|
||||
if (device != null && device.getSipTransactionInfo() != null &&
|
||||
request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
|
||||
Response response = getRegisterOkResponse(request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
deviceService.offline(device);
|
||||
log.info("[注销成功] deviceId: {}->{}", deviceId, requestAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device == null && ObjectUtils.isEmpty(sipConfig.getPassword())) {
|
||||
log.info("[注销请求] 设备:{}, 地址: {}, 公共密码已经禁用,请添加用户信息后注销", deviceId, requestAddress);
|
||||
Response response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
String password = device != null && !ObjectUtils.isEmpty(device.getPassword()) ? device.getPassword() : sipConfig.getPassword();
|
||||
|
||||
AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
|
||||
if (!ObjectUtils.isEmpty(password) && authHead == null) {
|
||||
log.info("[注销请求] 设备:{}, 回复401: {}", deviceId, requestAddress);
|
||||
Response response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
|
||||
new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(password) && !new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, password)) {
|
||||
log.info("[注销请求] 设备:{}, 密码/SIP服务器ID错误, 回复403: {}", deviceId, requestAddress);
|
||||
Response response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
|
||||
response.setReasonPhrase("wrong password");
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
return;
|
||||
}
|
||||
|
||||
Response response = getRegisterOkResponse(request);
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
|
||||
if (device != null) {
|
||||
deviceService.offline(device);
|
||||
redisCatchStorage.updateDeviceRegisterTimeStamp(List.of(device));
|
||||
}
|
||||
|
||||
log.info("[注销成功] deviceId: {}->{}", deviceId, requestAddress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -119,6 +119,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
|
||||
}else { // 由于上面已经判断都为null则直接返回,所以这里device和parentPlatform必有一个不为null
|
||||
messageHandler.handForPlatform(evt, parentPlatform, rootElement);
|
||||
}
|
||||
responseAck(request, Response.OK);
|
||||
}else {
|
||||
// 不支持的message
|
||||
// 不存在则回复415
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Platform;
|
||||
import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
|
||||
@ -31,9 +29,6 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar
|
||||
@Autowired
|
||||
private ResponseMessageHandler responseMessageHandler;
|
||||
|
||||
@Autowired
|
||||
private DeferredResultHolder deferredResultHolder;
|
||||
|
||||
@Autowired
|
||||
private IDeviceService deviceService;
|
||||
|
||||
@ -42,54 +37,29 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar
|
||||
responseMessageHandler.addHandler(cmdType, this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handForDevice(RequestEvent evt, Device device, Element element) {
|
||||
try {
|
||||
// 回复200 OK
|
||||
responseAck((SIPRequest) evt.getRequest(), Response.OK);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
log.error("[命令发送失败] 设备配置查询: {}", e.getMessage());
|
||||
}
|
||||
// 此处是对本平台发出DeviceControl指令的应答
|
||||
JSONObject json = new JSONObject();
|
||||
XmlUtil.node2Json(element, json);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(json.toJSONString());
|
||||
}
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
if (json.get("BasicParam") != null) {
|
||||
jsonObject.put("BasicParam", json.getJSONObject("BasicParam"));
|
||||
}
|
||||
if (json.get("VideoParamOpt") != null) {
|
||||
jsonObject.put("VideoParamOpt", json.getJSONObject("VideoParamOpt"));
|
||||
}
|
||||
if (json.get("SVACEncodeConfig") != null) {
|
||||
jsonObject.put("SVACEncodeConfig", json.getJSONObject("SVACEncodeConfig"));
|
||||
}
|
||||
if (json.get("SVACDecodeConfig") != null) {
|
||||
jsonObject.put("SVACDecodeConfig", json.getJSONObject("SVACDecodeConfig"));
|
||||
}
|
||||
|
||||
responseMessageHandler.handMessageEvent(element, jsonObject);
|
||||
responseMessageHandler.handMessageEvent(element, element);
|
||||
|
||||
JSONObject basicParam = json.getJSONObject("BasicParam");
|
||||
Element basicParam = element.element("BasicParam");
|
||||
if (basicParam != null) {
|
||||
Integer heartBeatInterval = basicParam.getInteger("HeartBeatInterval");
|
||||
Integer heartBeatCount = basicParam.getInteger("HeartBeatCount");
|
||||
Integer positionCapability = basicParam.getInteger("PositionCapability");
|
||||
Integer heartBeatInterval = XmlUtil.getInteger(basicParam, "HeartBeatInterval");
|
||||
Integer heartBeatCount = XmlUtil.getInteger(basicParam, "HeartBeatCount");
|
||||
Integer positionCapability = XmlUtil.getInteger(basicParam, "PositionCapability");
|
||||
device.setHeartBeatInterval(heartBeatInterval);
|
||||
device.setHeartBeatCount(heartBeatCount);
|
||||
device.setPositionCapability(positionCapability);
|
||||
|
||||
deviceService.updateDeviceHeartInfo(device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) {
|
||||
// 不会收到上级平台的心跳信息
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ public interface IRedisRpcService {
|
||||
|
||||
WVPResult<String> deviceBasicConfig(String serverId, Device device, BasicParam basicParam);
|
||||
|
||||
WVPResult<String> deviceConfigQuery(String serverId, Device device, String channelId, String configType);
|
||||
WVPResult<Object> deviceConfigQuery(String serverId, Device device, String channelId, String configType);
|
||||
|
||||
void teleboot(String serverId, Device device);
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.BasicParam;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceConfigAware;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
|
||||
import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcController;
|
||||
@ -121,7 +122,7 @@ public class RedisRpcDeviceController extends RpcController {
|
||||
JSONObject paramJson = JSONObject.parseObject(request.getParam().toString());
|
||||
String deviceId = paramJson.getString("deviceId");
|
||||
String channelId = paramJson.getString("channelId");
|
||||
String configType = paramJson.getString("configType");
|
||||
String className = paramJson.getString("configType");
|
||||
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
|
||||
@ -131,12 +132,24 @@ public class RedisRpcDeviceController extends RpcController {
|
||||
response.setBody("param error");
|
||||
return response;
|
||||
}
|
||||
deviceService.deviceConfigQuery(device, channelId, configType, (code, msg, data) -> {
|
||||
response.setStatusCode(code);
|
||||
response.setBody(new WVPResult<>(code, msg, data));
|
||||
// 手动发送结果
|
||||
sendResponse(response);
|
||||
});
|
||||
try {
|
||||
Class<?> rawClass = Class.forName(className);
|
||||
if (!DeviceConfigAware.class.isAssignableFrom(rawClass)) {
|
||||
response.setStatusCode(ErrorCode.ERROR400.getCode());
|
||||
response.setBody("invalid config type: " + className);
|
||||
return response;
|
||||
}
|
||||
Class<? extends DeviceConfigAware> configClass = rawClass.asSubclass(DeviceConfigAware.class);
|
||||
deviceService.deviceConfigQuery(device, channelId, configClass, (code, msg, data) -> {
|
||||
response.setStatusCode(code);
|
||||
response.setBody(new WVPResult<>(code, msg, data));
|
||||
sendResponse(response);
|
||||
});
|
||||
} catch (ClassNotFoundException e) {
|
||||
response.setStatusCode(ErrorCode.ERROR400.getCode());
|
||||
response.setBody("unknown config type: " + className);
|
||||
return response;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -351,9 +351,9 @@ public class RedisRpcServiceImpl implements IRedisRpcService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WVPResult<String> deviceConfigQuery(String serverId, Device device, String channelId, String configType) {
|
||||
public WVPResult<Object> deviceConfigQuery(String serverId, Device device, String channelId, String configType) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("device", device.getDeviceId());
|
||||
jsonObject.put("deviceId", device.getDeviceId());
|
||||
jsonObject.put("channelId", channelId);
|
||||
jsonObject.put("configType", configType);
|
||||
RedisRpcRequest request = buildRequest("device/deviceConfigQuery", jsonObject);
|
||||
|
||||
@ -113,10 +113,19 @@ export function subscribeForAlarm(params) {
|
||||
})
|
||||
}
|
||||
|
||||
export function queryBasicParam(deviceId) {
|
||||
export function queryBasicParam(params) {
|
||||
return request({
|
||||
method: 'get',
|
||||
url: `/api/device/config/query/${deviceId}/BasicParam`
|
||||
url: '/api/device/config/query/basicParam',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function setBasicParam(params) {
|
||||
return request({
|
||||
method: 'get',
|
||||
url: '/api/device/config/set/basicParam',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
queryDeviceTree,
|
||||
queryHasStreamChannels,
|
||||
resetGuard,
|
||||
setBasicParam,
|
||||
setGuard,
|
||||
subscribeCatalog, subscribeForAlarm,
|
||||
subscribeMobilePosition,
|
||||
@ -124,9 +125,19 @@ const actions = {
|
||||
})
|
||||
})
|
||||
},
|
||||
queryBasicParam({ commit }, deviceId) {
|
||||
queryBasicParam({ commit }, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
queryBasicParam(deviceId).then(response => {
|
||||
queryBasicParam(params).then(response => {
|
||||
const { data } = response
|
||||
resolve(data)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
setBasicParam({ commit }, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setBasicParam(params).then(response => {
|
||||
const { data } = response
|
||||
resolve(data)
|
||||
}).catch(error => {
|
||||
|
||||
132
web/src/views/device/channel/basicPropertyConfig.vue
Normal file
132
web/src/views/device/channel/basicPropertyConfig.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div id="dhBasicPropertyPage">
|
||||
<div class="basic-property-body">
|
||||
<div class="config-section">
|
||||
<el-form ref="formRef" :model="form" label-width="140px">
|
||||
<el-form-item label="设备名称" prop="name">
|
||||
<el-input v-model="form.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="注册过期时间(秒)" prop="expiration">
|
||||
<el-input-number v-model="form.expiration" :min="60" :max="86400" />
|
||||
</el-form-item>
|
||||
<el-form-item label="心跳间隔(秒)" prop="heartBeatInterval">
|
||||
<el-input-number v-model="form.heartBeatInterval" :min="5" :max="3600" />
|
||||
</el-form-item>
|
||||
<el-form-item label="心跳超时次数" prop="heartBeatCount">
|
||||
<el-input-number v-model="form.heartBeatCount" :min="1" :max="60" />
|
||||
</el-form-item>
|
||||
<el-form-item label="定位功能" prop="positionCapability">
|
||||
<el-select v-model="form.positionCapability">
|
||||
<el-option :value="0" label="不支持" />
|
||||
<el-option :value="1" label="GPS定位" />
|
||||
<el-option :value="2" label="北斗定位" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="经度" v-if="form.longitude !== null">
|
||||
<span class="readonly-field">{{ form.longitude }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="纬度" v-if="form.latitude !== null">
|
||||
<span class="readonly-field">{{ form.latitude }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
||||
<el-button :loading="loading" @click="handleRefresh">刷新</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BasicPropertyPage',
|
||||
props: {
|
||||
deviceId: { type: String, default: null },
|
||||
channelDeviceId: { type: String, default: null }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
form: {
|
||||
deviceId: this.deviceId,
|
||||
channelId: this.channelDeviceId,
|
||||
name: '',
|
||||
expiration: 3600,
|
||||
heartBeatInterval: 60,
|
||||
heartBeatCount: 3,
|
||||
positionCapability: 0,
|
||||
longitude: null,
|
||||
latitude: null
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadConfig()
|
||||
},
|
||||
methods: {
|
||||
loadConfig() {
|
||||
this.loading = true
|
||||
this.$store.dispatch('device/queryBasicParam', {
|
||||
deviceId: this.deviceId,
|
||||
channelId: this.channelDeviceId
|
||||
})
|
||||
.then(data => {
|
||||
this.form = { ...this.form, ...data }
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleSave() {
|
||||
this.saving = true
|
||||
this.$store.dispatch('device/setBasicParam', this.form)
|
||||
.then(() => {
|
||||
this.$message({ showClose: true, message: '保存成功', type: 'success' })
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$message({ showClose: true, message: error, type: 'error' })
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving = false
|
||||
})
|
||||
},
|
||||
handleRefresh() {
|
||||
this.loadConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#dhBasicPropertyPage {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.basic-property-body {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
min-height: 30vw;
|
||||
flex: 1;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.config-section {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.config-section .el-input,
|
||||
.config-section .el-input-number,
|
||||
.config-section .el-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.readonly-field {
|
||||
color: #606266;
|
||||
line-height: 32px;
|
||||
}
|
||||
</style>
|
||||
93
web/src/views/device/channel/cameraConfig.vue
Normal file
93
web/src/views/device/channel/cameraConfig.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div id="dhCameraConfigPage">
|
||||
<el-page-header content="相机配置" @back="$emit('close')" />
|
||||
<div class="camera-config-body">
|
||||
<div class="config-sidebar">
|
||||
<el-menu :default-active="activeTab" @select="handleMenuSelect">
|
||||
<el-menu-item index="base">
|
||||
<i class="iconfont icon-wxbzhuye" style="margin-right: 10px" />
|
||||
<span>基础属性</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="image">
|
||||
<i class="iconfont icon-shishiyulan" style="margin-right: 10px" />
|
||||
<span>图像属性</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="encode">
|
||||
<i class="iconfont icon-shipin" style="margin-right: 10px" />
|
||||
<span>编码设置</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="alarm">
|
||||
<i class="iconfont icon-baojing" style="margin-right: 10px" />
|
||||
<span>报警配置</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="config-content">
|
||||
<basicPropertyConfig v-if="activeTab === 'base'" :device-id="deviceId" :channel-device-id="channelDeviceId" />
|
||||
<imagePropertyConfig v-if="activeTab === 'image'" :device-id="deviceId" :channel-device-id="channelDeviceId" />
|
||||
<div v-if="activeTab === 'osd'" class="placeholder-tab">OSD配置(开发中)</div>
|
||||
<div v-if="activeTab === 'encode'" class="placeholder-tab">编码设置(开发中)</div>
|
||||
<div v-if="activeTab === 'alarm'" class="placeholder-tab">报警配置(开发中)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import basicPropertyConfig from './basicPropertyConfig.vue'
|
||||
import imagePropertyConfig from './imagePropertyConfig.vue'
|
||||
|
||||
export default {
|
||||
name: 'CameraConfigPage',
|
||||
components: { basicPropertyConfig, imagePropertyConfig },
|
||||
props: {
|
||||
deviceId: { type: String, default: null },
|
||||
channelDeviceId: { type: String, default: null }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'base'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMenuSelect(index) {
|
||||
this.activeTab = index
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#dhCameraConfigPage {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.camera-config-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
padding-top: 16px;
|
||||
}
|
||||
.config-sidebar {
|
||||
width: 160px;
|
||||
flex: none;
|
||||
border-right: 1px solid #e6e6e6;
|
||||
}
|
||||
.config-sidebar .el-menu {
|
||||
border-right: none;
|
||||
}
|
||||
.config-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.placeholder-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 200px;
|
||||
color: #909399;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
84
web/src/views/device/channel/imagePropertyConfig.vue
Normal file
84
web/src/views/device/channel/imagePropertyConfig.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div id="dhImagePropertyPage">
|
||||
<div class="image-property-body">
|
||||
<div class="player-section">
|
||||
<playerPtzPanel :device-id="deviceId" :channel-device-id="channelDeviceId" />
|
||||
</div>
|
||||
<div class="config-section">
|
||||
<el-tabs v-model="activeTab" tab-position="left" class="config-tabs">
|
||||
<el-tab-pane label="OSD配置" name="osd">
|
||||
<div class="placeholder-tab">OSD配置(开发中)</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="视角" name="control">
|
||||
<div class="placeholder-tab">视角(开发中)</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="画面遮挡" name="pictureMask">
|
||||
<div class="placeholder-tab">画面遮挡(开发中)</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import playerPtzPanel from '../common/playerPtzPanel.vue'
|
||||
|
||||
export default {
|
||||
name: 'ImagePropertyPage',
|
||||
components: { playerPtzPanel },
|
||||
props: {
|
||||
deviceId: { type: String, default: null },
|
||||
channelDeviceId: { type: String, default: null }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'osd'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#dhImagePropertyPage {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.image-property-body {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
min-height: 30vw;
|
||||
flex: 1;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.player-section {
|
||||
width: 520px;
|
||||
flex: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.config-section {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.config-tabs {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.placeholder-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 200px;
|
||||
color: #909399;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="channelList" style="height: calc(100vh - 124px);">
|
||||
<div v-if="!editId && !ptzConfigChannelDeviceId" style="height: 100%">
|
||||
<div v-if="!editId && !ptzConfigChannelDeviceId && !cameraConfigDeviceId" style="height: 100%">
|
||||
<el-form :inline="true" size="mini">
|
||||
<el-form-item style="margin-right: 2rem">
|
||||
<el-page-header content="通道列表" @back="showDevice" />
|
||||
@ -193,6 +193,8 @@
|
||||
云台配置</el-dropdown-item>
|
||||
<el-dropdown-item command="audioTalk" :disabled="device == null || device.online === 0">
|
||||
语音对讲</el-dropdown-item>
|
||||
<el-dropdown-item command="cameraConfig" :disabled="device == null || device.online === 0" divided>
|
||||
相机配置</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
|
||||
</el-dropdown>
|
||||
@ -214,6 +216,7 @@
|
||||
<devicePlayer ref="devicePlayer" />
|
||||
<channel-edit v-if="editId" :id="editId" :close-edit="closeEdit" />
|
||||
<ptzConfig v-if="ptzConfigChannelDeviceId" :device-id="ptzConfigDeviceId" :channel-device-id="ptzConfigChannelDeviceId" @close="closePtzConfig" />
|
||||
<cameraConfig v-if="cameraConfigDeviceId" :device-id="deviceId" :channel-device-id="cameraConfigDeviceId" @close="cameraConfigDeviceId = null" />
|
||||
<audioTalk ref="audioTalk" />
|
||||
|
||||
</div>
|
||||
@ -224,6 +227,7 @@ import devicePlayer from '../dialog/devicePlayer.vue'
|
||||
import audioTalk from '../dialog/audioTalk.vue'
|
||||
import Edit from './edit.vue'
|
||||
import ptzConfig from '@/views/device/channel/ptzConfig.vue'
|
||||
import cameraConfig from './cameraConfig.vue'
|
||||
|
||||
export default {
|
||||
name: 'ChannelList',
|
||||
@ -231,7 +235,8 @@ export default {
|
||||
devicePlayer,
|
||||
audioTalk,
|
||||
ChannelEdit: Edit,
|
||||
ptzConfig
|
||||
ptzConfig,
|
||||
cameraConfig
|
||||
},
|
||||
props: {
|
||||
defaultPage: {
|
||||
@ -270,6 +275,7 @@ export default {
|
||||
editId: null,
|
||||
ptzConfigDeviceId: null,
|
||||
ptzConfigChannelDeviceId: null,
|
||||
cameraConfigDeviceId: null,
|
||||
loadSnap: {},
|
||||
ptzTypes: {
|
||||
0: '未知',
|
||||
@ -402,6 +408,8 @@ export default {
|
||||
this.ptzConfigChannelDeviceId = itemData.deviceId
|
||||
} else if (command === 'audioTalk') {
|
||||
this.$refs.audioTalk.openDialog(this.deviceId, itemData.deviceId)
|
||||
} else if (command === 'cameraConfig') {
|
||||
this.cameraConfigDeviceId = itemData.deviceId
|
||||
}
|
||||
},
|
||||
queryRecords: function(itemData) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user