Commit 7a9f001dcb5b8d6c4b52e8ce2c5a8764752ff7ef

Authored by gaofw189
1 parent bdf799fb

修复WVP作为下级平台接收上级平台DeviceControl信令不做处理的问题。

... ... @@ -58,6 +58,11 @@
58 58  
59 59 <dependencies>
60 60 <dependency>
  61 + <groupId>org.projectlombok</groupId>
  62 + <artifactId>lombok</artifactId>
  63 + <optional>true</optional>
  64 + </dependency>
  65 + <dependency>
61 66 <groupId>org.springframework.boot</groupId>
62 67 <artifactId>spring-boot-starter-data-redis</artifactId>
63 68 </dependency>
... ...
src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java 0 → 100644
  1 +package com.genersoft.iot.vmp.common.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +import org.dom4j.Element;
  6 +import org.springframework.util.ObjectUtils;
  7 +
  8 +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
  9 +
  10 +/**
  11 + * @author gaofuwang
  12 + * @date 2023/01/18/ 10:09:00
  13 + * @since 1.0
  14 + */
  15 +@Getter
  16 +@AllArgsConstructor
  17 +public enum DeviceControlType {
  18 +
  19 + /**
  20 + * 云台控制
  21 + * 上下左右,预置位,扫描,辅助功能,巡航
  22 + */
  23 + PTZ("PTZCmd","云台控制"),
  24 + /**
  25 + * 远程启动
  26 + */
  27 + TELE_BOOT("TeleBoot","远程启动"),
  28 + /**
  29 + * 录像控制
  30 + */
  31 + RECORD("RecordCmd","录像控制"),
  32 + /**
  33 + * 布防撤防
  34 + */
  35 + GUARD("GuardCmd","布防撤防"),
  36 + /**
  37 + * 告警控制
  38 + */
  39 + ALARM("AlarmCmd","告警控制"),
  40 + /**
  41 + * 强制关键帧
  42 + */
  43 + I_FRAME("IFameCmd","强制关键帧"),
  44 + /**
  45 + * 拉框放大
  46 + */
  47 + DRAG_ZOOM_IN("DragZoomIn","拉框放大"),
  48 + /**
  49 + * 拉框缩小
  50 + */
  51 + DRAG_ZOOM_OUT("DragZoomOut","拉框缩小"),
  52 + /**
  53 + * 看守位
  54 + */
  55 + HOME_POSITION("HomePosition","看守位");
  56 +
  57 + private final String val;
  58 +
  59 + private final String desc;
  60 +
  61 + public static DeviceControlType typeOf(Element rootElement) {
  62 + for (DeviceControlType item : DeviceControlType.values()) {
  63 + if (!ObjectUtils.isEmpty(getText(rootElement,item.val))) {
  64 + return item;
  65 + }
  66 + }
  67 + return null;
  68 + }
  69 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
... ... @@ -215,17 +215,19 @@ public interface ISIPCommander {
215 215 * @param channelId 预览通道
216 216 */
217 217 void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException;
218   -
  218 +
219 219 /**
220 220 * 看守位控制命令
221   - *
222   - * @param device 视频设备
223   - * @param enabled 看守位使能:1 = 开启,0 = 关闭
224   - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
225   - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
226   - */
227   - void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
228   -
  221 + *
  222 + * @param device 视频设备
  223 + * @param channelId 通道id,非通道则是设备本身
  224 + * @param frontCmd 上级平台的指令,如果存在则直接下发
  225 + * @param enabled 看守位使能:1 = 开启,0 = 关闭
  226 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
  227 + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
  228 + */
  229 + void homePositionCmd(Device device, String channelId,String frontCmd, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
  230 +
229 231 /**
230 232 * 设备配置命令
231 233 *
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
29 29 import org.springframework.context.annotation.DependsOn;
30 30 import org.springframework.stereotype.Component;
31 31 import org.springframework.util.ObjectUtils;
  32 +import org.springframework.util.StringUtils;
32 33  
33 34 import javax.sip.InvalidArgumentException;
34 35 import javax.sip.ResponseEvent;
... ... @@ -800,12 +801,14 @@ public class SIPCommander implements ISIPCommander {
800 801 * 看守位控制命令
801 802 *
802 803 * @param device 视频设备
  804 + * @param channelId 通道id,非通道则是设备本身
  805 + * @param frontCmd 上级平台的指令,如果存在则直接下发
803 806 * @param enabled 看守位使能:1 = 开启,0 = 关闭
804 807 * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
805 808 * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
806 809 */
807 810 @Override
808   - public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  811 + public void homePositionCmd(Device device, String channelId,String frontCmd, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
809 812  
810 813 StringBuffer cmdXml = new StringBuffer(200);
811 814 String charset = device.getCharset();
... ... @@ -819,28 +822,33 @@ public class SIPCommander implements ISIPCommander {
819 822 cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
820 823 }
821 824 cmdXml.append("<HomePosition>\r\n");
822   - if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
823   - cmdXml.append("<Enabled>1</Enabled>\r\n");
824   - if (NumericUtil.isInteger(resetTime)) {
825   - cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
  825 + if (StringUtils.hasText(frontCmd)){
  826 + cmdXml.append(frontCmd);
  827 + }else{
  828 + if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
  829 + cmdXml.append("<Enabled>1</Enabled>\r\n");
  830 + if (NumericUtil.isInteger(resetTime)) {
  831 + cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
  832 + } else {
  833 + cmdXml.append("<ResetTime>0</ResetTime>\r\n");
  834 + }
  835 + if (NumericUtil.isInteger(presetIndex)) {
  836 + cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
  837 + } else {
  838 + cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
  839 + }
826 840 } else {
827   - cmdXml.append("<ResetTime>0</ResetTime>\r\n");
  841 + cmdXml.append("<Enabled>0</Enabled>\r\n");
828 842 }
829   - if (NumericUtil.isInteger(presetIndex)) {
830   - cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
831   - } else {
832   - cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
833   - }
834   - } else {
835   - cmdXml.append("<Enabled>0</Enabled>\r\n");
836 843 }
  844 +
837 845 cmdXml.append("</HomePosition>\r\n");
838 846 cmdXml.append("</Control>\r\n");
839 847  
840 848  
841 849  
842 850 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
843   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
  851 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
844 852 }
845 853  
846 854 /**
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd;
2 2  
3   -import com.genersoft.iot.vmp.VManageBootstrap;
  3 +import com.genersoft.iot.vmp.common.enums.DeviceControlType;
4 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
5 5 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  6 +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
6 7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
7 8 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
8 9 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
... ... @@ -28,6 +29,7 @@ import javax.sip.header.ToHeader;
28 29 import javax.sip.message.Response;
29 30 import java.text.ParseException;
30 31 import java.util.Iterator;
  32 +import java.util.Objects;
31 33  
32 34 import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
33 35  
... ... @@ -101,13 +103,11 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
101 103 // logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
102 104 // }
103 105 });
104   - } else {
105   - // 远程启动指定设备
106 106 }
107 107 }
108   - // 云台/前端控制命令
109   - if (!ObjectUtils.isEmpty(getText(rootElement,"PTZCmd")) && !parentPlatform.getServerGBId().equals(targetGBId)) {
110   - String cmdString = getText(rootElement,"PTZCmd");
  108 + DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
  109 + if (!ObjectUtils.isEmpty(deviceControlType) && !parentPlatform.getServerGBId().equals(targetGBId)){
  110 + //判断是否存在该通道
111 111 Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
112 112 if (deviceForPlatform == null) {
113 113 try {
... ... @@ -117,25 +117,212 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
117 117 }
118 118 return;
119 119 }
120   - try {
121   - cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> {
122   - // 失败的回复
123   - try {
124   - responseAck(request, eventResult.statusCode, eventResult.msg);
125   - } catch (SipException | InvalidArgumentException | ParseException e) {
126   - logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());
127   - }
128   - }, eventResult -> {
129   - // 成功的回复
130   - try {
131   - responseAck(request, eventResult.statusCode);
132   - } catch (SipException | InvalidArgumentException | ParseException e) {
133   - logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());
134   - }
135   - });
136   - } catch (InvalidArgumentException | SipException | ParseException e) {
137   - logger.error("[命令发送失败] 云台/前端: {}", e.getMessage());
  120 + switch (deviceControlType){
  121 + case PTZ:
  122 + handlePtzCmd(deviceForPlatform,channelId,rootElement,request,DeviceControlType.PTZ);
  123 + break;
  124 + case ALARM:
  125 + handleAlarmCmd(deviceForPlatform,rootElement,request);
  126 + break;
  127 + case GUARD:
  128 + handleGuardCmd(deviceForPlatform,rootElement,request,DeviceControlType.GUARD);
  129 + break;
  130 + case RECORD:
  131 + handleRecordCmd(deviceForPlatform,channelId,rootElement,request,DeviceControlType.RECORD);
  132 + break;
  133 + case I_FRAME:
  134 + handleIFameCmd(deviceForPlatform,channelId);
  135 + break;
  136 + case TELE_BOOT:
  137 + handleTeleBootCmd(deviceForPlatform);
  138 + break;
  139 + case DRAG_ZOOM_IN:
  140 + handleDragZoom(deviceForPlatform,channelId,rootElement,DeviceControlType.DRAG_ZOOM_IN);
  141 + break;
  142 + case DRAG_ZOOM_OUT:
  143 + handleDragZoom(deviceForPlatform,channelId,rootElement,DeviceControlType.DRAG_ZOOM_OUT);
  144 + break;
  145 + case HOME_POSITION:
  146 + handleHomePositionCmd(deviceForPlatform,channelId,rootElement,request,DeviceControlType.HOME_POSITION);
  147 + break;
  148 + default:
  149 + break;
138 150 }
139 151 }
140 152 }
  153 +
  154 + /**
  155 + * 处理云台指令
  156 + * @param device 设备
  157 + * @param channelId 通道id
  158 + * @param rootElement
  159 + * @param request
  160 + */
  161 + private void handlePtzCmd(Device device,String channelId,Element rootElement,SIPRequest request,DeviceControlType type){
  162 + String cmdString = getText(rootElement,type.getVal());
  163 + try {
  164 + cmder.fronEndCmd(device, channelId, cmdString,
  165 + errorResult -> onError(request,errorResult),
  166 + okResult -> onOk(request,okResult));
  167 + } catch (InvalidArgumentException | SipException | ParseException e) {
  168 + logger.error("[命令发送失败] 云台/前端: {}", e.getMessage());
  169 + }
  170 +
  171 + }
  172 +
  173 + /**
  174 + * 处理强制关键帧
  175 + * @param device 设备
  176 + * @param channelId 通道id
  177 + */
  178 + private void handleIFameCmd(Device device,String channelId){
  179 + try {
  180 + cmder.iFrameCmd(device,channelId);
  181 + } catch (InvalidArgumentException | SipException | ParseException e) {
  182 + logger.error("[命令发送失败] 关键帧: {}", e.getMessage());
  183 + }
  184 + }
  185 +
  186 + /**
  187 + * 处理重启命令
  188 + * @param device 设备信息
  189 + */
  190 + private void handleTeleBootCmd(Device device){
  191 + try {
  192 + cmder.teleBootCmd(device);
  193 + } catch (InvalidArgumentException | SipException | ParseException e) {
  194 + logger.error("[命令发送失败] 重启: {}", e.getMessage());
  195 + }
  196 +
  197 + }
  198 +
  199 + /**
  200 + * 处理拉框控制
  201 + * @param device 设备信息
  202 + * @param channelId 通道id
  203 + * @param rootElement 根节点
  204 + * @param type 消息类型
  205 + */
  206 + private void handleDragZoom(Device device,String channelId,Element rootElement,DeviceControlType type){
  207 + String cmdString = getText(rootElement,type.getVal());
  208 + StringBuffer cmdXml = new StringBuffer(200);
  209 + cmdXml.append("<" + type.getVal() + ">\r\n");
  210 + cmdXml.append(cmdString);
  211 + cmdXml.append("</" + type.getVal() + ">\r\n");
  212 + try {
  213 + cmder.dragZoomCmd(device,channelId,cmdXml.toString());
  214 + } catch (InvalidArgumentException | SipException | ParseException e) {
  215 + logger.error("[命令发送失败] 拉框控制: {}", e.getMessage());
  216 + }
  217 +
  218 + }
  219 +
  220 + /**
  221 + * 处理看守位命令
  222 + * @param device 设备信息
  223 + * @param channelId 通道id
  224 + * @param rootElement 根节点
  225 + * @param request 请求信息
  226 + * @param type 消息类型
  227 + */
  228 + private void handleHomePositionCmd(Device device,String channelId,Element rootElement,SIPRequest request,DeviceControlType type){
  229 + //获取整个消息主体,我们只需要修改请求头即可
  230 + String cmdString = getText(rootElement,type.getVal());
  231 + try {
  232 + cmder.homePositionCmd(device, channelId, cmdString,null,null,null,
  233 + errorResult -> onError(request,errorResult),
  234 + okResult -> onOk(request,okResult));
  235 + } catch (InvalidArgumentException | SipException | ParseException e) {
  236 + logger.error("[命令发送失败] 看守位设置: {}", e.getMessage());
  237 + }
  238 + }
  239 +
  240 + /**
  241 + * 处理告警消息
  242 + * @param device 设备信息
  243 + * @param rootElement 根节点
  244 + * @param request 请求信息
  245 + */
  246 + private void handleAlarmCmd(Device device,Element rootElement,SIPRequest request){
  247 + //告警方法
  248 + String alarmMethod = "";
  249 + //告警类型
  250 + String alarmType = "";
  251 + Element info = rootElement.element("Info");
  252 + if (info !=null){
  253 + alarmMethod = getText(rootElement,"AlarmMethod");
  254 + alarmType = getText(rootElement,"AlarmType");
  255 + }
  256 + try {
  257 + cmder.alarmCmd(device, alarmMethod,alarmType,
  258 + errorResult -> onError(request,errorResult));
  259 + } catch (InvalidArgumentException | SipException | ParseException e) {
  260 + logger.error("[命令发送失败] 告警重置: {}", e.getMessage());
  261 + }
  262 + }
  263 +
  264 + /**
  265 + * 处理录像控制
  266 + * @param device 设备信息
  267 + * @param channelId 通道id
  268 + * @param rootElement 根节点
  269 + * @param request 请求信息
  270 + * @param type 消息类型
  271 + */
  272 + private void handleRecordCmd(Device device,String channelId,Element rootElement,SIPRequest request,DeviceControlType type){
  273 + //获取整个消息主体,我们只需要修改请求头即可
  274 + String cmdString = getText(rootElement,type.getVal());
  275 + try {
  276 + cmder.recordCmd(device, channelId,cmdString,
  277 + errorResult -> onError(request,errorResult));
  278 + } catch (InvalidArgumentException | SipException | ParseException e) {
  279 + logger.error("[命令发送失败] 告警重置: {}", e.getMessage());
  280 + }
  281 + }
  282 +
  283 + /**
  284 + * 处理报警布防/撤防命令
  285 + * @param device 设备信息
  286 + * @param rootElement 根节点
  287 + * @param request 请求信息
  288 + * @param type 消息类型
  289 + */
  290 + private void handleGuardCmd(Device device,Element rootElement,SIPRequest request,DeviceControlType type){
  291 + //获取整个消息主体,我们只需要修改请求头即可
  292 + String cmdString = getText(rootElement,type.getVal());
  293 + try {
  294 + cmder.guardCmd(device, cmdString,
  295 + errorResult -> onError(request,errorResult));
  296 + } catch (InvalidArgumentException | SipException | ParseException e) {
  297 + logger.error("[命令发送失败] 布防/撤防命令: {}", e.getMessage());
  298 + }
  299 + }
  300 +
  301 +
  302 + /**
  303 + * 错误响应处理
  304 + * @param request 请求
  305 + * @param eventResult 响应结构
  306 + */
  307 + private void onError(SIPRequest request, SipSubscribe.EventResult eventResult){
  308 + // 失败的回复
  309 + try {
  310 + responseAck(request, eventResult.statusCode, eventResult.msg);
  311 + } catch (SipException | InvalidArgumentException | ParseException e) {
  312 + logger.error("[命令发送失败] 回复: {}", e.getMessage());
  313 + }
  314 + }
  315 + /**
  316 + * 成功响应处理
  317 + * @param request 请求
  318 + * @param eventResult 响应结构
  319 + */
  320 + private void onOk(SIPRequest request, SipSubscribe.EventResult eventResult){
  321 + // 成功的回复
  322 + try {
  323 + responseAck(request, eventResult.statusCode);
  324 + } catch (SipException | InvalidArgumentException | ParseException e) {
  325 + logger.error("[命令发送失败] 回复: {}", e.getMessage());
  326 + }
  327 + }
141 328 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
... ... @@ -268,13 +268,13 @@ public class DeviceControl {
268 268 String uuid = UUID.randomUUID().toString();
269 269 Device device = storager.queryVideoDevice(deviceId);
270 270 try {
271   - cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> {
  271 + cmder.homePositionCmd(device, channelId,null, enabled, resetTime, presetIndex, event -> {
272 272 RequestMessage msg = new RequestMessage();
273 273 msg.setId(uuid);
274 274 msg.setKey(key);
275 275 msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg));
276 276 resultHolder.invokeResult(msg);
277   - });
  277 + },null);
278 278 } catch (InvalidArgumentException | SipException | ParseException e) {
279 279 logger.error("[命令发送失败] 看守位控制: {}", e.getMessage());
280 280 throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
... ...