lawrencehj
2021-02-03 18c3b5931872fe1399440a89e1c17562b16b997c
增加多种信令的支持
4个文件已修改
830 ■■■■ 已修改文件
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java 83 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java 437 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java 298 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
@@ -17,7 +17,15 @@
@Component
public class DeferredResultHolder {
    
    public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS";
    public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO";
    public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL";
    public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG";
    public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD";
    
    public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG";
    
@@ -29,6 +37,10 @@
    public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
    public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
    public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
    private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>();
    
    public void put(String key, DeferredResult result) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -115,24 +115,35 @@
    /**
     * 音视频录像控制
     * 
     * @param device  视频设备
     * @param channelId  预览通道
     * @param device          视频设备
     * @param channelId      预览通道
     * @param recordCmdStr    录像命令:Record / StopRecord
     */
    boolean recordCmd(Device device,String channelId);
    boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);
    
    /**
     * 远程启动控制命令
     *
     * @param device    视频设备
     */
    boolean teleBootCmd(Device device);
    /**
     * 报警布防/撤防命令
     * 
     * @param device  视频设备
     * @param device      视频设备
     * @param setGuard    true: SetGuard, false: ResetGuard
     */
    boolean guardCmd(Device device);
    boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);
    
    /**
     * 报警复位命令
     * 
     * @param device  视频设备
     * @param device        视频设备
     * @param alarmMethod    报警方式(可选)
     * @param alarmType        报警类型(可选)
     */
    boolean alarmCmd(Device device);
    boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);
    
    /**
     * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
@@ -140,14 +151,17 @@
     * @param device  视频设备
     * @param channelId  预览通道
     */
    boolean iFameCmd(Device device,String channelId);
    boolean iFrameCmd(Device device, String channelId);
    
    /**
     * 看守位控制命令
     * 
     * @param device  视频设备
     * @param device        视频设备
     * @param enabled        看守位使能:1 = 开启,0 = 关闭
     * @param resetTime        自动归位时间间隔,开启看守位时使用,单位:秒(s)
     * @param presetIndex    调用预置位编号,开启看守位时使用,取值范围0~255
     */
    boolean homePositionCmd(Device device);
    boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);
    
    /**
     * 设备配置命令
@@ -156,13 +170,24 @@
     */
    boolean deviceConfigCmd(Device device);
    
        /**
     * 设备配置命令:basicParam
     *
     * @param device              视频设备
     * @param channelId            通道编码(可选)
     * @param name                设备/通道名称(可选)
     * @param expiration        注册过期时间(可选)
     * @param heartBeatInterval    心跳间隔时间(可选)
     * @param heartBeatCount    心跳超时次数(可选)
     */
    boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);
    
    /**
     * 查询设备状态
     * 
     * @param device 视频设备
     */
    boolean deviceStatusQuery(Device device);
    boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);
    
    /**
     * 查询设备信息
@@ -191,23 +216,33 @@
    /**
     * 查询报警信息
     * 
     * @param device 视频设备
     * @param device        视频设备
     * @param startPriority    报警起始级别(可选)
     * @param endPriority    报警终止级别(可选)
     * @param alarmMethod    报警方式条件(可选)
     * @param alarmType        报警类型
     * @param startTime        报警发生起始时间(可选)
     * @param endTime        报警发生终止时间(可选)
     * @return                true = 命令发送成功
     */
    boolean alarmInfoQuery(Device device);
    boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
                            String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);
    
    /**
     * 查询设备配置
     * 
     * @param device 视频设备
     * @param device         视频设备
     * @param channelId        通道编码(可选)
     * @param configType    配置类型:
     */
    boolean configQuery(Device device);
    boolean deviceConfigQuery(Device device, String channelId, String configType,  SipSubscribe.Event errorEvent);
    
    /**
     * 查询设备预置位置
     * 
     * @param device 视频设备
     */
    boolean presetQuery(Device device);
    boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);
    
    /**
     * 查询移动设备位置数据
@@ -222,10 +257,26 @@
     * @param device    视频设备
     * @param expires    订阅超时时间(值=0时为取消订阅)
     * @param interval    上报时间间隔
     * @return            true = 命令发送成功
     */
    boolean mobilePositionSubscribe(Device device, int expires, int interval);
    /**
     * 订阅、取消订阅报警信息
     * @param device        视频设备
     * @param expires        订阅过期时间(0 = 取消订阅)
     * @param startPriority    报警起始级别(可选)
     * @param endPriority    报警终止级别(可选)
     * @param alarmMethods    报警方式条件(可选)
     * @param alarmType        报警类型
     * @param startTime        报警发生起始时间(可选)
     * @param endTime        报警发生终止时间(可选)
     * @return                true = 命令发送成功
     */
    boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
    /**
     * 释放rtpserver
     * @param device
     * @param channelId
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -34,6 +34,8 @@
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
/**    
 * @Description:设备能力接口,用于定义设备的控制、查询能力   
@@ -577,24 +579,89 @@
    /**
     * 音视频录像控制
     * 
     * @param device  视频设备
     * @param channelId  预览通道
     * @param device        视频设备
     * @param channelId      预览通道
     * @param recordCmdStr    录像命令:Record / StopRecord
     */  
    @Override
    public boolean recordCmd(Device device, String channelId) {
        // TODO Auto-generated method stub
        return false;
    public boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Control>\r\n");
            cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            if (XmlUtil.isEmpty(channelId)) {
                cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            } else {
                cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
            }
            cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");
            cmdXml.append("</Control>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromRecord" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 远程启动控制命令
     *
     * @param device    视频设备
     */
    @Override
    public boolean teleBootCmd(Device device) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Control>\r\n");
            cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");
            cmdXml.append("</Control>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null);
            transmitRequest(device, request);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 报警布防/撤防命令
     * 
     * @param device  视频设备
     */
     * @param device          视频设备
     * @param guardCmdStr    "SetGuard"/"ResetGuard"
     */
    @Override
    public boolean guardCmd(Device device) {
        // TODO Auto-generated method stub
        return false;
    public boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Control>\r\n");
            cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
            cmdXml.append("</Control>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
@@ -603,9 +670,37 @@
     * @param device  视频设备
     */  
    @Override
    public boolean alarmCmd(Device device) {
        // TODO Auto-generated method stub
        return false;
    public boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Control>\r\n");
            cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");
            if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) {
                cmdXml.append("<Info>\r\n");
            }
            if (!XmlUtil.isEmpty(alarmMethod)) {
                cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
            }
            if (!XmlUtil.isEmpty(alarmType)) {
                cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
            }
            if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) {
                cmdXml.append("</Info>\r\n");
            }
            cmdXml.append("</Control>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
@@ -615,20 +710,79 @@
     * @param channelId  预览通道
     */ 
    @Override
    public boolean iFameCmd(Device device, String channelId) {
        // TODO Auto-generated method stub
        return false;
    public boolean iFrameCmd(Device device, String channelId) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Control>\r\n");
            cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            if (XmlUtil.isEmpty(channelId)) {
                cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            } else {
                cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
            }
            cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");
            cmdXml.append("</Control>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null);
            transmitRequest(device, request);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 看守位控制命令
     * 
     * @param device  视频设备
     * @param device        视频设备
     * @param enabled        看守位使能:1 = 开启,0 = 关闭
     * @param resetTime        自动归位时间间隔,开启看守位时使用,单位:秒(s)
     * @param presetIndex    调用预置位编号,开启看守位时使用,取值范围0~255
     */  
    @Override
    public boolean homePositionCmd(Device device) {
        // TODO Auto-generated method stub
        return false;
    public boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Control>\r\n");
            cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            if (XmlUtil.isEmpty(channelId)) {
                cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            } else {
                cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
            }
            cmdXml.append("<HomePosition>\r\n");
            if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
                cmdXml.append("<Enabled>1</Enabled>\r\n");
                if (NumericUtil.isInteger(resetTime)) {
                    cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
                } else {
                    cmdXml.append("<ResetTime>0</ResetTime>\r\n");
                }
                if (NumericUtil.isInteger(presetIndex)) {
                    cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
                } else {
                    cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
                }
            } else {
                cmdXml.append("<Enabled>0</Enabled>\r\n");
            }
            cmdXml.append("</HomePosition>\r\n");
            cmdXml.append("</Control>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
@@ -643,14 +797,87 @@
    }
    /**
     * 设备配置命令:basicParam
     *
     * @param device              视频设备
     * @param channelId            通道编码(可选)
     * @param name                设备/通道名称(可选)
     * @param expiration        注册过期时间(可选)
     * @param heartBeatInterval    心跳间隔时间(可选)
     * @param heartBeatCount    心跳超时次数(可选)
     */
    @Override
    public boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration,
                                        String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Control>\r\n");
            cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            if (XmlUtil.isEmpty(channelId)) {
                cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            } else {
                cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
            }
            cmdXml.append("<BasicParam>\r\n");
            if (!XmlUtil.isEmpty(name)) {
                cmdXml.append("<Name>" + name + "</Name>\r\n");
            }
            if (NumericUtil.isInteger(expiration)) {
                if (Integer.valueOf(expiration) > 0) {
                    cmdXml.append("<Expiration>" + expiration + "</Expiration>\r\n");
                }
            }
            if (NumericUtil.isInteger(heartBeatInterval)) {
                if (Integer.valueOf(heartBeatInterval) > 0) {
                    cmdXml.append("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>\r\n");
                }
            }
            if (NumericUtil.isInteger(heartBeatCount)) {
                if (Integer.valueOf(heartBeatCount) > 0) {
                    cmdXml.append("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>\r\n");
                }
            }
            cmdXml.append("</BasicParam>\r\n");
            cmdXml.append("</Control>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 查询设备状态
     * 
     * @param device 视频设备
     */  
    @Override
    public boolean deviceStatusQuery(Device device) {
        // TODO Auto-generated method stub
        return false;
    public boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) {
        try {
            StringBuffer catalogXml = new StringBuffer(200);
            catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
            catalogXml.append("<Query>\r\n");
            catalogXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
            catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            catalogXml.append("</Query>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, "FromStatus" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
@@ -748,23 +975,86 @@
    /**
     * 查询报警信息
     * 
     * @param device 视频设备
     */
     * @param device        视频设备
     * @param startPriority    报警起始级别(可选)
     * @param endPriority    报警终止级别(可选)
     * @param alarmMethods    报警方式条件(可选)
     * @param alarmType        报警类型
     * @param startTime        报警发生起始时间(可选)
     * @param endTime        报警发生终止时间(可选)
     * @return                true = 命令发送成功
     */
    @Override
    public boolean alarmInfoQuery(Device device) {
        // TODO Auto-generated method stub
        return false;
    public boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType,
                                 String startTime, String endTime, SipSubscribe.Event errorEvent) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Query>\r\n");
            cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            if (!XmlUtil.isEmpty(startPriority)) {
                cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
            }
            if (!XmlUtil.isEmpty(endPriority)) {
                cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
            }
            if (!XmlUtil.isEmpty(alarmMethod)) {
                cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
            }
            if (!XmlUtil.isEmpty(alarmType)) {
                cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
            }
            if (!XmlUtil.isEmpty(startTime)) {
                cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
            }
            if (!XmlUtil.isEmpty(endTime)) {
                cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
            }
            cmdXml.append("</Query>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 查询设备配置
     * 
     * @param device 视频设备
     */
     * @param device         视频设备
     * @param channelId        通道编码(可选)
     * @param configType    配置类型:
     */
    @Override
    public boolean configQuery(Device device) {
        // TODO Auto-generated method stub
        return false;
    public boolean deviceConfigQuery(Device device, String channelId, String configType,  SipSubscribe.Event errorEvent) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Query>\r\n");
            cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            if (XmlUtil.isEmpty(channelId)) {
                cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            } else {
                cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
            }
            cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
            cmdXml.append("</Query>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
@@ -773,9 +1063,28 @@
     * @param device 视频设备
     */  
    @Override
    public boolean presetQuery(Device device) {
        // TODO Auto-generated method stub
        return false;
    public boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" ?>\r\n");
            cmdXml.append("<Query>\r\n");
            cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            if (XmlUtil.isEmpty(channelId)) {
                cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            } else {
                cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
            }
            cmdXml.append("</Query>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null);
            transmitRequest(device, request, errorEvent);
            return true;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
@@ -813,6 +1122,7 @@
     * @param device    视频设备
     * @param expires    订阅超时时间
     * @param interval    上报时间间隔
     * @return            true = 命令发送成功
     */
    public boolean mobilePositionSubscribe(Device device, int expires, int interval) {
        try {
@@ -828,7 +1138,60 @@
            subscribePostitionXml.append("</Query>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "Position;id=" + tm.substring(tm.length() - 4));
            Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); //Position;id=" + tm.substring(tm.length() - 4));
            transmitRequest(device, request);
            return true;
        } catch ( NumberFormatException | ParseException | InvalidArgumentException    | SipException e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 订阅、取消订阅报警信息
     *
     * @param device        视频设备
     * @param expires        订阅过期时间(0 = 取消订阅)
     * @param startPriority    报警起始级别(可选)
     * @param endPriority    报警终止级别(可选)
     * @param alarmMethod    报警方式条件(可选)
     * @param alarmType        报警类型
     * @param startTime        报警发生起始时间(可选)
     * @param endTime        报警发生终止时间(可选)
     * @return                true = 命令发送成功
     */
    public boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) {
        try {
            StringBuffer cmdXml = new StringBuffer(200);
            cmdXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
            cmdXml.append("<Query>\r\n");
            cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
            cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
            cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
            if (!XmlUtil.isEmpty(startPriority)) {
                cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
            }
            if (!XmlUtil.isEmpty(endPriority)) {
                cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
            }
            if (!XmlUtil.isEmpty(alarmMethod)) {
                cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
            }
            if (!XmlUtil.isEmpty(alarmType)) {
                cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
            }
            if (!XmlUtil.isEmpty(startTime)) {
                cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
            }
            if (!XmlUtil.isEmpty(endTime)) {
                cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
            }
            cmdXml.append("</Query>\r\n");
            String tm = Long.toString(System.currentTimeMillis());
            Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" );
            transmitRequest(device, request);
            return true;
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
@@ -10,6 +10,7 @@
import javax.sip.message.Request;
import javax.sip.message.Response;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.UserSetup;
@@ -78,9 +79,12 @@
    private static final String MESSAGE_RECORD_INFO = "RecordInfo";
    private static final String MESSAGE_MEDIA_STATUS = "MediaStatus";
    // private static final String MESSAGE_BROADCAST = "Broadcast";
    // private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
    private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
    private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl";
    private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig";
    private static final String MESSAGE_MOBILE_POSITION = "MobilePosition";
    // private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval";
    private static final String MESSAGE_PRESET_QUERY = "PresetQuery";
    /**
     * 处理MESSAGE请求
@@ -99,12 +103,22 @@
                processMessageKeepAlive(evt);
            } else if (MESSAGE_CONFIG_DOWNLOAD.equals(cmd)) {
                logger.info("接收到ConfigDownload消息");
                processMessageConfigDownload(evt);
            } else if (MESSAGE_CATALOG.equals(cmd)) {
                logger.info("接收到Catalog消息");
                processMessageCatalogList(evt);
            } else if (MESSAGE_DEVICE_INFO.equals(cmd)) {
                logger.info("接收到DeviceInfo消息");
                processMessageDeviceInfo(evt);
            } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) {
                logger.info("接收到DeviceStatus消息");
                processMessageDeviceStatus(evt);
            } else if (MESSAGE_DEVICE_CONTROL.equals(cmd)) {
                logger.info("接收到DeviceControl消息");
                processMessageDeviceControl(evt);
            } else if (MESSAGE_DEVICE_CONFIG.equals(cmd)) {
                logger.info("接收到DeviceConfig消息");
                processMessageDeviceConfig(evt);
            } else if (MESSAGE_ALARM.equals(cmd)) {
                logger.info("接收到Alarm消息");
                processMessageAlarm(evt);
@@ -117,6 +131,9 @@
            } else if (MESSAGE_MOBILE_POSITION.equals(cmd)) {
                logger.info("接收到MobilePosition消息");
                processMessageMobilePosition(evt);
            } else if (MESSAGE_PRESET_QUERY.equals(cmd)) {
                logger.info("接收到PresetQuery消息");
                processMessagePresetQuery(evt);
            } else {
                logger.info("接收到消息:" + cmd);
                responseAck(evt);
@@ -133,7 +150,6 @@
     */
    private void processMessageMobilePosition(RequestEvent evt) {
        try {
            //回复 200 OK
            Element rootElement = getRootElement(evt);
            MobilePosition mobilePosition = new MobilePosition();
            Element deviceIdElement = rootElement.element("DeviceID");
@@ -174,12 +190,7 @@
                storager.clearMobilePositionsByDeviceId(deviceId);
            }
            storager.insertMobilePosition(mobilePosition);
    // List<MobilePosition> all= storager.queryMobilePositions(deviceId, "2021-01-23T00:00:00", "2021-02-28T23:59:59");
            // all= storager.queryMobilePositions(deviceId, null, "2021-01-24T23:59:59");
            // all= storager.queryMobilePositions(deviceId, "2021-01-24T00:00:00", null);
            // //logger.debug(all.toString());
            // MobilePosition mp = storager.queryLatestPosition(deviceId);
            // logger.debug("最新位置:" + mp.getLongitude() + ", " + mp.getLatitude());
            //回复 200 OK
            responseAck(evt);
        } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
            e.printStackTrace();
@@ -187,7 +198,169 @@
    }
    /**
     * 收到deviceInfo设备信息请求 处理
     * 处理DeviceStatus设备状态Message
     *
     * @param evt
     */
    private void processMessageDeviceStatus(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            String deviceId = XmlUtil.getText(rootElement, "DeviceID");
            // 检查设备是否存在, 不存在则不回复
            if (storager.exists(deviceId)) {
                // 回复200 OK
                responseAck(evt);
                JSONObject json = new JSONObject();
                XmlUtil.node2Json(rootElement, json);
                if (logger.isDebugEnabled()) {
                    logger.debug(json.toJSONString());
                }
                RequestMessage msg = new RequestMessage();
                msg.setDeviceId(deviceId);
                msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS);
                msg.setData(json);
                deferredResultHolder.invokeResult(msg);
                if (offLineDetector.isOnline(deviceId)) {
                    publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
                } else {
                }
            }
        } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
            e.printStackTrace();
        }
    }
    /**
     * 处理DeviceControl设备状态Message
     *
     * @param evt
     */
    private void processMessageDeviceControl(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            String deviceId = XmlUtil.getText(rootElement, "DeviceID");
            String result = XmlUtil.getText(rootElement, "Result");
            // 回复200 OK
            responseAck(evt);
            if (!XmlUtil.isEmpty(result)) {
                // 此处是对本平台发出DeviceControl指令的应答
                JSONObject json = new JSONObject();
                XmlUtil.node2Json(rootElement, json);
                if (logger.isDebugEnabled()) {
                    logger.debug(json.toJSONString());
                }
                RequestMessage msg = new RequestMessage();
                msg.setDeviceId(deviceId);
                msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL);
                msg.setData(json);
                deferredResultHolder.invokeResult(msg);
            } else {
                // 此处是上级发出的DeviceControl指令
            }
        } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
            e.printStackTrace();
        }
    }
    /**
     * 处理DeviceConfig设备状态Message
     *
     * @param evt
     */
    private void processMessageDeviceConfig(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            String deviceId = XmlUtil.getText(rootElement, "DeviceID");
            String result = XmlUtil.getText(rootElement, "Result");
            // 回复200 OK
            responseAck(evt);
            //if (!XmlUtil.isEmpty(result)) {
                // 此处是对本平台发出DeviceControl指令的应答
                JSONObject json = new JSONObject();
                XmlUtil.node2Json(rootElement, json);
                if (logger.isDebugEnabled()) {
                    logger.debug(json.toJSONString());
                }
                RequestMessage msg = new RequestMessage();
                msg.setDeviceId(deviceId);
                msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG);
                msg.setData(json);
                deferredResultHolder.invokeResult(msg);
            // } else {
            //     // 此处是上级发出的DeviceConfig指令
            //}
        } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
            e.printStackTrace();
        }
    }
    /**
     * 处理ConfigDownload设备状态Message
     *
     * @param evt
     */
    private void processMessageConfigDownload(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            String deviceId = XmlUtil.getText(rootElement, "DeviceID");
            String result = XmlUtil.getText(rootElement, "Result");
            // 回复200 OK
            responseAck(evt);
            //if (!XmlUtil.isEmpty(result)) {
                // 此处是对本平台发出DeviceControl指令的应答
                JSONObject json = new JSONObject();
                XmlUtil.node2Json(rootElement, json);
                if (logger.isDebugEnabled()) {
                    logger.debug(json.toJSONString());
                }
                RequestMessage msg = new RequestMessage();
                msg.setDeviceId(deviceId);
                msg.setType(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD);
                msg.setData(json);
                deferredResultHolder.invokeResult(msg);
            // } else {
            //     // 此处是上级发出的DeviceConfig指令
            //}
        } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
            e.printStackTrace();
        }
    }
    /**
     * 处理PresetQuery预置位列表Message
     *
     * @param evt
     */
    private void processMessagePresetQuery(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            String deviceId = XmlUtil.getText(rootElement, "DeviceID");
            String result = XmlUtil.getText(rootElement, "Result");
            // 回复200 OK
            responseAck(evt);
            if (rootElement.getName().equals("Response")) {//   !XmlUtil.isEmpty(result)) {
                // 此处是对本平台发出DeviceControl指令的应答
                JSONObject json = new JSONObject();
                XmlUtil.node2Json(rootElement, json);
                if (logger.isDebugEnabled()) {
                    logger.debug(json.toJSONString());
                }
                RequestMessage msg = new RequestMessage();
                msg.setDeviceId(deviceId);
                msg.setType(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY);
                msg.setData(json);
                deferredResultHolder.invokeResult(msg);
            } else {
                // 此处是上级发出的DeviceControl指令
            }
        } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
            e.printStackTrace();
        }
    }
    /**
     * 处理DeviceInfo设备信息Message
     * 
     * @param evt
     */
@@ -354,56 +527,72 @@
            Element rootElement = getRootElement(evt);
            Element deviceIdElement = rootElement.element("DeviceID");
            String deviceId = deviceIdElement.getText().toString();
            // 回复200 OK
            responseAck(evt);
            Device device = storager.queryVideoDevice(deviceId);
            if (device == null) {
                return;
            }
            DeviceAlarm deviceAlarm = new DeviceAlarm();
            deviceAlarm.setDeviceId(deviceId);
            deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
            deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
            deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
            if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
                deviceAlarm.setAlarmDescription("");
            } else {
                deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription"));
            }
            if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) {
                deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
            } else {
                deviceAlarm.setLongitude(0.00);
            }
            if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) {
                deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
            } else {
                deviceAlarm.setLatitude(0.00);
            }
            if ( deviceAlarm.getAlarmMethod().equals("4")) {
                MobilePosition mobilePosition = new MobilePosition();
                mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
                mobilePosition.setTime(deviceAlarm.getAlarmTime());
                mobilePosition.setLongitude(deviceAlarm.getLongitude());
                mobilePosition.setLatitude(deviceAlarm.getLatitude());
                mobilePosition.setReportSource("GPS Alarm");
                BaiduPoint bp = new BaiduPoint();
                bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
                logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
                mobilePosition.setGeodeticSystem("BD-09");
                mobilePosition.setCnLng(bp.getBdLng());
                mobilePosition.setCnLat(bp.getBdLat());
                if (!userSetup.getSavePositionHistory()) {
                    storager.clearMobilePositionsByDeviceId(deviceId);
            if (rootElement.getName().equals("Notify")) {    // 处理报警通知
                DeviceAlarm deviceAlarm = new DeviceAlarm();
                deviceAlarm.setDeviceId(deviceId);
                deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
                deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
                deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
                if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
                    deviceAlarm.setAlarmDescription("");
                } else {
                    deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription"));
                }
                storager.insertMobilePosition(mobilePosition);
            }
            // TODO: 需要实现存储报警信息、报警分类
            // 回复200 OK
            responseAck(evt);
            if (offLineDetector.isOnline(deviceId)) {
                publisher.deviceAlarmEventPublish(deviceAlarm);
                if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) {
                    deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
                } else {
                    deviceAlarm.setLongitude(0.00);
                }
                if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) {
                    deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
                } else {
                    deviceAlarm.setLatitude(0.00);
                }
                if (!XmlUtil.isEmpty(deviceAlarm.getAlarmMethod())) {
                    if ( deviceAlarm.getAlarmMethod().equals("4")) {
                        MobilePosition mobilePosition = new MobilePosition();
                        mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
                        mobilePosition.setTime(deviceAlarm.getAlarmTime());
                        mobilePosition.setLongitude(deviceAlarm.getLongitude());
                        mobilePosition.setLatitude(deviceAlarm.getLatitude());
                        mobilePosition.setReportSource("GPS Alarm");
                        BaiduPoint bp = new BaiduPoint();
                        bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
                        logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
                        mobilePosition.setGeodeticSystem("BD-09");
                        mobilePosition.setCnLng(bp.getBdLng());
                        mobilePosition.setCnLat(bp.getBdLat());
                        if (!userSetup.getSavePositionHistory()) {
                            storager.clearMobilePositionsByDeviceId(deviceId);
                        }
                        storager.insertMobilePosition(mobilePosition);
                    }
                }
                // TODO: 需要实现存储报警信息、报警分类
                if (offLineDetector.isOnline(deviceId)) {
                    publisher.deviceAlarmEventPublish(deviceAlarm);
                }
            } else if (rootElement.getName().equals("Response")) {    // 处理报警查询响应
                JSONObject json = new JSONObject();
                XmlUtil.node2Json(rootElement, json);
                if (logger.isDebugEnabled()) {
                    logger.debug(json.toJSONString());
                }
                RequestMessage msg = new RequestMessage();
                msg.setDeviceId(deviceId);
                msg.setType(DeferredResultHolder.CALLBACK_CMD_ALARM);
                msg.setData(json);
                deferredResultHolder.invokeResult(msg);
            }
        } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
            // } catch (DocumentException e) {
@@ -435,7 +624,7 @@
    }
    /***
     * 收到catalog设备目录列表请求 处理 TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致
     * 处理RecordInfo设备录像列表Message请求 TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致
     * 
     * @param evt
     */
@@ -522,12 +711,9 @@
            // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作
            // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据
            // 对记录进行排序
            RequestMessage msg = new RequestMessage();
            msg.setDeviceId(deviceId);
            msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
            // // 自然顺序排序, 元素进行升序排列
            // recordInfo.getRecordList().sort(Comparator.naturalOrder());
            msg.setData(recordInfo);
            deferredResultHolder.invokeResult(msg);
            logger.info("处理完成,返回结果");