Compare commits

...

4 Commits

Author SHA1 Message Date
阿斌
486e83ae4f
Pre Merge pull request !36 from 阿斌/N/A 2026-06-17 08:36:50 +00:00
lin
56b211cc6c 增加视频参数范围设置接口 2026-06-17 16:34:06 +08:00
lin
5a3b831717 支持相机基础信息配置 2026-06-17 16:16:50 +08:00
阿斌
da98101aac
update src/main/resources/civilCode.csv.
行政规划错误。江苏南通海门市,修改为海门区,浙江杭州删除下城区、江干区,新增钱塘区,临平区

Signed-off-by: 阿斌 <38912748@qq.com>
2024-12-15 08:58:42 +00:00
23 changed files with 1020 additions and 299 deletions

View File

@ -1,14 +1,16 @@
package com.genersoft.iot.vmp.gb28181.bean; package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.dom4j.Element;
/** /**
* 基础配置 * 基础配置
*/ */
@Data @Data
@Schema(description = "基础配置") @Schema(description = "基础配置")
public class BasicParam { public class BasicParam implements DeviceConfigAware {
@Schema(description = "设备ID") @Schema(description = "设备ID")
private String deviceId; private String deviceId;
@ -46,4 +48,20 @@ public class BasicParam {
basicParam.setHeartBeatCount(heartBeatCount); basicParam.setHeartBeatCount(heartBeatCount);
return basicParam; 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"));
}
} }

View File

@ -0,0 +1,10 @@
package com.genersoft.iot.vmp.gb28181.bean;
import org.dom4j.Element;
public interface DeviceConfigAware {
String configType();
void fromXml(Element element);
}

View File

@ -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"));
}
}
}

View File

@ -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"));
}
}
}

View File

@ -0,0 +1,31 @@
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 = "设备ID")
private String deviceId;
@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"));
}
}

View File

@ -1,15 +1,7 @@
/**
* 设备设置命令API接口
*
* @author lawrencehj
* @date 2021年2月2日
*/
package com.genersoft.iot.vmp.gb28181.controller; package com.genersoft.iot.vmp.gb28181.controller;
import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.conf.security.JwtUtils;
import com.genersoft.iot.vmp.gb28181.bean.BasicParam; import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
@ -32,60 +24,130 @@ public class DeviceConfig {
@Autowired @Autowired
private IDeviceService deviceService; private IDeviceService deviceService;
@GetMapping("/basicParam") @GetMapping("/set/basicParam")
@Operation(summary = "基本配置设置命令", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Operation(summary = "设置基本配置", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "basicParam", description = "基础配置参数", required = true) @Parameter(name = "basicParam", description = "基础配置参数", required = true)
public DeferredResult<WVPResult<String>> homePositionApi(BasicParam basicParam) { public DeferredResult<WVPResult<String>> homePositionApi(BasicParam basicParam) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("基本配置设置命令API调用"); log.debug("基本配置设置命令API调用");
} }
Assert.notNull(basicParam.getDeviceId(), "设备ID必须存在"); Assert.notNull(basicParam.getDeviceId(), "设备ID必须存在");
Device device = deviceService.getDeviceByDeviceId(basicParam.getDeviceId()); Device device = deviceService.getDeviceByDeviceId(basicParam.getDeviceId());
Assert.notNull(device, "设备不存在"); Assert.notNull(device, "设备不存在");
DeferredResult<WVPResult<String>> deferredResult = new DeferredResult<>(); DeferredResult<WVPResult<String>> deferredResult = new DeferredResult<>();
deviceService.deviceBasicConfig(device, basicParam, (code, msg, data) -> { deviceService.deviceBasicConfig(device, basicParam, (code, msg, data) -> {
deferredResult.setResult(new WVPResult<>(code, msg, data)); deferredResult.setResult(new WVPResult<>(code, msg, data));
}); });
deferredResult.onTimeout(() -> { deferredResult.onTimeout(() -> {
log.warn("[设备配置] 超时, {}", device.getDeviceId()); log.warn("[设备配置] 超时, {}", device.getDeviceId());
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时")); deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
}); });
return deferredResult; return deferredResult;
}
} @GetMapping("/set/videoParamOpt")
@Operation(summary = "设置视频参数", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "videoParamOpt", description = "视频参数", required = true)
public DeferredResult<WVPResult<String>> setVideoParamOpt(VideoParamOpt videoParamOpt) {
if (log.isDebugEnabled()) {
log.debug("视频参数设置命令API调用");
}
Assert.notNull(videoParamOpt.getDeviceId(), "设备ID必须存在");
@Operation(summary = "设备配置查询", security = @SecurityRequirement(name = JwtUtils.HEADER)) Device device = deviceService.getDeviceByDeviceId(videoParamOpt.getDeviceId());
@Parameter(name = "deviceId", description = "设备国标编号", required = true) Assert.notNull(device, "设备不存在");
@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, "设备不存在");
DeferredResult<WVPResult<Object>> deferredResult = new DeferredResult<>(); DeferredResult<WVPResult<String>> deferredResult = new DeferredResult<>();
deviceService.deviceVideoParamConfig(device, videoParamOpt, (code, msg, data) -> {
deferredResult.setResult(new WVPResult<>(code, msg, data));
});
deviceService.deviceConfigQuery(device, channelId, configType, (code, msg, data) -> { deferredResult.onTimeout(() -> {
deferredResult.setResult(new WVPResult<>(code, msg, data)); log.warn("[设备配置] 超时, {}", device.getDeviceId());
}); deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时"));
});
return deferredResult;
}
deferredResult.onTimeout(() -> { @Operation(summary = "查询基本参数配置", security = @SecurityRequirement(name = JwtUtils.HEADER))
log.warn("[获取设备配置] 超时, {}", device.getDeviceId()); @Parameter(name = "deviceId", description = "设备国标编号", required = true)
deferredResult.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "超时")); @Parameter(name = "channelId", description = "通道国标编号")
}); @GetMapping("/query/basicParam")
return deferredResult; 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 = "通道国标编号")
@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;
}
@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;
}
@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;
}
} }

