diff --git a/pom.xml b/pom.xml index c0c99004..9e369e0f 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,11 @@ + + org.projectlombok + lombok + true + org.springframework.boot spring-boot-starter-data-redis diff --git a/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java b/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java new file mode 100644 index 00000000..3b801d29 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java @@ -0,0 +1,69 @@ +package com.genersoft.iot.vmp.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dom4j.Element; +import org.springframework.util.ObjectUtils; + +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +/** + * @author gaofuwang + * @date 2023/01/18/ 10:09:00 + * @since 1.0 + */ +@Getter +@AllArgsConstructor +public enum DeviceControlType { + + /** + * 云台控制 + * 上下左右,预置位,扫描,辅助功能,巡航 + */ + PTZ("PTZCmd","云台控制"), + /** + * 远程启动 + */ + TELE_BOOT("TeleBoot","远程启动"), + /** + * 录像控制 + */ + RECORD("RecordCmd","录像控制"), + /** + * 布防撤防 + */ + GUARD("GuardCmd","布防撤防"), + /** + * 告警控制 + */ + ALARM("AlarmCmd","告警控制"), + /** + * 强制关键帧 + */ + I_FRAME("IFameCmd","强制关键帧"), + /** + * 拉框放大 + */ + DRAG_ZOOM_IN("DragZoomIn","拉框放大"), + /** + * 拉框缩小 + */ + DRAG_ZOOM_OUT("DragZoomOut","拉框缩小"), + /** + * 看守位 + */ + HOME_POSITION("HomePosition","看守位"); + + private final String val; + + private final String desc; + + public static DeviceControlType typeOf(Element rootElement) { + for (DeviceControlType item : DeviceControlType.values()) { + if (!ObjectUtils.isEmpty(getText(rootElement,item.val))) { + return item; + } + } + return 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 eebed4eb..40571fe2 100644 --- 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 @@ -215,17 +215,19 @@ public interface ISIPCommander { * @param channelId 预览通道 */ void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException; - + /** * 看守位控制命令 - * - * @param device 视频设备 - * @param enabled 看守位使能:1 = 开启,0 = 关闭 - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 + * + * @param device 视频设备 + * @param channelId 通道id,非通道则是设备本身 + * @param frontCmd 上级平台的指令,如果存在则直接下发 + * @param enabled 看守位使能:1 = 开启,0 = 关闭 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 */ - void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; - + void homePositionCmd(Device device, String channelId,String frontCmd, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) 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 24155384..47c798fb 100644 --- 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 org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import javax.sip.InvalidArgumentException; import javax.sip.ResponseEvent; @@ -800,12 +801,14 @@ public class SIPCommander implements ISIPCommander { * 看守位控制命令 * * @param device 视频设备 + * @param channelId 通道id,非通道则是设备本身 + * @param frontCmd 上级平台的指令,如果存在则直接下发 * @param enabled 看守位使能:1 = 开启,0 = 关闭 * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 */ @Override - public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + public void homePositionCmd(Device device, String channelId,String frontCmd, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { StringBuffer cmdXml = new StringBuffer(200); String charset = device.getCharset(); @@ -819,28 +822,33 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("" + channelId + "\r\n"); } cmdXml.append("\r\n"); - if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) { - cmdXml.append("1\r\n"); - if (NumericUtil.isInteger(resetTime)) { - cmdXml.append("" + resetTime + "\r\n"); + if (StringUtils.hasText(frontCmd)){ + cmdXml.append(frontCmd); + }else{ + if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) { + cmdXml.append("1\r\n"); + if (NumericUtil.isInteger(resetTime)) { + cmdXml.append("" + resetTime + "\r\n"); + } else { + cmdXml.append("0\r\n"); + } + if (NumericUtil.isInteger(presetIndex)) { + cmdXml.append("" + presetIndex + "\r\n"); + } else { + cmdXml.append("0\r\n"); + } } else { - cmdXml.append("0\r\n"); + cmdXml.append("0\r\n"); } - if (NumericUtil.isInteger(presetIndex)) { - cmdXml.append("" + presetIndex + "\r\n"); - } else { - cmdXml.append("0\r\n"); - } - } else { - cmdXml.append("0\r\n"); } + cmdXml.append("\r\n"); cmdXml.append("\r\n"); Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent); } /** diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java index f97a6592..e2c8fd9a 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java @@ -1,8 +1,9 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd; -import com.genersoft.iot.vmp.VManageBootstrap; +import com.genersoft.iot.vmp.common.enums.DeviceControlType; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; @@ -28,6 +29,7 @@ import javax.sip.header.ToHeader; import javax.sip.message.Response; import java.text.ParseException; import java.util.Iterator; +import java.util.Objects; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; @@ -101,13 +103,11 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent // logger.error("[任务执行失败] 服务重启: {}", e.getMessage()); // } }); - } else { - // 远程启动指定设备 } } - // 云台/前端控制命令 - if (!ObjectUtils.isEmpty(getText(rootElement,"PTZCmd")) && !parentPlatform.getServerGBId().equals(targetGBId)) { - String cmdString = getText(rootElement,"PTZCmd"); + DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement); + if (!ObjectUtils.isEmpty(deviceControlType) && !parentPlatform.getServerGBId().equals(targetGBId)){ + //判断是否存在该通道 Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); if (deviceForPlatform == null) { try { @@ -117,25 +117,212 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent } return; } - try { - cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> { - // 失败的回复 - try { - responseAck(request, eventResult.statusCode, eventResult.msg); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage()); - } - }, eventResult -> { - // 成功的回复 - try { - responseAck(request, eventResult.statusCode); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage()); - } - }); - } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 云台/前端: {}", e.getMessage()); + switch (deviceControlType){ + case PTZ: + handlePtzCmd(deviceForPlatform,channelId,rootElement,request,DeviceControlType.PTZ); + break; + case ALARM: + handleAlarmCmd(deviceForPlatform,rootElement,request); + break; + case GUARD: + handleGuardCmd(deviceForPlatform,rootElement,request,DeviceControlType.GUARD); + break; + case RECORD: + handleRecordCmd(deviceForPlatform,channelId,rootElement,request,DeviceControlType.RECORD); + break; + case I_FRAME: + handleIFameCmd(deviceForPlatform,channelId); + break; + case TELE_BOOT: + handleTeleBootCmd(deviceForPlatform); + break; + case DRAG_ZOOM_IN: + handleDragZoom(deviceForPlatform,channelId,rootElement,DeviceControlType.DRAG_ZOOM_IN); + break; + case DRAG_ZOOM_OUT: + handleDragZoom(deviceForPlatform,channelId,rootElement,DeviceControlType.DRAG_ZOOM_OUT); + break; + case HOME_POSITION: + handleHomePositionCmd(deviceForPlatform,channelId,rootElement,request,DeviceControlType.HOME_POSITION); + break; + default: + break; } } } + + /** + * 处理云台指令 + * @param device 设备 + * @param channelId 通道id + * @param rootElement + * @param request + */ + private void handlePtzCmd(Device device,String channelId,Element rootElement,SIPRequest request,DeviceControlType type){ + String cmdString = getText(rootElement,type.getVal()); + try { + cmder.fronEndCmd(device, channelId, cmdString, + errorResult -> onError(request,errorResult), + okResult -> onOk(request,okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 云台/前端: {}", e.getMessage()); + } + + } + + /** + * 处理强制关键帧 + * @param device 设备 + * @param channelId 通道id + */ + private void handleIFameCmd(Device device,String channelId){ + try { + cmder.iFrameCmd(device,channelId); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 关键帧: {}", e.getMessage()); + } + } + + /** + * 处理重启命令 + * @param device 设备信息 + */ + private void handleTeleBootCmd(Device device){ + try { + cmder.teleBootCmd(device); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 重启: {}", e.getMessage()); + } + + } + + /** + * 处理拉框控制 + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param type 消息类型 + */ + private void handleDragZoom(Device device,String channelId,Element rootElement,DeviceControlType type){ + String cmdString = getText(rootElement,type.getVal()); + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("<" + type.getVal() + ">\r\n"); + cmdXml.append(cmdString); + cmdXml.append("\r\n"); + try { + cmder.dragZoomCmd(device,channelId,cmdXml.toString()); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 拉框控制: {}", e.getMessage()); + } + + } + + /** + * 处理看守位命令 + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleHomePositionCmd(Device device,String channelId,Element rootElement,SIPRequest request,DeviceControlType type){ + //获取整个消息主体,我们只需要修改请求头即可 + String cmdString = getText(rootElement,type.getVal()); + try { + cmder.homePositionCmd(device, channelId, cmdString,null,null,null, + errorResult -> onError(request,errorResult), + okResult -> onOk(request,okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 看守位设置: {}", e.getMessage()); + } + } + + /** + * 处理告警消息 + * @param device 设备信息 + * @param rootElement 根节点 + * @param request 请求信息 + */ + private void handleAlarmCmd(Device device,Element rootElement,SIPRequest request){ + //告警方法 + String alarmMethod = ""; + //告警类型 + String alarmType = ""; + Element info = rootElement.element("Info"); + if (info !=null){ + alarmMethod = getText(rootElement,"AlarmMethod"); + alarmType = getText(rootElement,"AlarmType"); + } + try { + cmder.alarmCmd(device, alarmMethod,alarmType, + errorResult -> onError(request,errorResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 告警重置: {}", e.getMessage()); + } + } + + /** + * 处理录像控制 + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleRecordCmd(Device device,String channelId,Element rootElement,SIPRequest request,DeviceControlType type){ + //获取整个消息主体,我们只需要修改请求头即可 + String cmdString = getText(rootElement,type.getVal()); + try { + cmder.recordCmd(device, channelId,cmdString, + errorResult -> onError(request,errorResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 告警重置: {}", e.getMessage()); + } + } + + /** + * 处理报警布防/撤防命令 + * @param device 设备信息 + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleGuardCmd(Device device,Element rootElement,SIPRequest request,DeviceControlType type){ + //获取整个消息主体,我们只需要修改请求头即可 + String cmdString = getText(rootElement,type.getVal()); + try { + cmder.guardCmd(device, cmdString, + errorResult -> onError(request,errorResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 布防/撤防命令: {}", e.getMessage()); + } + } + + + /** + * 错误响应处理 + * @param request 请求 + * @param eventResult 响应结构 + */ + private void onError(SIPRequest request, SipSubscribe.EventResult eventResult){ + // 失败的回复 + try { + responseAck(request, eventResult.statusCode, eventResult.msg); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 回复: {}", e.getMessage()); + } + } + /** + * 成功响应处理 + * @param request 请求 + * @param eventResult 响应结构 + */ + private void onOk(SIPRequest request, SipSubscribe.EventResult eventResult){ + // 成功的回复 + try { + responseAck(request, eventResult.statusCode); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 回复: {}", e.getMessage()); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java index 18618e74..2a44eb54 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java @@ -268,13 +268,13 @@ public class DeviceControl { String uuid = UUID.randomUUID().toString(); Device device = storager.queryVideoDevice(deviceId); try { - cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { + cmder.homePositionCmd(device, channelId,null, enabled, resetTime, presetIndex, event -> { RequestMessage msg = new RequestMessage(); msg.setId(uuid); msg.setKey(key); msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg)); resultHolder.invokeResult(msg); - }); + },null); } catch (InvalidArgumentException | SipException | ParseException e) { logger.error("[命令发送失败] 看守位控制: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());