From 4e62e90c0c009064c65642e20003c0601802ef8f Mon Sep 17 00:00:00 2001 From: shikong <919411476@qq.com> Date: Sun, 24 Sep 2023 21:31:09 +0800 Subject: [PATCH] =?UTF-8?q?gb28181=20MANSCDP+xml=20=E7=BC=96=E8=A7=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docking/gb28181/constant/CmdType.java | 18 ++ .../gb28181/constant/GB28181Constant.java | 2 + .../manscdp/catalog/CatalogDeviceListDTO.java | 22 +++ .../sip/manscdp/catalog/CatalogItemDTO.java | 150 ++++++++++++++ .../sip/manscdp/catalog/CatalogQueryDTO.java | 30 +++ .../manscdp/catalog/CatalogResponseDTO.java | 31 +++ .../gb28181/sip/utils/MANSCDPUtils.java | 186 ++++++++++++++++++ .../gb28181/sip/manscdp/MANSCDPTest.java | 68 +++++++ 8 files changed, 507 insertions(+) create mode 100644 gb28181-sip/src/main/java/cn/skcks/docking/gb28181/constant/CmdType.java create mode 100644 gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogDeviceListDTO.java create mode 100644 gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogItemDTO.java create mode 100644 gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogQueryDTO.java create mode 100644 gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogResponseDTO.java create mode 100644 gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/utils/MANSCDPUtils.java create mode 100644 gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/manscdp/MANSCDPTest.java diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/constant/CmdType.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/constant/CmdType.java new file mode 100644 index 0000000..3779402 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/constant/CmdType.java @@ -0,0 +1,18 @@ +package cn.skcks.docking.gb28181.constant; + +@SuppressWarnings("unused") +public class CmdType { + public static final String KEEPALIVE = "Keepalive"; + public static final String DEVICE_CONFIG = "DeviceConfig"; + public static final String DEVICE_CONTROL = "DeviceControl"; + public static final String DEVICE_STATUS = "DeviceStatus"; + public static final String CATALOG = "Catalog"; + public static final String ALARM = "Alarm"; + public static final String MOBILE_POSITION = "MobilePosition"; + public static final String BROADCAST = "Broadcast"; + public static final String DEVICE_INFO = "DeviceInfo"; + public static final String RECORD_INFO = "RecordInfo"; + public static final String MEDIA_STATUS = "MediaStatus"; + public static final String CONFIG_DOWNLOAD = "ConfigDownload"; + public static final String PRESET_QUERY = "PresetQuery"; +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/constant/GB28181Constant.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/constant/GB28181Constant.java index ca8f668..cc877e3 100644 --- a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/constant/GB28181Constant.java +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/constant/GB28181Constant.java @@ -7,6 +7,8 @@ public class GB28181Constant { public static final String CHARSET = "GB2312"; public static final String GEO_COORD_SYS = "WGS84"; + public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; + public static class TransPort { public static final String UDP = ListeningPoint.UDP; public static final String TCP = ListeningPoint.TCP; diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogDeviceListDTO.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogDeviceListDTO.java new file mode 100644 index 0000000..8b817b2 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogDeviceListDTO.java @@ -0,0 +1,22 @@ +package cn.skcks.docking.gb28181.sip.manscdp.catalog; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@JacksonXmlRootElement(localName = "DeviceList") +@AllArgsConstructor +@NoArgsConstructor +@Data +public class CatalogDeviceListDTO { + @JacksonXmlProperty(isAttribute = true) + private Integer num; + @JacksonXmlProperty(localName = "Item") + @JacksonXmlElementWrapper(useWrapping = false) + private List deviceList; +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogItemDTO.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogItemDTO.java new file mode 100644 index 0000000..0f31c5f --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogItemDTO.java @@ -0,0 +1,150 @@ +package cn.skcks.docking.gb28181.sip.manscdp.catalog; + + +import cn.skcks.docking.gb28181.constant.GB28181Constant; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + + +@JacksonXmlRootElement(localName = "Item") +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class CatalogItemDTO { + /** + * 设备/区域/系统编码(必选) + */ + @JacksonXmlProperty(localName = "DeviceID") + private String deviceId; + + /** + * 设备/区域/系统名称(必选) + */ + private String name; + + /** + * 当为设备时,设备厂商(必选) + */ + private String manufacturer; + + /** + * 当为设备时,设备型号(必选) + */ + private String model; + + /** + * 当为设备时,设备归属(必选) + */ + private String owner; + + /** + * 行政区域(必选) + */ + @JacksonXmlProperty(localName = "CivilCode") + private String civilCode; + + /** + * 警区(可选) + */ + private String block; + + /** + * 当为设备时,安装地址(必选) + */ + private String address; + + /** + * 当为设备时,是否有子设备(必选)1有, 0没有 + */ + @Builder.Default + private Integer parental = 0; + + /** + * 父设备/区域/系统ID(必选) + */ + @JacksonXmlProperty(localName = "ParentID") + private String parentId; + + /** + * 信令安全模式(可选)缺省为0; 0:不采用;2:S/MIME 签名方式;3:S/ MIME加密签名同时采用方式;4:数字摘要方式 + */ + @Builder.Default + private Integer safetyWay = 0; + + /** + * 注册方式(必选)缺省为1;1:符合IETF RFC3261标准的认证注册模 式;2:基于口令的双向认证注册模式;3:基于数字证书的双向认证注册模式 + */ + @Builder.Default + private Integer registerWay = 1; + + /** + * 证书序列号(有证书的设备必选) + */ + private String certNum; + + /** + * 证书有效标识(有证书的设备必选)缺省为0;证书有效标识:0:无效 1: 有效 + */ + @Builder.Default + private Integer certifiable = 0; + + /** + * 无效原因码(有证书且证书无效的设备必选) + */ + @Builder.Default + private Integer errCode = 0; + + /** + * 证书终止有效期(有证书的设备必选) + */ + @JsonFormat(pattern = GB28181Constant.DATETIME_FORMAT, timezone = GB28181Constant.TIME_ZONE) + private Date endTime; + + /** + * 保密属性(必选)缺省为0;0:不涉密,1:涉密 + */ + @Builder.Default + private Integer secrecy = 0; + + /** + * 设备/区域/系统IP地址(可选) + */ + @JacksonXmlProperty(localName = "IPAddress") + private String ipAddress; + + /** + * 设备/区域/系统端口(可选) + */ + private Integer port; + + /** + * 设备口令(可选) + */ + private String password; + + /** + * 设备状态(必选) + */ + @Builder.Default + private String status = "ON"; + + /** + * 经度(可选) + */ + @Builder.Default + private String longitude = "0.0"; + + /** + * 纬度(可选) + */ + @Builder.Default + private String latitude = "0.0"; +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogQueryDTO.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogQueryDTO.java new file mode 100644 index 0000000..c6ded58 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogQueryDTO.java @@ -0,0 +1,30 @@ +package cn.skcks.docking.gb28181.sip.manscdp.catalog; + + +import cn.skcks.docking.gb28181.constant.CmdType; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@JacksonXmlRootElement(localName = "Query") +@JsonRootName("Query") +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class CatalogQueryDTO { + @Builder.Default + private String cmdType = CmdType.CATALOG; + @JacksonXmlProperty(localName = "SN") + private String sn; + + /** + * 目标设备的设备编码(必选) + */ + @JacksonXmlProperty(localName = "DeviceID") + private String deviceId; +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogResponseDTO.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogResponseDTO.java new file mode 100644 index 0000000..57474bb --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/manscdp/catalog/CatalogResponseDTO.java @@ -0,0 +1,31 @@ +package cn.skcks.docking.gb28181.sip.manscdp.catalog; + +import cn.skcks.docking.gb28181.constant.CmdType; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@JacksonXmlRootElement(localName = "Response") +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class CatalogResponseDTO { + @Builder.Default + private String cmdType = CmdType.CATALOG; + @JacksonXmlProperty(localName = "SN") + private String sn; + + /** + * 目标设备的设备编码(必选) + */ + @JacksonXmlProperty(localName = "DeviceID") + private String deviceId; + + private Long sumNum; + + private CatalogDeviceListDTO deviceList; +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/utils/MANSCDPUtils.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/utils/MANSCDPUtils.java new file mode 100644 index 0000000..d66acea --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/utils/MANSCDPUtils.java @@ -0,0 +1,186 @@ +package cn.skcks.docking.gb28181.sip.utils; + +import cn.skcks.docking.gb28181.constant.GB28181Constant; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.json.JsonReadFeature; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; +import lombok.*; + +import java.nio.charset.Charset; +import java.util.Objects; + +@SuppressWarnings("unused") +public class MANSCDPUtils { + private static final XmlMapper mapper = new XmlMapper(); + static { + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + // 如果json中有新增的字段并且是实体类类中不存在的,不报错 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); + // 允许出现特殊字符和转义符 + mapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + // 允许出现单引号 + mapper.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true); + // 大驼峰 (首字母大写) + mapper.setPropertyNamingStrategy(new PropertyNamingStrategies.UpperCamelCaseStrategy()); + // 添加 xml 头部声明 + mapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, false); + mapper.configure(ToXmlGenerator.Feature.UNWRAP_ROOT_OBJECT_NODE, true); + } + + @JacksonXmlRootElement(localName = "xml") + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Data + public static class XMLWrapper { + @Builder.Default + @JacksonXmlProperty(isAttribute = true, localName = "encoding") + private String encoding = GB28181Constant.CHARSET; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private T query; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private T response; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private T notify; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private T control; + } + + + public static String toXml(Object obj, String inputCharset,String outputCharset) { + try { + return new String(Objects.requireNonNull(toByteXml(obj, inputCharset)), Charset.forName(outputCharset)); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static String toXml(Object obj, String charset) { + if(obj == null){ + return null; + } + try { + return new String(toByteXml(obj, GB28181Constant.CHARSET), Charset.forName(charset)); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static String toXml(Object obj) { + if(obj == null){ + return null; + } + try { + return new String(toByteXml(obj, GB28181Constant.CHARSET), GB28181Constant.CHARSET); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static byte[] toByteXml(Object obj) { + try { + return toByteXml(obj, GB28181Constant.CHARSET); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static byte[] toByteXml(Object obj, Charset charset) { + try { + return mapper.writeValueAsString(obj).getBytes(charset); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static byte[] toByteXml(Object obj, String charset) { + if(obj == null){ + return null; + } + try { + return mapper.writeValueAsString(obj).getBytes(Charset.forName(charset)); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @SneakyThrows + public static T parse(byte[] xml, Class clazz) { + return parse(xml, clazz, GB28181Constant.CHARSET); + } + + public static T parse(byte[] xml, Class clazz, String charset) { + return parse(xml, clazz, Charset.forName(charset)); + } + + public static T parse(byte[] xml, Class clazz, Charset charset) { + return parse(new String(xml, charset), clazz); + } + + public static T parse(String xml, Class clazz) { + try { + return mapper.readValue(xml, clazz); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @SneakyThrows + public static T parse(byte[] xml, TypeReference clazz) { + return parse(xml, clazz, GB28181Constant.CHARSET); + } + + public static T parse(byte[] xml, TypeReference clazz, String charset) { + return parse(xml, clazz, Charset.forName(charset)); + } + + public static T parse(byte[] xml, TypeReference clazz, Charset charset) { + return parse(new String(xml, charset), clazz); + } + + public static T parse(String xml, TypeReference clazz) { + try { + return mapper.readValue(xml, clazz); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static T convert(Object object, Class clazz) { + return MANSCDPUtils.convert(object, clazz, GB28181Constant.CHARSET); + } + + @SneakyThrows + public static T convert(Object object, Class clazz, String charset) { + return MANSCDPUtils.convert(object, clazz, Charset.forName(charset)); + } + + @SneakyThrows + public static T convert(Object object, Class clazz, Charset charset) { + if(object == null){ + return null; + } + return MANSCDPUtils.parse(MANSCDPUtils.toXml(object).getBytes(charset), clazz, charset); + } +} diff --git a/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/manscdp/MANSCDPTest.java b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/manscdp/MANSCDPTest.java new file mode 100644 index 0000000..d06dccd --- /dev/null +++ b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/manscdp/MANSCDPTest.java @@ -0,0 +1,68 @@ +package cn.skcks.docking.gb28181.sip.manscdp; + +import cn.skcks.docking.gb28181.sip.manscdp.catalog.CatalogDeviceListDTO; +import cn.skcks.docking.gb28181.sip.manscdp.catalog.CatalogItemDTO; +import cn.skcks.docking.gb28181.sip.manscdp.catalog.CatalogQueryDTO; +import cn.skcks.docking.gb28181.sip.manscdp.catalog.CatalogResponseDTO; +import cn.skcks.docking.gb28181.sip.utils.MANSCDPUtils; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +@Slf4j +public class MANSCDPTest { + public static final String deviceId = "44050100001110000010"; + public static final String channelId = "44050100001310000010"; + public static final String deviceName = "模拟设备名称"; + + @Test + public void catalog(){ + String sn = String.valueOf(1); + // 创建 目录查询 请求 + CatalogQueryDTO catalogQueryDTO = CatalogQueryDTO.builder() + .deviceId("时空旅行者") + .sn(sn) + .build(); + log.info("\n{}", MANSCDPUtils.toXml(catalogQueryDTO)); + + MANSCDPUtils.XMLWrapper query = MANSCDPUtils.XMLWrapper.builder() + .query(catalogQueryDTO) + .build(); + String serialize = MANSCDPUtils.toXml(query); + log.info("\n{}", serialize); + + MANSCDPUtils.XMLWrapper deserializeCatalogQueryDTO = MANSCDPUtils.parse(serialize, new TypeReference<>(){}); + log.info("\n{}",deserializeCatalogQueryDTO); + if (deserializeCatalogQueryDTO == null) { + return; + } + CatalogQueryDTO queryDTO = deserializeCatalogQueryDTO.getQuery(); + log.info("{}",queryDTO); + + // 创建 目录 查询响应 + CatalogItemDTO catalogItemDTO = CatalogItemDTO.builder() + .deviceId(channelId) + .name(deviceName) + .manufacturer("gb28181-docking-platform") + .build(); + CatalogResponseDTO catalogResponseDTO = CatalogResponseDTO.builder() + .deviceId(deviceId) + .sn(sn) + .deviceList(new CatalogDeviceListDTO(1,Collections.singletonList(catalogItemDTO))) + .sumNum(0L) + .build(); + MANSCDPUtils.XMLWrapper response = MANSCDPUtils.XMLWrapper.builder() + .response(catalogResponseDTO) + .build(); + serialize = MANSCDPUtils.toXml(response); + log.info("\n{}", serialize); + MANSCDPUtils.XMLWrapper deserializeCatalogResponseDTO = MANSCDPUtils.parse(serialize, new TypeReference<>(){}); + if (deserializeCatalogResponseDTO == null) { + return; + } + CatalogResponseDTO responseDTO = deserializeCatalogResponseDTO.getResponse(); + log.info("{}",responseDTO); + } +}