View File

@ -171,7 +171,9 @@ public interface IDeviceService {
void deviceBasicConfig(Device device, BasicParam basicParam, ErrorCallback<String> callback); void deviceBasicConfig(Device device, BasicParam basicParam, ErrorCallback<String> callback);
void deviceConfigQuery(Device device, String channelId, String configType, ErrorCallback<Object> callback); void deviceVideoParamConfig(Device device, VideoParamOpt videoParamOpt, ErrorCallback<String> callback);
<T extends DeviceConfigAware> void deviceConfigQuery(Device device, String channelId, Class<T> configClass, ErrorCallback<T> callback);
void teleboot(Device device); void teleboot(Device device);

View File

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.gb28181.service.impl; package com.genersoft.iot.vmp.gb28181.service.impl;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.CommonCallback; import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.enums.ChannelDataType; import com.genersoft.iot.vmp.common.enums.ChannelDataType;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
@ -331,7 +332,7 @@ public class DeviceServiceImpl implements IDeviceService {
redisCatchStorage.updateDevice(device); redisCatchStorage.updateDevice(device);
try { try {
commander.deviceInfoQuery(device, null); commander.deviceInfoQuery(device, null);
commander.deviceConfigQuery(device, null, "BasicParam", null); commander.deviceConfigQuery(device, null, BasicParam.class, null);
} catch (InvalidArgumentException | SipException | ParseException e) { } catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); log.error("[命令发送失败] 查询设备信息: {}", e.getMessage());
} }
@ -500,37 +501,37 @@ public class DeviceServiceImpl implements IDeviceService {
} }
private void catalogSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) { private void catalogSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) {
log.info("[目录订阅] 到期, 编号: {}", deviceId);
Device device = getDeviceByDeviceId(deviceId); Device device = getDeviceByDeviceId(deviceId);
if (device == null) { if (device == null) {
log.info("[目录订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId); log.info("[目录订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId);
return; return;
} }
if (device.isOnLine() && device.getSubscribeCycleForCatalog() > 0) { if (device.isOnLine() && device.getSubscribeCycleForCatalog() > 0) {
log.info("[目录订阅] 到期, 编号: {}", deviceId);
addCatalogSubscribe(device, transactionInfo); addCatalogSubscribe(device, transactionInfo);
} }
} }
private void mobilPositionSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) { private void mobilPositionSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) {
log.info("[移动位置订阅] 到期, 编号: {}", deviceId);
Device device = getDeviceByDeviceId(deviceId); Device device = getDeviceByDeviceId(deviceId);
if (device == null) { if (device == null) {
log.info("[移动位置订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId); log.info("[移动位置订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId);
return; return;
} }
if (device.isOnLine() && device.getSubscribeCycleForMobilePosition() > 0) { if (device.isOnLine() && device.getSubscribeCycleForMobilePosition() > 0) {
log.info("[移动位置订阅] 到期, 编号: {}", deviceId);
addMobilePositionSubscribe(device, transactionInfo); addMobilePositionSubscribe(device, transactionInfo);
} }
} }
private void alarmSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) { private void alarmSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) {
log.info("[报警订阅] 到期, 编号: {}", deviceId);
Device device = getDeviceByDeviceId(deviceId); Device device = getDeviceByDeviceId(deviceId);
if (device == null) { if (device == null) {
log.info("[移报警订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId); log.info("[移报警订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId);
return; return;
} }
if (device.isOnLine() && device.getSubscribeCycleForAlarm() > 0) { if (device.isOnLine() && device.getSubscribeCycleForAlarm() > 0) {
log.info("[报警订阅] 到期, 编号: {}", deviceId);
addAlarmSubscribe(device, transactionInfo); addAlarmSubscribe(device, transactionInfo);
} }
} }
@ -1160,16 +1161,40 @@ public class DeviceServiceImpl implements IDeviceService {
} }
@Override @Override
public void deviceConfigQuery(Device device, String channelId, String configType, ErrorCallback<Object> callback) { public void deviceVideoParamConfig(Device device, VideoParamOpt videoParamOpt, ErrorCallback<String> callback) {
if (!userSetting.getServerId().equals(device.getServerId())) { if (!userSetting.getServerId().equals(device.getServerId())) {
WVPResult<String> result = redisRpcService.deviceConfigQuery(device.getServerId(), device, channelId, configType); WVPResult<String> result = redisRpcService.deviceVideoParamConfig(device.getServerId(), device, videoParamOpt);
callback.run(result.getCode(), result.getMsg(), result.getData()); if (result.getCode() == ErrorCode.SUCCESS.getCode()) {
callback.run(result.getCode(), result.getMsg(), result.getData());
}
return; return;
} }
try { try {
sipCommander.deviceConfigQuery(device, channelId, configType, callback); sipCommander.deviceVideoParamConfigCmd(device, videoParamOpt, callback);
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 设备配置: {}", e.getMessage());
callback.run(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage(), null);
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage());
}
}
@Override
public <T extends DeviceConfigAware> void deviceConfigQuery(Device device, String channelId, Class<T> configClass, ErrorCallback<T> callback) {
if (!userSetting.getServerId().equals(device.getServerId())) {
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, configClass, callback);
} catch (InvalidArgumentException | SipException | ParseException e) { } catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 获取设备配置: {}", e.getMessage()); log.error("[命令发送失败] 获取设备配置: {}", e.getMessage());
callback.run(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage(), null); callback.run(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage(), null);

View File

@ -195,6 +195,11 @@ public interface ISIPCommander {
*/ */
void deviceBasicConfigCmd(Device device, BasicParam basicParam, ErrorCallback<String> callback) throws InvalidArgumentException, SipException, ParseException; void deviceBasicConfigCmd(Device device, BasicParam basicParam, ErrorCallback<String> callback) throws InvalidArgumentException, SipException, ParseException;
/**
* 设备配置命令VideoParamOpt
*/
void deviceVideoParamConfigCmd(Device device, VideoParamOpt videoParamOpt, ErrorCallback<String> callback) throws InvalidArgumentException, SipException, ParseException;
/** /**
* 查询设备状态 * 查询设备状态
* *
@ -248,9 +253,9 @@ public interface ISIPCommander {
* *
* @param device 视频设备 * @param device 视频设备
* @param channelId 通道编码可选 * @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;
/** /**
* 查询设备预置位置 * 查询设备预置位置

View File

@ -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.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.message.SIPResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -846,11 +847,13 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<Control>\r\n"); cmdXml.append("<Control>\r\n");
cmdXml.append("<CmdType>" + cmdType + "</CmdType>\r\n"); cmdXml.append("<CmdType>" + cmdType + "</CmdType>\r\n");
cmdXml.append("<SN>" + sn + "</SN>\r\n"); cmdXml.append("<SN>" + sn + "</SN>\r\n");
String channelId = basicParam.getChannelId(); // String channelId = basicParam.getChannelId();
if (ObjectUtils.isEmpty(channelId)) { // if (ObjectUtils.isEmpty(channelId)) {
channelId = device.getDeviceId(); // channelId = device.getDeviceId();
} // }
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); // 此处经过详细测试 对于这里使用通道ID还是设备ID海康两者都支持 大华必须是设备ID宇视都支持但是不回复消息直接自己就注销重启
// 所以这里取值 device.getDeviceId()
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
cmdXml.append("<BasicParam>\r\n"); cmdXml.append("<BasicParam>\r\n");
if (!ObjectUtils.isEmpty(basicParam.getName())) { if (!ObjectUtils.isEmpty(basicParam.getName())) {
cmdXml.append("<Name>" + basicParam.getName() + "</Name>\r\n"); cmdXml.append("<Name>" + basicParam.getName() + "</Name>\r\n");
@ -870,7 +873,39 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("</Control>\r\n"); 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()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, eventResult -> {
messageSubscribe.removeSubscribe(messageEvent.getKey());
callback.run(ErrorCode.ERROR100.getCode(), "失败," + eventResult.msg, null);
});
}
@Override
public void deviceVideoParamConfigCmd(Device device, VideoParamOpt videoParamOpt, ErrorCallback<String> callback) throws InvalidArgumentException, SipException, ParseException {
int sn = (int) ((Math.random() * 9 + 1) * 100000);
String cmdType = "DeviceConfig";
StringBuffer cmdXml = new StringBuffer(200);
String charset = device.getCharset();
cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
cmdXml.append("<Control>\r\n");
cmdXml.append("<CmdType>" + cmdType + "</CmdType>\r\n");
cmdXml.append("<SN>" + sn + "</SN>\r\n");
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
cmdXml.append("<VideoParamOpt>\r\n");
if (!ObjectUtils.isEmpty(videoParamOpt.getResolution())) {
cmdXml.append("<Resolution>" + videoParamOpt.getResolution() + "</Resolution>\r\n");
}
if (!ObjectUtils.isEmpty(videoParamOpt.getDownloadSpeed())) {
cmdXml.append("<DownloadSpeed>" + videoParamOpt.getDownloadSpeed() + "</DownloadSpeed>\r\n");
}
cmdXml.append("</VideoParamOpt>\r\n");
cmdXml.append("</Control>\r\n");
MessageEvent<String> messageEvent = MessageEvent.getInstance(cmdType, sn + "", device.getDeviceId(), 2000L, callback);
messageSubscribe.addSubscribe(messageEvent); messageSubscribe.addSubscribe(messageEvent);
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
@ -1073,12 +1108,12 @@ public class SIPCommander implements ISIPCommander {
/** /**
* 查询设备配置 * 查询设备配置
* *
* @param device 视频设备 * @param device 视频设备
* @param channelId 通道编码可选 * @param channelId 通道编码可选
* @param configType 配置类型 * @param configClass 配置类型
*/ */
@Override @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"; String cmdType = "ConfigDownload";
int sn = (int) ((Math.random() * 9 + 1) * 100000); int sn = (int) ((Math.random() * 9 + 1) * 100000);
@ -1093,10 +1128,42 @@ public class SIPCommander implements ISIPCommander {
} else { } else {
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); 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("<ConfigType>" + configType + "</ConfigType>\r\n");
cmdXml.append("</Query>\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); messageSubscribe.addSubscribe(messageEvent);
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));

View File

@ -31,6 +31,7 @@ import javax.sip.RequestEvent;
import javax.sip.SipException; import javax.sip.SipException;
import javax.sip.header.AuthorizationHeader; import javax.sip.header.AuthorizationHeader;
import javax.sip.header.ContactHeader; import javax.sip.header.ContactHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.FromHeader; import javax.sip.header.FromHeader;
import javax.sip.header.ViaHeader; import javax.sip.header.ViaHeader;
import javax.sip.message.Request; import javax.sip.message.Request;
@ -75,177 +76,45 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
sipProcessorObserver.addRequestProcessor(method, this); sipProcessorObserver.addRequestProcessor(method, this);
} }
/**
* 收到注册请求 处理
*/
@Override @Override
public void process(RequestEvent evt) { public void process(RequestEvent evt) {
try { try {
SIPRequest request = (SIPRequest) evt.getRequest(); SIPRequest request = (SIPRequest) evt.getRequest();
Response response = null;
boolean passwordCorrect = false;
// 注册标志
boolean registerFlag = request.getExpires().getExpires() != 0;
// 注销成功
FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
AddressImpl address = (AddressImpl) fromHeader.getAddress(); AddressImpl address = (AddressImpl) fromHeader.getAddress();
SipUri uri = (SipUri) address.getURI(); SipUri uri = (SipUri) address.getURI();
String deviceId = uri.getUser(); String deviceId = uri.getUser();
if (userSetting.isDeviceIdStrict()) { if (userSetting.isDeviceIdStrict()) {
// 严格模式下非20位设备ID不予处理
GbCode decode = GbCode.decode(deviceId); GbCode decode = GbCode.decode(deviceId);
if (decode == null) { if (decode == null) {
// 注册失败 Response response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
return; 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); Device device = deviceService.getDeviceByDeviceId(deviceId);
RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
userSetting.getSipUseSourceIpAsRemoteAddress()); userSetting.getSipUseSourceIpAsRemoteAddress());
String requestAddress = remoteAddressInfo.getIp() + ":" + remoteAddressInfo.getPort(); 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) { if (registerFlag) {
log.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); registerHandler(device, request, remoteAddressInfo, deviceId, requestAddress);
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse) response);
device.setSipTransactionInfo(sipTransactionInfo);
deviceService.online(device);
} else { } else {
log.info("[注销成功] deviceId: {}->{}", deviceId, requestAddress); cancellationHandler(device, request, remoteAddressInfo, deviceId, requestAddress);
deviceService.offline(device);
} }
redisCatchStorage.updateDeviceRegisterTimeStamp(List.of(device));
} catch (SipException | NoSuchAlgorithmException | ParseException e) { } catch (SipException | NoSuchAlgorithmException | ParseException e) {
log.error("未处理的异常 ", 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);
}
} }

