From 5a3b831717856e1e4b713411f9809c3c3a297439 Mon Sep 17 00:00:00 2001 From: lin <648540858@qq.com> Date: Wed, 17 Jun 2026 16:16:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9B=B8=E6=9C=BA=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E4=BF=A1=E6=81=AF=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/vmp/gb28181/bean/BasicParam.java | 20 +- .../vmp/gb28181/bean/DeviceConfigAware.java | 10 + .../vmp/gb28181/bean/SVACDecodeConfig.java | 70 ++++ .../vmp/gb28181/bean/SVACEncodeConfig.java | 167 ++++++++++ .../iot/vmp/gb28181/bean/VideoParamOpt.java | 28 ++ .../vmp/gb28181/controller/DeviceConfig.java | 150 +++++---- .../vmp/gb28181/service/IDeviceService.java | 2 +- .../service/impl/DeviceServiceImpl.java | 22 +- .../gb28181/transmit/cmd/ISIPCommander.java | 4 +- .../transmit/cmd/impl/SIPCommander.java | 57 +++- .../impl/RegisterRequestProcessor.java | 308 +++++++++--------- .../impl/message/MessageRequestProcessor.java | 1 + .../ConfigDownloadResponseMessageHandler.java | 40 +-- .../service/redisMsg/IRedisRpcService.java | 2 +- .../control/RedisRpcDeviceController.java | 27 +- .../redisMsg/service/RedisRpcServiceImpl.java | 4 +- web/src/api/device.js | 13 +- web/src/store/modules/device.js | 15 +- .../device/channel/basicPropertyConfig.vue | 132 ++++++++ web/src/views/device/channel/cameraConfig.vue | 93 ++++++ .../device/channel/imagePropertyConfig.vue | 84 +++++ web/src/views/device/channel/index.vue | 12 +- 22 files changed, 984 insertions(+), 277 deletions(-) create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceConfigAware.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/bean/SVACDecodeConfig.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/bean/SVACEncodeConfig.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/bean/VideoParamOpt.java create mode 100644 web/src/views/device/channel/basicPropertyConfig.vue create mode 100644 web/src/views/device/channel/cameraConfig.vue create mode 100644 web/src/views/device/channel/imagePropertyConfig.vue diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/BasicParam.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/BasicParam.java index 1ec7a721f..78aa41435 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/BasicParam.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/BasicParam.java @@ -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")); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceConfigAware.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceConfigAware.java new file mode 100644 index 000000000..3c40f1e78 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceConfigAware.java @@ -0,0 +1,10 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import org.dom4j.Element; + +public interface DeviceConfigAware { + + String configType(); + + void fromXml(Element element); +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SVACDecodeConfig.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SVACDecodeConfig.java new file mode 100644 index 000000000..d9ffd6f8b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SVACDecodeConfig.java @@ -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")); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SVACEncodeConfig.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SVACEncodeConfig.java new file mode 100644 index 000000000..9770a05d9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SVACEncodeConfig.java @@ -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 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 itemElements = element.elements("Item"); + if (!itemElements.isEmpty()) { + List 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")); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/VideoParamOpt.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/VideoParamOpt.java new file mode 100644 index 000000000..af13a9630 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/VideoParamOpt.java @@ -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")); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java index f2402cf8f..22592ad2a 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java @@ -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> homePositionApi(BasicParam basicParam) { + @GetMapping("/set/basicParam") + @Operation(summary = "设置基本配置", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "basicParam", description = "基础配置参数", required = true) + public DeferredResult> 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> deferredResult = new DeferredResult<>(); - deviceService.deviceBasicConfig(device, basicParam, (code, msg, data) -> { - deferredResult.setResult(new WVPResult<>(code, msg, data)); - }); + DeferredResult> 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> queryBasicParam(String deviceId, + @RequestParam(required = false) String channelId) { + Device device = deviceService.getDeviceByDeviceId(deviceId); + Assert.notNull(device, "设备不存在"); + DeferredResult> 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> 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> queryVideoParamOpt(String deviceId, + @RequestParam(required = false) String channelId) { + Device device = deviceService.getDeviceByDeviceId(deviceId); + Assert.notNull(device, "设备不存在"); + DeferredResult> 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> 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> querySVACEncodeConfig(String deviceId, + @RequestParam(required = false) String channelId) { + Device device = deviceService.getDeviceByDeviceId(deviceId); + Assert.notNull(device, "设备不存在"); + DeferredResult> 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> querySVACDecodeConfig(String deviceId, + @RequestParam(required = false) String channelId) { + Device device = deviceService.getDeviceByDeviceId(deviceId); + Assert.notNull(device, "设备不存在"); + DeferredResult> 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; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java index 0ba7d7c8c..c9619fa9d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java @@ -171,7 +171,7 @@ public interface IDeviceService { void deviceBasicConfig(Device device, BasicParam basicParam, ErrorCallback callback); - void deviceConfigQuery(Device device, String channelId, String configType, ErrorCallback callback); + void deviceConfigQuery(Device device, String channelId, Class configClass, ErrorCallback callback); void teleboot(Device device); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java index 54b8cdd49..e7b8c4670 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java @@ -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 callback) { + public void deviceConfigQuery(Device device, String channelId, Class configClass, ErrorCallback callback) { if (!userSetting.getServerId().equals(device.getServerId())) { - WVPResult result = redisRpcService.deviceConfigQuery(device.getServerId(), device, channelId, configType); - callback.run(result.getCode(), result.getMsg(), result.getData()); + WVPResult 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); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index 2af63f5f5..7cee6b18a 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java @@ -248,9 +248,9 @@ public interface ISIPCommander { * * @param device 视频设备 * @param channelId 通道编码(可选) - * @param configType 配置类型: + * @param configClass 配置类型: */ - void deviceConfigQuery(Device device, String channelId, String configType, ErrorCallback callback) throws InvalidArgumentException, SipException, ParseException; + void deviceConfigQuery(Device device, String channelId, Class configClass, ErrorCallback callback) throws InvalidArgumentException, SipException, ParseException; /** * 查询设备预置位置 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index e7acf1527..db6715ebd 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -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("\r\n"); cmdXml.append("" + cmdType + "\r\n"); cmdXml.append("" + sn + "\r\n"); - String channelId = basicParam.getChannelId(); - if (ObjectUtils.isEmpty(channelId)) { - channelId = device.getDeviceId(); - } - cmdXml.append("" + channelId + "\r\n"); +// String channelId = basicParam.getChannelId(); +// if (ObjectUtils.isEmpty(channelId)) { +// channelId = device.getDeviceId(); +// } + // 此处经过详细测试 对于这里使用通道ID还是设备ID,海康两者都支持 大华必须是设备ID,宇视都支持,但是不回复消息直接自己就注销重启, + // 所以这里取值 device.getDeviceId() + cmdXml.append("" + device.getDeviceId() + "\r\n"); cmdXml.append("\r\n"); if (!ObjectUtils.isEmpty(basicParam.getName())) { cmdXml.append("" + basicParam.getName() + "\r\n"); @@ -870,7 +873,7 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("\r\n"); - MessageEvent messageEvent = MessageEvent.getInstance(cmdType, sn + "", channelId, 1000L, callback); + MessageEvent 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 callback) throws InvalidArgumentException, SipException, ParseException { + public void deviceConfigQuery(Device device, String channelId, Class configClass, ErrorCallback 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("" + channelId + "\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 + "\r\n"); cmdXml.append("\r\n"); - MessageEvent messageEvent = MessageEvent.getInstance(cmdType, sn + "", channelId, 1000L, callback); + ErrorCallback 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 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())); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java index fac2c8576..e28bddbb0 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java @@ -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); + } + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java index a8c106281..ca44f941b 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java @@ -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 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java index 6aa5cfa4d..f15128c1f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java @@ -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) { - // 不会收到上级平台的心跳信息 - } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/IRedisRpcService.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/IRedisRpcService.java index 9f63b0d80..37499e986 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/IRedisRpcService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/IRedisRpcService.java @@ -55,7 +55,7 @@ public interface IRedisRpcService { WVPResult deviceBasicConfig(String serverId, Device device, BasicParam basicParam); - WVPResult deviceConfigQuery(String serverId, Device device, String channelId, String configType); + WVPResult deviceConfigQuery(String serverId, Device device, String channelId, String configType); void teleboot(String serverId, Device device); diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcDeviceController.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcDeviceController.java index c57c84dff..706d0fdfc 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcDeviceController.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcDeviceController.java @@ -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 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; } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/service/RedisRpcServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/service/RedisRpcServiceImpl.java index a3a69b2bc..5d54369c7 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/service/RedisRpcServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/service/RedisRpcServiceImpl.java @@ -351,9 +351,9 @@ public class RedisRpcServiceImpl implements IRedisRpcService { } @Override - public WVPResult deviceConfigQuery(String serverId, Device device, String channelId, String configType) { + public WVPResult 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); diff --git a/web/src/api/device.js b/web/src/api/device.js index 3e8e33d6c..073322dd6 100644 --- a/web/src/api/device.js +++ b/web/src/api/device.js @@ -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 }) } diff --git a/web/src/store/modules/device.js b/web/src/store/modules/device.js index b8e6f77fe..96d0a7408 100644 --- a/web/src/store/modules/device.js +++ b/web/src/store/modules/device.js @@ -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 => { diff --git a/web/src/views/device/channel/basicPropertyConfig.vue b/web/src/views/device/channel/basicPropertyConfig.vue new file mode 100644 index 000000000..458d4b3eb --- /dev/null +++ b/web/src/views/device/channel/basicPropertyConfig.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/web/src/views/device/channel/cameraConfig.vue b/web/src/views/device/channel/cameraConfig.vue new file mode 100644 index 000000000..9575b36b9 --- /dev/null +++ b/web/src/views/device/channel/cameraConfig.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/web/src/views/device/channel/imagePropertyConfig.vue b/web/src/views/device/channel/imagePropertyConfig.vue new file mode 100644 index 000000000..39ea7c0a1 --- /dev/null +++ b/web/src/views/device/channel/imagePropertyConfig.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/web/src/views/device/channel/index.vue b/web/src/views/device/channel/index.vue index 022c30f3f..2068026cf 100755 --- a/web/src/views/device/channel/index.vue +++ b/web/src/views/device/channel/index.vue @@ -1,6 +1,6 @@