View File

@ -119,6 +119,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
}else { // 由于上面已经判断都为null则直接返回所以这里device和parentPlatform必有一个不为null }else { // 由于上面已经判断都为null则直接返回所以这里device和parentPlatform必有一个不为null
messageHandler.handForPlatform(evt, parentPlatform, rootElement); messageHandler.handForPlatform(evt, parentPlatform, rootElement);
} }
responseAck(request, Response.OK);
}else { }else {
// 不支持的message // 不支持的message
// 不存在则回复415 // 不存在则回复415

View File

@ -1,10 +1,8 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; 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.Device;
import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService; 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.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
@ -31,9 +29,6 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar
@Autowired @Autowired
private ResponseMessageHandler responseMessageHandler; private ResponseMessageHandler responseMessageHandler;
@Autowired
private DeferredResultHolder deferredResultHolder;
@Autowired @Autowired
private IDeviceService deviceService; private IDeviceService deviceService;
@ -42,54 +37,29 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar
responseMessageHandler.addHandler(cmdType, this); responseMessageHandler.addHandler(cmdType, this);
} }
@Override @Override
public void handForDevice(RequestEvent evt, Device device, Element element) { public void handForDevice(RequestEvent evt, Device device, Element element) {
try { try {
// 回复200 OK
responseAck((SIPRequest) evt.getRequest(), Response.OK); responseAck((SIPRequest) evt.getRequest(), Response.OK);
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 设备配置查询: {}", e.getMessage()); 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) { if (basicParam != null) {
Integer heartBeatInterval = basicParam.getInteger("HeartBeatInterval"); Integer heartBeatInterval = XmlUtil.getInteger(basicParam, "HeartBeatInterval");
Integer heartBeatCount = basicParam.getInteger("HeartBeatCount"); Integer heartBeatCount = XmlUtil.getInteger(basicParam, "HeartBeatCount");
Integer positionCapability = basicParam.getInteger("PositionCapability"); Integer positionCapability = XmlUtil.getInteger(basicParam, "PositionCapability");
device.setHeartBeatInterval(heartBeatInterval); device.setHeartBeatInterval(heartBeatInterval);
device.setHeartBeatCount(heartBeatCount); device.setHeartBeatCount(heartBeatCount);
device.setPositionCapability(positionCapability); device.setPositionCapability(positionCapability);
deviceService.updateDeviceHeartInfo(device); deviceService.updateDeviceHeartInfo(device);
} }
} }
@Override @Override
public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) {
// 不会收到上级平台的心跳信息
} }
} }

View File

@ -55,7 +55,9 @@ public interface IRedisRpcService {
WVPResult<String> deviceBasicConfig(String serverId, Device device, BasicParam basicParam); WVPResult<String> deviceBasicConfig(String serverId, Device device, BasicParam basicParam);
WVPResult<String> deviceConfigQuery(String serverId, Device device, String channelId, String configType); WVPResult<String> deviceVideoParamConfig(String serverId, Device device, VideoParamOpt videoParamOpt);
WVPResult<Object> deviceConfigQuery(String serverId, Device device, String channelId, String configType);
void teleboot(String serverId, Device device); void teleboot(String serverId, Device device);

View File

@ -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.conf.redis.bean.RedisRpcResponse;
import com.genersoft.iot.vmp.gb28181.bean.BasicParam; import com.genersoft.iot.vmp.gb28181.bean.BasicParam;
import com.genersoft.iot.vmp.gb28181.bean.Device; 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.bean.SyncStatus;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcController; import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcController;
@ -115,13 +116,33 @@ public class RedisRpcDeviceController extends RpcController {
return null; return null;
} }
@RedisRpcMapping("deviceVideoParamConfig")
public RedisRpcResponse deviceVideoParamConfig(RedisRpcRequest request) {
VideoParamOpt videoParamOpt = JSONObject.parseObject(request.getParam().toString(), VideoParamOpt.class);
Device device = deviceService.getDeviceByDeviceId(videoParamOpt.getDeviceId());
RedisRpcResponse response = request.getResponse();
if (device == null || !userSetting.getServerId().equals(device.getServerId())) {
response.setStatusCode(ErrorCode.ERROR400.getCode());
response.setBody("param error");
return response;
}
deviceService.deviceVideoParamConfig(device, videoParamOpt, (code, msg, data) -> {
response.setStatusCode(code);
response.setBody(new WVPResult<>(code, msg, data));
sendResponse(response);
});
return null;
}
@RedisRpcMapping("deviceConfigQuery") @RedisRpcMapping("deviceConfigQuery")
public RedisRpcResponse deviceConfigQuery(RedisRpcRequest request) { public RedisRpcResponse deviceConfigQuery(RedisRpcRequest request) {
JSONObject paramJson = JSONObject.parseObject(request.getParam().toString()); JSONObject paramJson = JSONObject.parseObject(request.getParam().toString());
String deviceId = paramJson.getString("deviceId"); String deviceId = paramJson.getString("deviceId");
String channelId = paramJson.getString("channelId"); String channelId = paramJson.getString("channelId");
String configType = paramJson.getString("configType"); String className = paramJson.getString("configType");
Device device = deviceService.getDeviceByDeviceId(deviceId); Device device = deviceService.getDeviceByDeviceId(deviceId);
@ -131,12 +152,24 @@ public class RedisRpcDeviceController extends RpcController {
response.setBody("param error"); response.setBody("param error");
return response; return response;
} }
deviceService.deviceConfigQuery(device, channelId, configType, (code, msg, data) -> { try {
response.setStatusCode(code); Class<?> rawClass = Class.forName(className);
response.setBody(new WVPResult<>(code, msg, data)); if (!DeviceConfigAware.class.isAssignableFrom(rawClass)) {
// 手动发送结果 response.setStatusCode(ErrorCode.ERROR400.getCode());
sendResponse(response); 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; return null;
} }

View File

@ -351,9 +351,17 @@ public class RedisRpcServiceImpl implements IRedisRpcService {
} }
@Override @Override
public WVPResult<String> deviceConfigQuery(String serverId, Device device, String channelId, String configType) { public WVPResult<String> deviceVideoParamConfig(String serverId, Device device, VideoParamOpt videoParamOpt) {
RedisRpcRequest request = buildRequest("device/deviceVideoParamConfig", JSONObject.toJSONString(videoParamOpt));
request.setToId(serverId);
RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
return JSON.parseObject(response.getBody().toString(), WVPResult.class);
}
@Override
public WVPResult<Object> deviceConfigQuery(String serverId, Device device, String channelId, String configType) {
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
jsonObject.put("device", device.getDeviceId()); jsonObject.put("deviceId", device.getDeviceId());
jsonObject.put("channelId", channelId); jsonObject.put("channelId", channelId);
jsonObject.put("configType", configType); jsonObject.put("configType", configType);
RedisRpcRequest request = buildRequest("device/deviceConfigQuery", jsonObject); RedisRpcRequest request = buildRequest("device/deviceConfigQuery", jsonObject);

View File

@ -861,7 +861,7 @@
320623,如东县,3206 320623,如东县,3206
320681,启东市,3206 320681,启东市,3206
320682,如皋市,3206 320682,如皋市,3206
320684,海门,3206 320684,海门,3206
320685,海安市,3206 320685,海安市,3206
3207,连云港市,32 3207,连云港市,32
320703,连云区,3207 320703,连云区,3207
@ -918,8 +918,6 @@
33,浙江省, 33,浙江省,
3301,杭州市,33 3301,杭州市,33
330102,上城区,3301 330102,上城区,3301
330103,下城区,3301
330104,江干区,3301
330105,拱墅区,3301 330105,拱墅区,3301
330106,西湖区,3301 330106,西湖区,3301
330108,滨江区,3301 330108,滨江区,3301
@ -927,6 +925,8 @@
330110,余杭区,3301 330110,余杭区,3301
330111,富阳区,3301 330111,富阳区,3301
330112,临安区,3301 330112,临安区,3301
330113,临平区,3301
330114,钱塘区,3301
330122,桐庐县,3301 330122,桐庐县,3301
330127,淳安县,3301 330127,淳安县,3301
330182,建德市,3301 330182,建德市,3301

1 编号 名称 上级
861 320623 如东县 3206
862 320681 启东市 3206
863 320682 如皋市 3206
864 320684 海门市 海门区 3206
865 320685 海安市 3206
866 3207 连云港市 32
867 320703 连云区 3207
918 33 浙江省
919 3301 杭州市 33
920 330102 上城区 3301
330103 下城区 3301
330104 江干区 3301
921 330105 拱墅区 3301
922 330106 西湖区 3301
923 330108 滨江区 3301
925 330110 余杭区 3301
926 330111 富阳区 3301
927 330112 临安区 3301
928 330113 临平区 3301
929 330114 钱塘区 3301
930 330122 桐庐县 3301
931 330127 淳安县 3301
932 330182 建德市 3301

View File

@ -113,10 +113,27 @@ export function subscribeForAlarm(params) {
}) })
} }
export function queryBasicParam(deviceId) { export function queryBasicParam(params) {
return request({ return request({
method: 'get', 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
})
}
export function setVideoParamOpt(params) {
return request({
method: 'get',
url: '/api/device/config/set/videoParamOpt',
params
}) })
} }

View File

@ -14,7 +14,9 @@ import {
queryDeviceTree, queryDeviceTree,
queryHasStreamChannels, queryHasStreamChannels,
resetGuard, resetGuard,
setBasicParam,
setGuard, setGuard,
setVideoParamOpt,
subscribeCatalog, subscribeForAlarm, subscribeCatalog, subscribeForAlarm,
subscribeMobilePosition, subscribeMobilePosition,
sync, sync,
@ -124,9 +126,29 @@ const actions = {
}) })
}) })
}, },
queryBasicParam({ commit }, deviceId) { queryBasicParam({ commit }, params) {
return new Promise((resolve, reject) => { 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 => {
reject(error)
})
})
},
setVideoParamOpt({ commit }, params) {
return new Promise((resolve, reject) => {
setVideoParamOpt(params).then(response => {
const { data } = response const { data } = response
resolve(data) resolve(data)
}).catch(error => { }).catch(error => {

View 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>

View File

@ -0,0 +1,76 @@
<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>
</div>
<div class="config-content">
<basicPropertyConfig v-if="activeTab === 'base'" :device-id="deviceId" :channel-device-id="channelDeviceId" />
</div>
</div>
</div>
</template>
<script>
import basicPropertyConfig from './basicPropertyConfig.vue'
export default {
name: 'CameraConfigPage',
components: { basicPropertyConfig },
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>

View File

@ -1,6 +1,6 @@
<template> <template>
<div id="channelList" style="height: calc(100vh - 124px);"> <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 :inline="true" size="mini">
<el-form-item style="margin-right: 2rem"> <el-form-item style="margin-right: 2rem">
<el-page-header content="通道列表" @back="showDevice" /> <el-page-header content="通道列表" @back="showDevice" />
@ -193,6 +193,8 @@
云台配置</el-dropdown-item> 云台配置</el-dropdown-item>
<el-dropdown-item command="audioTalk" :disabled="device == null || device.online === 0"> <el-dropdown-item command="audioTalk" :disabled="device == null || device.online === 0">
语音对讲</el-dropdown-item> 语音对讲</el-dropdown-item>
<el-dropdown-item command="cameraConfig" :disabled="device == null || device.online === 0" divided>
相机配置</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
@ -214,6 +216,7 @@
<devicePlayer ref="devicePlayer" /> <devicePlayer ref="devicePlayer" />
<channel-edit v-if="editId" :id="editId" :close-edit="closeEdit" /> <channel-edit v-if="editId" :id="editId" :close-edit="closeEdit" />
<ptzConfig v-if="ptzConfigChannelDeviceId" :device-id="ptzConfigDeviceId" :channel-device-id="ptzConfigChannelDeviceId" @close="closePtzConfig" /> <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" /> <audioTalk ref="audioTalk" />
</div> </div>
@ -224,6 +227,7 @@ import devicePlayer from '../dialog/devicePlayer.vue'
import audioTalk from '../dialog/audioTalk.vue' import audioTalk from '../dialog/audioTalk.vue'
import Edit from './edit.vue' import Edit from './edit.vue'
import ptzConfig from '@/views/device/channel/ptzConfig.vue' import ptzConfig from '@/views/device/channel/ptzConfig.vue'
import cameraConfig from './cameraConfig.vue'
export default { export default {
name: 'ChannelList', name: 'ChannelList',
@ -231,7 +235,8 @@ export default {
devicePlayer, devicePlayer,
audioTalk, audioTalk,
ChannelEdit: Edit, ChannelEdit: Edit,
ptzConfig ptzConfig,
cameraConfig
}, },
props: { props: {
defaultPage: { defaultPage: {
@ -270,6 +275,7 @@ export default {
editId: null, editId: null,
ptzConfigDeviceId: null, ptzConfigDeviceId: null,
ptzConfigChannelDeviceId: null, ptzConfigChannelDeviceId: null,
cameraConfigDeviceId: null,
loadSnap: {}, loadSnap: {},
ptzTypes: { ptzTypes: {
0: '未知', 0: '未知',
@ -402,6 +408,8 @@ export default {
this.ptzConfigChannelDeviceId = itemData.deviceId this.ptzConfigChannelDeviceId = itemData.deviceId
} else if (command === 'audioTalk') { } else if (command === 'audioTalk') {
this.$refs.audioTalk.openDialog(this.deviceId, itemData.deviceId) this.$refs.audioTalk.openDialog(this.deviceId, itemData.deviceId)
} else if (command === 'cameraConfig') {
this.cameraConfigDeviceId = itemData.deviceId
} }
}, },
queryRecords: function(itemData) { queryRecords: function(itemData) {

View File

@ -168,9 +168,6 @@
<el-dropdown-item command="resetGuard" :disabled="!scope.row.onLine"> <el-dropdown-item command="resetGuard" :disabled="!scope.row.onLine">
撤防 撤防
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item command="syncBasicParam" :disabled="!scope.row.onLine">
基础配置同步
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</template> </template>
@ -391,8 +388,6 @@ export default {
this.resetGuard(itemData) this.resetGuard(itemData)
} else if (command === 'delete') { } else if (command === 'delete') {
this.deleteDevice(itemData) this.deleteDevice(itemData)
} else if (command === 'syncBasicParam') {
this.syncBasicParam(itemData)
} }
}, },
setGuard: function(itemData) { setGuard: function(itemData) {
@ -472,20 +467,6 @@ export default {
}) })
}) })
}, },
syncBasicParam: function(data) {
this.$store.dispatch('device/queryBasicParam')
.then((data) => {
this.$message.success({
showClose: true,
message: `配置已同步,当前心跳间隔: ${data.BasicParam.HeartBeatInterval} 心跳间隔:${res.data.data.BasicParam.HeartBeatCount}`
})
}).catch((error) => {
this.$message.error({
showClose: true,
message: error.message
})
})
},
getKeepaliveTimeStatistics: function(deviceId) { getKeepaliveTimeStatistics: function(deviceId) {
this.$refs.timeStatistics.openDialog('心跳时间统计', 'device/getKeepaliveTimeStatistics', deviceId, 60) this.$refs.timeStatistics.openDialog('心跳时间统计', 'device/getKeepaliveTimeStatistics', deviceId, 60)
}, },