| New file |
| | |
| | | HELP.md |
| | | target/ |
| | | !.mvn/wrapper/maven-wrapper.jar |
| | | !**/src/main/**/target/ |
| | | !**/src/test/**/target/ |
| | | |
| | | ### STS ### |
| | | .apt_generated |
| | | .classpath |
| | | .factorypath |
| | | .project |
| | | .settings |
| | | .springBeans |
| | | .sts4-cache |
| | | |
| | | ### IntelliJ IDEA ### |
| | | .idea |
| | | *.iws |
| | | *.iml |
| | | *.ipr |
| | | |
| | | ### NetBeans ### |
| | | /nbproject/private/ |
| | | /nbbuild/ |
| | | /dist/ |
| | | /nbdist/ |
| | | /.nb-gradle/ |
| | | build/ |
| | | !**/src/main/**/build/ |
| | | !**/src/test/**/build/ |
| | | |
| | | ### VS Code ### |
| | | .vscode/ |
| | | /.mvn/ |
| | | /mvnw |
| | | /mvnw.cmd |
| | | /logs/ |
| | | /src/test/ |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | <parent> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-parent</artifactId> |
| | | <version>2.4.11</version> |
| | | <relativePath/> <!-- lookup parent from repository --> |
| | | </parent> |
| | | |
| | | <groupId>com.dji</groupId> |
| | | <artifactId>cloud-api-sample</artifactId> |
| | | <version>1.0.0</version> |
| | | <name>cloud-api-sample</name> |
| | | |
| | | <properties> |
| | | <java.version>11</java.version> |
| | | <mybatis-plus.version>3.4.2</mybatis-plus.version> |
| | | <druid.version>1.2.6</druid.version> |
| | | <jwt.version>3.12.1</jwt.version> |
| | | <mqtt.version>5.5.5</mqtt.version> |
| | | <minio.version>8.3.7</minio.version> |
| | | <okhttp3.version>4.9.1</okhttp3.version> |
| | | <aliyun-sdk-sts.version>3.1.0</aliyun-sdk-sts.version> |
| | | <aliyun-oss.version>3.12.0</aliyun-oss.version> |
| | | <javax-activation.version>1.1.1</javax-activation.version> |
| | | <glassfish-jaxb.version>2.3.3</glassfish-jaxb.version> |
| | | <log4j2.version>2.15.0</log4j2.version> |
| | | <javax-jaxb.version>2.3.0</javax-jaxb.version> |
| | | </properties> |
| | | |
| | | <dependencies> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-web</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>mysql</groupId> |
| | | <artifactId>mysql-connector-java</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>org.projectlombok</groupId> |
| | | <artifactId>lombok</artifactId> |
| | | <optional>true</optional> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-test</artifactId> |
| | | <scope>test</scope> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.baomidou</groupId> |
| | | <artifactId>mybatis-plus-boot-starter</artifactId> |
| | | <version>${mybatis-plus.version}</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.alibaba</groupId> |
| | | <artifactId>druid-spring-boot-starter</artifactId> |
| | | <version>${druid.version}</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.auth0</groupId> |
| | | <artifactId>java-jwt</artifactId> |
| | | <version>${jwt.version}</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>org.springframework.integration</groupId> |
| | | <artifactId>spring-integration-mqtt</artifactId> |
| | | <version>${mqtt.version}</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-websocket</artifactId> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>org.jetbrains</groupId> |
| | | <artifactId>annotations</artifactId> |
| | | <version>RELEASE</version> |
| | | <scope>compile</scope> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>io.minio</groupId> |
| | | <artifactId>minio</artifactId> |
| | | <version>${minio.version}</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.squareup.okhttp3</groupId> |
| | | <artifactId>okhttp</artifactId> |
| | | <version>${okhttp3.version}</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.aliyun</groupId> |
| | | <artifactId>aliyun-java-sdk-sts</artifactId> |
| | | <version>${aliyun-sdk-sts.version}</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.aliyun.oss</groupId> |
| | | <artifactId>aliyun-sdk-oss</artifactId> |
| | | <version>${aliyun-oss.version}</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>javax.xml.bind</groupId> |
| | | <artifactId>jaxb-api</artifactId> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>javax.activation</groupId> |
| | | <artifactId>activation</artifactId> |
| | | <version>${javax-activation.version}</version> |
| | | </dependency> |
| | | <!-- no more than 2.3.3--> |
| | | <dependency> |
| | | <groupId>org.glassfish.jaxb</groupId> |
| | | <artifactId>jaxb-runtime</artifactId> |
| | | <version>${glassfish-jaxb.version}</version> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | <build> |
| | | <plugins> |
| | | <plugin> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-maven-plugin</artifactId> |
| | | <configuration> |
| | | <excludes> |
| | | <exclude> |
| | | <groupId>org.projectlombok</groupId> |
| | | <artifactId>lombok</artifactId> |
| | | </exclude> |
| | | </excludes> |
| | | </configuration> |
| | | </plugin> |
| | | </plugins> |
| | | </build> |
| | | </project> |
| New file |
| | |
| | | CREATE DATABASE IF NOT EXISTS `cloud_sample` /*!40100 DEFAULT CHARACTER SET utf8 */; |
| | | USE `cloud_sample`; |
| | | |
| | | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; |
| | | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; |
| | | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; |
| | | SET NAMES utf8mb4; |
| | | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; |
| | | /*!40101 SET @OLD_SQL_MODE='NO_AUTO_VALUE_ON_ZERO', SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; |
| | | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; |
| | | |
| | | |
| | | # manage_camera_video |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `manage_camera_video`; |
| | | |
| | | CREATE TABLE `manage_camera_video` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `camera_id` int NOT NULL, |
| | | `video_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `video_type` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | PRIMARY KEY (`id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | |
| | | |
| | | # manage_capacity_camera |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `manage_capacity_camera`; |
| | | |
| | | CREATE TABLE `manage_capacity_camera` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', |
| | | `description` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `camera_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `coexist_video_number_max` int NOT NULL, |
| | | `available_video_number` int NOT NULL, |
| | | PRIMARY KEY (`id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | |
| | | |
| | | # manage_device |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `manage_device`; |
| | | |
| | | CREATE TABLE `manage_device` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `device_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', |
| | | `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `device_type` smallint NOT NULL, |
| | | `sub_type` smallint NOT NULL, |
| | | `domain` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `version` smallint NOT NULL, |
| | | `device_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `child_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | `device_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `url_normal` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `url_select` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `product_sn_UNIQUE` (`device_sn`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | |
| | | |
| | | # manage_device_dictionary |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `manage_device_dictionary`; |
| | | |
| | | CREATE TABLE `manage_device_dictionary` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `domain` int NOT NULL, |
| | | `device_type` int NOT NULL, |
| | | `sub_type` int NOT NULL, |
| | | `device_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `device_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | PRIMARY KEY (`id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | LOCK TABLES `manage_device_dictionary` WRITE; |
| | | /*!40000 ALTER TABLE `manage_device_dictionary` DISABLE KEYS */; |
| | | |
| | | INSERT INTO `manage_device_dictionary` (`id`, `domain`, `device_type`, `sub_type`, `device_name`, `device_desc`) |
| | | VALUES |
| | | (1,0,60,0,'Matrice 300 RTK',NULL), |
| | | (16,2,56,0,'DJI Smart Controller','Remote control for M300'), |
| | | (17,1,20,0,'Z30',NULL), |
| | | (18,1,26,0,'XT2',NULL), |
| | | (19,1,39,0,'FPV',NULL), |
| | | (20,1,41,0,'XTS',NULL), |
| | | (21,1,42,0,'H20',NULL), |
| | | (22,1,43,0,'H20T',NULL), |
| | | (24,1,90742,0,'L1',NULL), |
| | | (27,1,50,0,'P1 24mm lens',NULL), |
| | | (28,1,50,1,'P1 35mm lens',NULL), |
| | | (29,1,50,2,'P1 50mm lens',NULL), |
| | | (30,0,67,0,'Matrice 30',NULL), |
| | | (31,0,67,1,'Matrice 30T',NULL), |
| | | (32,2,119,0,'DJI RC Plus','Remote control for M30'), |
| | | (33,1,52,0,'M30',NULL), |
| | | (34,1,53,1,'M30T',NULL); |
| | | |
| | | /*!40000 ALTER TABLE `manage_device_dictionary` ENABLE KEYS */; |
| | | UNLOCK TABLES; |
| | | |
| | | |
| | | # manage_device_payload |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `manage_device_payload`; |
| | | |
| | | CREATE TABLE `manage_device_payload` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `payload_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `payload_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', |
| | | `payload_type` smallint NOT NULL, |
| | | `sub_type` smallint NOT NULL, |
| | | `version` smallint DEFAULT NULL, |
| | | `payload_index` smallint NOT NULL, |
| | | `device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `payload_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `payload_sn_UNIQUE` (`payload_sn`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | |
| | | |
| | | # manage_user |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `manage_user`; |
| | | |
| | | CREATE TABLE `manage_user` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `user_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `workspace_id` int NOT NULL, |
| | | `user_type` smallint NOT NULL, |
| | | `mqtt_username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `mqtt_password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `user_id_UNIQUE` (`user_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | LOCK TABLES `manage_user` WRITE; |
| | | /*!40000 ALTER TABLE `manage_user` DISABLE KEYS */; |
| | | |
| | | INSERT INTO `manage_user` (`id`, `user_id`, `username`, `password`, `workspace_id`, `user_type`, `mqtt_username`, `mqtt_password`, `create_time`, `update_time`) |
| | | VALUES |
| | | (1,'a1559e7c-8dd8-4780-b952-100cc4797da2','adminPC','adminPC',1,1,'admin','admin',1634898410751,1634898410751), |
| | | (2,'be7c6c3d-afe9-4be4-b9eb-c55066c0914e','pilot','pilot123',1,2,'pilot','pilot123',1634898410751,1634898410751); |
| | | |
| | | /*!40000 ALTER TABLE `manage_user` ENABLE KEYS */; |
| | | UNLOCK TABLES; |
| | | |
| | | |
| | | # manage_workspace |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `manage_workspace`; |
| | | |
| | | CREATE TABLE `manage_workspace` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `workspace_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `workspace_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `platform_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `workspace_id_UNIQUE` (`workspace_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | LOCK TABLES `manage_workspace` WRITE; |
| | | /*!40000 ALTER TABLE `manage_workspace` DISABLE KEYS */; |
| | | |
| | | INSERT INTO `manage_workspace` (`id`, `workspace_id`, `workspace_name`, `workspace_desc`, `platform_name`, `create_time`, `update_time`) |
| | | VALUES |
| | | (1,'e3dea0f5-37f2-4d79-ae58-490af3228069','Test Group One','Cloud Sample Test Platform','Cloud Api Platform',1634898410751,1634898410751); |
| | | |
| | | /*!40000 ALTER TABLE `manage_workspace` ENABLE KEYS */; |
| | | UNLOCK TABLES; |
| | | |
| | | |
| | | # map_element_coordinate |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `map_element_coordinate`; |
| | | |
| | | CREATE TABLE `map_element_coordinate` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `element_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `longitude` decimal(18,14) NOT NULL, |
| | | `latitude` decimal(17,14) NOT NULL, |
| | | `altitude` decimal(17,14) DEFAULT NULL, |
| | | PRIMARY KEY (`id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | |
| | | |
| | | # map_group |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `map_group`; |
| | | |
| | | CREATE TABLE `map_group` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `group_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `group_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `group_type` int NOT NULL, |
| | | `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `is_distributed` tinyint(1) NOT NULL DEFAULT '1', |
| | | `is_lock` tinyint(1) NOT NULL DEFAULT '0', |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `group_id_UNIQUE` (`group_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin; |
| | | |
| | | LOCK TABLES `map_group` WRITE; |
| | | /*!40000 ALTER TABLE `map_group` DISABLE KEYS */; |
| | | |
| | | INSERT INTO `map_group` (`id`, `group_id`, `group_name`, `group_type`, `workspace_id`, `is_distributed`, `is_lock`, `create_time`, `update_time`) |
| | | VALUES |
| | | (1,'e3dea0f5-37f2-4d79-ae58-490af3228060','Pilot Share Layer',2,'e3dea0f5-37f2-4d79-ae58-490af3228069',1,0,1638330077356,1638330077356), |
| | | (2,'e3dea0f5-37f2-4d79-ae58-490af3228011','Default Layer',1,'e3dea0f5-37f2-4d79-ae58-490af3228069',1,0,1638330077356,1638330077356); |
| | | |
| | | /*!40000 ALTER TABLE `map_group` ENABLE KEYS */; |
| | | UNLOCK TABLES; |
| | | |
| | | |
| | | # map_group_element |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `map_group_element`; |
| | | |
| | | CREATE TABLE `map_group_element` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `element_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `element_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `display` smallint NOT NULL DEFAULT '1', |
| | | `group_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `element_type` smallint NOT NULL, |
| | | `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `color` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `clamp_to_ground` tinyint(1) NOT NULL DEFAULT '0', |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `element_id_UNIQUE` (`element_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin; |
| | | |
| | | |
| | | |
| | | # media_file |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `media_file`; |
| | | |
| | | CREATE TABLE `media_file` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `file_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `file_path` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `fingerprint` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `tinny_fingerprint` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `object_key` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `sub_file_type` int NOT NULL, |
| | | `is_original` tinyint(1) NOT NULL, |
| | | `drone` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', |
| | | `payload` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `fingerprint_UNIQUE` (`fingerprint`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | |
| | | |
| | | # wayline_file |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `wayline_file`; |
| | | |
| | | CREATE TABLE `wayline_file` ( |
| | | `id` int unsigned NOT NULL AUTO_INCREMENT, |
| | | `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `wayline_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `drone_model_key` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `payload_model_keys` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `favorited` tinyint(1) NOT NULL DEFAULT '0', |
| | | `template_types` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `object_key` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', |
| | | `user_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL COMMENT 'required, can not modify.', |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `wayline_id_UNIQUE` (`wayline_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | |
| | | |
| | | |
| | | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; |
| | | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; |
| | | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; |
| | | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; |
| | | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; |
| | | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; |
| | | |
| New file |
| | |
| | | package com.dji.sample; |
| | | |
| | | import org.mybatis.spring.annotation.MapperScan; |
| | | import org.springframework.boot.SpringApplication; |
| | | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| | | import org.springframework.scheduling.annotation.EnableScheduling; |
| | | |
| | | @MapperScan("com.dji.sample.*.dao") |
| | | @SpringBootApplication |
| | | @EnableScheduling |
| | | public class CloudApiSampleApplication { |
| | | |
| | | public static void main(String[] args) { |
| | | SpringApplication.run(CloudApiSampleApplication.class, args); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.error; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/25 |
| | | */ |
| | | public enum CommonErrorEnum implements IErrorInfo { |
| | | |
| | | SYSTEM_ERROR(600500, "system error"), |
| | | |
| | | SECRET_INVALID(600100, "secret invalid"), |
| | | |
| | | NO_TOKEN(600101, "accss_token is null"), |
| | | |
| | | TOKEN_EXPIRED(600102, "token is expired"), |
| | | |
| | | TOKEN_INVALID(600103, "token invalid"), |
| | | |
| | | SIGN_INVALID(600104, "sign invalid"); |
| | | |
| | | private String msg; |
| | | |
| | | private int code; |
| | | |
| | | CommonErrorEnum(int code, String msg) { |
| | | this.code = code; |
| | | this.msg = msg; |
| | | } |
| | | |
| | | @Override |
| | | public String getErrorMsg() { |
| | | return this.msg; |
| | | } |
| | | |
| | | @Override |
| | | public Integer getErrorCode() { |
| | | return this.code; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.error; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/25 |
| | | */ |
| | | public interface IErrorInfo { |
| | | |
| | | /** |
| | | * Get error message. |
| | | * @return error message |
| | | */ |
| | | String getErrorMsg(); |
| | | |
| | | /** |
| | | * Get error code. |
| | | * @return error code |
| | | */ |
| | | Integer getErrorCode(); |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.error; |
| | | |
| | | /** |
| | | * Live streaming related error codes. When on-demand via mqtt, |
| | | * it can be matched with the error code information replied by the pilot. |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/25 |
| | | */ |
| | | public enum LiveErrorEnum implements IErrorInfo { |
| | | |
| | | NO_AIRCRAFT(613001, "No aircraft."), |
| | | |
| | | NO_CAMERA(613002, "No camera."), |
| | | |
| | | LIVE_STREAM_ALREADY_STARTED(613003, "The camera has started live streaming."), |
| | | |
| | | FUNCTION_NOT_SUPPORT(613004, "The function is not supported."), |
| | | |
| | | STRATEGY_NOT_SUPPORT(613005, "The strategy is not supported."), |
| | | |
| | | NOT_IN_CAMERA_INTERFACE(613006, "The current app is not in the camera interface."), |
| | | |
| | | NO_FLIGHT_CONTROL(613007, "The remote control has no flight control rights and cannot respond to control commands"), |
| | | |
| | | NO_STREAM_DATA(613008, "The current app has no stream data."), |
| | | |
| | | TOO_FREQUENT(613009, "The operation is too frequent."), |
| | | |
| | | ENABLE_FAILED(613010, "Please check whether the live stream service is normal."), |
| | | |
| | | NO_LIVE_STREAM(613011, "There are no live stream currently."), |
| | | |
| | | SWITCH_NOT_SUPPORT(613012, "There is already another camera in the live stream. It's not support to switch the stream directly."), |
| | | |
| | | URL_TYPE_NOT_SUPPORTED(613013, "This url type is not supported."), |
| | | |
| | | ERROR_PARAMETERS(613014, "The live stream parameters are abnormal or incomplete."), |
| | | |
| | | NO_REPLY(613098, "No live reply received."), |
| | | |
| | | UNKNOWN(613099, "UNKNOWN"); |
| | | |
| | | |
| | | private String msg; |
| | | |
| | | private int code; |
| | | |
| | | LiveErrorEnum(int code, String msg) { |
| | | this.code = code; |
| | | this.msg = msg; |
| | | } |
| | | |
| | | @Override |
| | | public String getErrorMsg() { |
| | | return this.msg; |
| | | } |
| | | |
| | | @Override |
| | | public Integer getErrorCode() { |
| | | return this.code; |
| | | } |
| | | |
| | | /** |
| | | * Get the corresponding enumeration object based on the error code. |
| | | * @param code error code |
| | | * @return enumeration object |
| | | */ |
| | | public static LiveErrorEnum find(int code) { |
| | | |
| | | for (LiveErrorEnum errorEnum : LiveErrorEnum.class.getEnumConstants()) { |
| | | if (errorEnum.code == code) { |
| | | return errorEnum; |
| | | } |
| | | } |
| | | return UNKNOWN; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.model; |
| | | |
| | | import com.auth0.jwt.interfaces.Claim; |
| | | import com.fasterxml.jackson.annotation.JsonAlias; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.lang.reflect.Field; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * A custom claim for storing custom information in the token. |
| | | * @author sean.zhou |
| | | * @date 2021/11/16 |
| | | * @version 0.1 |
| | | */ |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Data |
| | | @Slf4j |
| | | public class CustomClaim { |
| | | |
| | | /** |
| | | * The id of the account. |
| | | */ |
| | | private String id; |
| | | |
| | | private String username; |
| | | |
| | | @JsonAlias("user_type") |
| | | private Integer userType; |
| | | |
| | | @JsonAlias("workspace_id") |
| | | private String workspaceId; |
| | | |
| | | /** |
| | | * Convert the custom claim data type to the Map type. |
| | | * @return map |
| | | */ |
| | | public ConcurrentHashMap<String, String> convertToMap() { |
| | | ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(4); |
| | | try { |
| | | Field[] declaredFields = this.getClass().getDeclaredFields(); |
| | | for (Field field : declaredFields) { |
| | | JsonAlias annotation = field.getAnnotation(JsonAlias.class); |
| | | field.setAccessible(true); |
| | | // The value of key is named underscore. |
| | | map.put(annotation != null ? annotation.value()[0] : field.getName(), |
| | | field.get(this).toString()); |
| | | } |
| | | } catch (IllegalAccessException e) { |
| | | log.info("CustomClaim converts failed. {}", this.toString()); |
| | | e.printStackTrace(); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | /** |
| | | * Convert the data in Map into a custom claim object. |
| | | * @param claimMap |
| | | */ |
| | | public CustomClaim (Map<String, Claim> claimMap) { |
| | | Field[] declaredFields = this.getClass().getDeclaredFields(); |
| | | for (Field field : declaredFields) { |
| | | field.setAccessible(true); |
| | | JsonAlias annotation = field.getAnnotation(JsonAlias.class); |
| | | |
| | | Claim value = claimMap.get(annotation == null ? field.getName() : annotation.value()[0]); |
| | | try { |
| | | Class<?> type = field.getType(); |
| | | if (Integer.class.equals(type)) { |
| | | field.set(this, Integer.valueOf(value.asString())); |
| | | continue; |
| | | } |
| | | if (String.class.equals(type)) { |
| | | field.set(this, value.asString()); |
| | | continue; |
| | | } |
| | | } catch (IllegalAccessException e) { |
| | | log.info("Claim parses failed. {}", claimMap.toString()); |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.model; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * Used for paging display in the wayline. These field names cannot be changed. |
| | | * Because they need to be the same as the pilot. |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | @Data |
| | | public class Pagination { |
| | | |
| | | /** |
| | | * The current page number. |
| | | */ |
| | | private long page; |
| | | |
| | | /** |
| | | * The amount of data displayed per page. |
| | | */ |
| | | private long pageSize; |
| | | |
| | | /** |
| | | * The total amount of all data. |
| | | */ |
| | | private long total; |
| | | |
| | | public Pagination(Page page) { |
| | | this.page = page.getCurrent(); |
| | | this.pageSize = page.getSize(); |
| | | this.total = page.getTotal(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * The format of the data response when a paginated display is required. |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | @Data |
| | | public class PaginationData<T> { |
| | | |
| | | /** |
| | | * The collection in which the data list is stored. |
| | | */ |
| | | private List<T> list; |
| | | |
| | | private Pagination pagination; |
| | | |
| | | public PaginationData(List<T> list, Pagination pagination) { |
| | | this.list = list; |
| | | this.pagination = pagination; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.model; |
| | | |
| | | import com.dji.sample.common.error.IErrorInfo; |
| | | import com.fasterxml.jackson.annotation.JsonInclude; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | import org.springframework.http.HttpStatus; |
| | | |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Builder |
| | | @JsonInclude |
| | | public class ResponseResult<T> { |
| | | |
| | | public static final int CODE_SUCCESS = 0; |
| | | public static final String MESSAGE_SUCCESS = "success"; |
| | | |
| | | private int code; |
| | | |
| | | private String message; |
| | | |
| | | private T data; |
| | | |
| | | public static <T> ResponseResult<T> success(T data) { |
| | | return ResponseResult.<T>builder() |
| | | .code(CODE_SUCCESS) |
| | | .message(MESSAGE_SUCCESS) |
| | | .data(data) |
| | | .build(); |
| | | } |
| | | |
| | | public static ResponseResult success() { |
| | | return ResponseResult.builder() |
| | | .code(0) |
| | | .message(MESSAGE_SUCCESS) |
| | | .build(); |
| | | } |
| | | |
| | | public static ResponseResult error() { |
| | | return ResponseResult.builder() |
| | | .code(HttpStatus.INTERNAL_SERVER_ERROR.value()) |
| | | .message(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()) |
| | | .build(); |
| | | } |
| | | |
| | | public static ResponseResult error(String message) { |
| | | return ResponseResult.builder() |
| | | .code(HttpStatus.INTERNAL_SERVER_ERROR.value()) |
| | | .message(message) |
| | | .build(); |
| | | } |
| | | |
| | | public static ResponseResult error(int code, String message) { |
| | | return ResponseResult.builder() |
| | | .code(code) |
| | | .message(message) |
| | | .build(); |
| | | } |
| | | |
| | | public static ResponseResult error(IErrorInfo errorInfo) { |
| | | return ResponseResult.builder() |
| | | .code(errorInfo.getErrorCode()) |
| | | .message(errorInfo.getErrorMsg()) |
| | | .build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.util; |
| | | |
| | | import com.auth0.jwt.JWT; |
| | | import com.auth0.jwt.JWTCreator; |
| | | import com.auth0.jwt.JWTVerifier; |
| | | import com.auth0.jwt.algorithms.Algorithm; |
| | | import com.auth0.jwt.exceptions.TokenExpiredException; |
| | | import com.auth0.jwt.interfaces.DecodedJWT; |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.Date; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class JwtUtil { |
| | | |
| | | private static String issuer; |
| | | |
| | | private static String subject; |
| | | |
| | | private static long age; |
| | | |
| | | private static String secret; |
| | | |
| | | private static Algorithm algorithm; |
| | | |
| | | @Value("${jwt.issuer: DJI}") |
| | | private void setIssuer(String issuer) { |
| | | JwtUtil.issuer = issuer; |
| | | } |
| | | |
| | | @Value("${jwt.subject: CloudApiSample}") |
| | | private void setSubject(String subject) { |
| | | JwtUtil.subject = subject; |
| | | } |
| | | |
| | | @Value("${jwt.age: 86400}") |
| | | private void setAge(long age) { |
| | | JwtUtil.age = age * 1000; |
| | | } |
| | | |
| | | @Value("${jwt.secret: CloudApiSample}") |
| | | private void setSecret(String secret) { |
| | | JwtUtil.secret = secret; |
| | | setAlgorithm(); |
| | | } |
| | | |
| | | private void setAlgorithm() { |
| | | JwtUtil.algorithm = Algorithm.HMAC256(secret); |
| | | } |
| | | |
| | | /** |
| | | * Create a token based on custom information. |
| | | * @param claims custom information |
| | | * @return token |
| | | */ |
| | | public static String createToken(Map<String, String> claims) { |
| | | Date now = new Date(); |
| | | JWTCreator.Builder builder = JWT.create(); |
| | | // Add custom information to the token's payload segment. |
| | | claims.forEach(builder::withClaim); |
| | | String token = builder.withIssuer(issuer) |
| | | .withSubject(subject) |
| | | .withIssuedAt(now) |
| | | .withExpiresAt(new Date(now.getTime() + age)) |
| | | .withNotBefore(now) |
| | | .sign(algorithm); |
| | | log.debug("token created. " + token); |
| | | return token; |
| | | } |
| | | |
| | | /** |
| | | * Verify that the token is valid. |
| | | * @param token |
| | | * @return |
| | | * @throws TokenExpiredException |
| | | */ |
| | | public static DecodedJWT verifyToken(String token) { |
| | | try { |
| | | JWTVerifier verifier = JWT.require(algorithm).build(); |
| | | return verifier.verify(token); |
| | | } catch (Exception e) { |
| | | log.error(e.getMessage()); |
| | | e.printStackTrace(); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Parses the custom information in the token into a CustomClaim object. |
| | | * @param token |
| | | * @return custom claim |
| | | */ |
| | | public static Optional<CustomClaim> parseToken(String token) { |
| | | DecodedJWT jwt = verifyToken(token); |
| | | return jwt == null ? Optional.empty() : Optional.of(new CustomClaim(jwt.getClaims())); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component; |
| | | |
| | | import com.dji.sample.manage.model.DeviceStatusManager; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.boot.CommandLineRunner; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/24 |
| | | * @version 0.1 |
| | | */ |
| | | @Component |
| | | public class ApplicationBootInitial implements CommandLineRunner { |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | /** |
| | | * Subscribe to the devices that exist in the database when the program starts, |
| | | * to prevent the data from being different from the pilot side due to program interruptions. |
| | | * @param args |
| | | * @throws Exception |
| | | */ |
| | | @Override |
| | | public void run(String... args) throws Exception { |
| | | deviceService.getDevicesByParams(DeviceQueryParam.builder().build()) |
| | | .forEach(device -> { |
| | | deviceService.subscribeTopicOnline(device.getDeviceSn()); |
| | | DeviceStatusManager.STATUS_MANAGER.put( |
| | | DeviceDomainEnum.getVal(device.getDomain()) + "/" |
| | | + device.getDeviceSn(), LocalDateTime.now()); |
| | | }); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component; |
| | | |
| | | import com.dji.sample.common.error.CommonErrorEnum; |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.util.JwtUtil; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.http.HttpMethod; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.servlet.HandlerInterceptor; |
| | | import org.springframework.web.servlet.ModelAndView; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.util.Optional; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class AuthInterceptor implements HandlerInterceptor { |
| | | |
| | | public static final String PARAM_TOKEN = "x-auth-token"; |
| | | |
| | | public static final String TOKEN_CLAIM = "customClaim"; |
| | | |
| | | @Override |
| | | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
| | | String uri = request.getRequestURI(); |
| | | log.debug("request uri: {}", uri); |
| | | // The options method is passed directly. |
| | | if (HttpMethod.OPTIONS.matches(request.getMethod())) { |
| | | response.setStatus(HttpStatus.OK.value()); |
| | | return false; |
| | | } |
| | | String token = request.getHeader(PARAM_TOKEN); |
| | | // Check if the token exists. |
| | | if (!StringUtils.hasText(token)) { |
| | | response.setStatus(HttpStatus.UNAUTHORIZED.value()); |
| | | log.error(CommonErrorEnum.NO_TOKEN.getErrorMsg()); |
| | | return false; |
| | | } |
| | | |
| | | // Check if the current token is valid. |
| | | Optional<CustomClaim> customClaimOpt = JwtUtil.parseToken(token); |
| | | if (customClaimOpt.isEmpty()) { |
| | | response.setStatus(HttpStatus.UNAUTHORIZED.value()); |
| | | return false; |
| | | } |
| | | |
| | | // Put the custom data from the token into the request. |
| | | request.setAttribute(TOKEN_CLAIM, customClaimOpt.get()); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { |
| | | // Delete the custom data in the request after the request ends. |
| | | request.removeAttribute(TOKEN_CLAIM); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component; |
| | | |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.servlet.*; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.PARAM_TOKEN; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/22 |
| | | */ |
| | | @Component |
| | | public class CorsFilter implements Filter { |
| | | |
| | | @Override |
| | | public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { |
| | | HttpServletResponse res = (HttpServletResponse) response; |
| | | res.addHeader("Access-Control-Allow-Credentials", "true"); |
| | | res.addHeader("Access-Control-Allow-Origin", "*"); |
| | | res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); |
| | | res.addHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers," + |
| | | "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, "+ |
| | | "Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive," + |
| | | " User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma," + PARAM_TOKEN); |
| | | if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) { |
| | | return; |
| | | } |
| | | filterChain.doFilter(request, response); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import org.springframework.web.bind.annotation.ControllerAdvice; |
| | | import org.springframework.web.bind.annotation.ExceptionHandler; |
| | | import org.springframework.web.bind.annotation.ResponseBody; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/1 |
| | | */ |
| | | @ControllerAdvice |
| | | @ResponseBody |
| | | public class GlobalExceptionHandler { |
| | | |
| | | /** |
| | | * Please do not return directly like this, there is a risk. |
| | | * @param e |
| | | * @return |
| | | */ |
| | | @ExceptionHandler(Exception.class) |
| | | public ResponseResult exceptionHandler(Exception e) { |
| | | e.printStackTrace(); |
| | | return ResponseResult.error(e.getLocalizedMessage()); |
| | | } |
| | | |
| | | @ExceptionHandler(NullPointerException.class) |
| | | public ResponseResult nullPointerExceptionHandler(NullPointerException e) { |
| | | e.printStackTrace(); |
| | | return ResponseResult.error("A null object appeared."); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component; |
| | | |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.Map; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import static com.dji.sample.manage.model.DeviceStatusManager.DEFAULT_ALIVE_SECOND; |
| | | import static com.dji.sample.manage.model.DeviceStatusManager.STATUS_MANAGER; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/24 |
| | | * @version 0.1 |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class GlobalScheduleService { |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | /** |
| | | * Check the status of the devices every 30 seconds. It is recommended to use cache. |
| | | */ |
| | | @Scheduled(fixedRate = 30, timeUnit = TimeUnit.SECONDS) |
| | | private void deviceStatusListen() { |
| | | for (Map.Entry<String, LocalDateTime> entry : STATUS_MANAGER.entrySet()) { |
| | | if (entry.getValue().isAfter( |
| | | LocalDateTime.now().minusSeconds(DEFAULT_ALIVE_SECOND))) { |
| | | continue; |
| | | } |
| | | |
| | | String device = entry.getKey(); |
| | | int index = device.indexOf("/"); |
| | | |
| | | STATUS_MANAGER.remove(device); |
| | | |
| | | int type = Integer.parseInt(device.substring(0, index)); |
| | | String sn = device.substring(index + 1); |
| | | // Determine whether it is a gateway device. |
| | | if (DeviceDomainEnum.GATEWAY.getVal() == type) { |
| | | deviceService.deviceOffline(sn); |
| | | deviceService.unsubscribeTopicOffline(sn); |
| | | continue; |
| | | } |
| | | |
| | | deviceService.subDeviceOffline(sn); |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.config; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.integration.annotation.Router; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.integration.router.AbstractMessageRouter; |
| | | import org.springframework.messaging.Message; |
| | | import org.springframework.messaging.MessageChannel; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.regex.Pattern; |
| | | |
| | | import static com.dji.sample.component.mqtt.model.TopicConst.*; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class InboundMessageRouter extends AbstractMessageRouter { |
| | | |
| | | @Resource(name = ChannelName.INBOUND) |
| | | private MessageChannel inboundChannel; |
| | | |
| | | @Resource(name = ChannelName.INBOUND_STATUS) |
| | | private MessageChannel statusChannel; |
| | | |
| | | @Resource(name = ChannelName.INBOUND_STATE) |
| | | private MessageChannel stateChannel; |
| | | |
| | | @Resource(name = ChannelName.DEFAULT) |
| | | private MessageChannel defaultChannel; |
| | | |
| | | @Resource(name = ChannelName.INBOUND_SERVICE_REPLY) |
| | | private MessageChannel serviceReplyChannel; |
| | | |
| | | @Resource(name = ChannelName.INBOUND_OSD) |
| | | private MessageChannel osdChannel; |
| | | |
| | | private static final Pattern PATTERN_TOPIC_STATUS = |
| | | Pattern.compile("^" + BASIC_PRE + PRODUCT + REGEX_SN + STATUS_SUF + "$"); |
| | | |
| | | private static final Pattern PATTERN_TOPIC_STATE = |
| | | Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + STATE_SUF + "$"); |
| | | |
| | | private static final Pattern PATTERN_TOPIC_SERVICE_REPLY = |
| | | Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + SERVICES_SUF + _REPLY_SUF + "$"); |
| | | |
| | | private static final Pattern PATTERN_TOPIC_OSD = |
| | | Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + OSD_SUF); |
| | | |
| | | /** |
| | | * All mqtt broker messages will arrive here before distributing them to different channels. |
| | | * @param message message from mqtt broker |
| | | * @return channel |
| | | */ |
| | | @Override |
| | | @Router(inputChannel = ChannelName.INBOUND) |
| | | protected Collection<MessageChannel> determineTargetChannels(Message<?> message) { |
| | | MessageHeaders headers = message.getHeaders(); |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); |
| | | byte[] payload = (byte[])message.getPayload(); |
| | | |
| | | // osd |
| | | if (PATTERN_TOPIC_OSD.matcher(topic).matches()) { |
| | | return Collections.singleton(osdChannel); |
| | | } |
| | | |
| | | log.debug("received topic :{} \t payload :{}", topic, new String(payload)); |
| | | |
| | | // status |
| | | if (PATTERN_TOPIC_STATUS.matcher(topic).matches()) { |
| | | return Collections.singleton(statusChannel); |
| | | } |
| | | |
| | | // state |
| | | if (PATTERN_TOPIC_STATE.matcher(topic).matches()) { |
| | | return Collections.singleton(stateChannel); |
| | | } |
| | | |
| | | // services_reply |
| | | if (PATTERN_TOPIC_SERVICE_REPLY.matcher(topic).matches()) { |
| | | return Collections.singleton(serviceReplyChannel); |
| | | } |
| | | return Collections.singleton(defaultChannel); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.config; |
| | | |
| | | import lombok.Data; |
| | | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; |
| | | import org.springframework.integration.mqtt.core.MqttPahoClientFactory; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Configuration |
| | | @Data |
| | | @ConfigurationProperties(prefix = "mqtt") |
| | | public class MqttConfiguration { |
| | | |
| | | private String protocol; |
| | | |
| | | private String host; |
| | | |
| | | private Integer port; |
| | | |
| | | private String username; |
| | | |
| | | private String password; |
| | | |
| | | private String clientId; |
| | | |
| | | /** |
| | | * The topic to subscribe to immediately when client connects. |
| | | */ |
| | | private String inboundTopic; |
| | | |
| | | @Bean |
| | | public MqttConnectOptions mqttConnectOptions() { |
| | | MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); |
| | | mqttConnectOptions.setServerURIs(new String[]{ |
| | | new StringBuilder() |
| | | .append(protocol.trim()) |
| | | .append("://") |
| | | .append(host.trim()) |
| | | .append(":") |
| | | .append(port) |
| | | .toString()}); |
| | | mqttConnectOptions.setUserName(username); |
| | | mqttConnectOptions.setPassword(password.toCharArray()); |
| | | mqttConnectOptions.setAutomaticReconnect(true); |
| | | mqttConnectOptions.setKeepAliveInterval(10); |
| | | return mqttConnectOptions; |
| | | } |
| | | |
| | | @Bean |
| | | public MqttPahoClientFactory mqttClientFactory() { |
| | | DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); |
| | | factory.setConnectionOptions(mqttConnectOptions()); |
| | | return factory; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.config; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.integration.annotation.IntegrationComponentScan; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.endpoint.MessageProducerSupport; |
| | | import org.springframework.integration.mqtt.core.MqttPahoClientFactory; |
| | | import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; |
| | | import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; |
| | | import org.springframework.messaging.MessageChannel; |
| | | import org.springframework.messaging.MessageHandler; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | /** |
| | | * Client configuration for inbound messages. |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Slf4j |
| | | @Configuration |
| | | @IntegrationComponentScan |
| | | public class MqttInboundConfiguration { |
| | | |
| | | @Autowired |
| | | private MqttConfiguration mqttConfiguration; |
| | | |
| | | @Autowired |
| | | private MqttPahoClientFactory mqttClientFactory; |
| | | |
| | | @Resource(name = ChannelName.INBOUND) |
| | | private MessageChannel inboundChannel; |
| | | |
| | | /** |
| | | * Clients of inbound message channels. |
| | | * @return |
| | | */ |
| | | @Bean(name = "adapter") |
| | | public MessageProducerSupport mqttInbound() { |
| | | MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( |
| | | mqttConfiguration.getClientId() + "_consumer_" + System.currentTimeMillis(), |
| | | mqttClientFactory, mqttConfiguration.getInboundTopic().split(",")); |
| | | DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter(); |
| | | // use byte types uniformly |
| | | converter.setPayloadAsBytes(true); |
| | | adapter.setConverter(converter); |
| | | adapter.setQos(1); |
| | | adapter.setOutputChannel(inboundChannel); |
| | | return adapter; |
| | | } |
| | | |
| | | /** |
| | | * Define a default channel to handle messages that have no effect. |
| | | * @return |
| | | */ |
| | | @Bean |
| | | @ServiceActivator(inputChannel = ChannelName.DEFAULT) |
| | | public MessageHandler defaultInboundHandler() { |
| | | return message -> { |
| | | log.info("The default channel does not handle messages."); |
| | | }; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.config; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.integration.channel.DirectChannel; |
| | | import org.springframework.integration.channel.ExecutorChannel; |
| | | import org.springframework.messaging.MessageChannel; |
| | | |
| | | import java.util.concurrent.Executor; |
| | | |
| | | /** |
| | | * Definition classes for all channels |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Configuration |
| | | public class MqttMessageChannel { |
| | | |
| | | @Autowired |
| | | private Executor threadPool; |
| | | |
| | | @Bean(name = ChannelName.INBOUND) |
| | | public MessageChannel inboundChannel() { |
| | | return new ExecutorChannel(threadPool); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_STATUS) |
| | | public MessageChannel statusChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_STATUS_ONLINE) |
| | | public MessageChannel statusOnlineChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_STATUS_OFFLINE) |
| | | public MessageChannel statusOffChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_STATE) |
| | | public MessageChannel stateChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_STATE_BASIC) |
| | | public MessageChannel stateBasicChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_STATE_PAYLOAD) |
| | | public MessageChannel statePayloadChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_SERVICE_REPLY) |
| | | public MessageChannel serviceReplyChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_STATE_CAPACITY) |
| | | public MessageChannel stateCapacityChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE) |
| | | public MessageChannel statePayloadUpdateChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_OSD) |
| | | public MessageChannel osdChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.DEFAULT) |
| | | public MessageChannel defaultChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.OUTBOUND) |
| | | public MessageChannel outboundChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.config; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.core.MqttPahoClientFactory; |
| | | import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; |
| | | import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; |
| | | import org.springframework.messaging.MessageHandler; |
| | | |
| | | /** |
| | | * Client configuration for outbound messages. |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Configuration |
| | | public class MqttOutboundConfiguration { |
| | | |
| | | @Autowired |
| | | private MqttConfiguration mqttConfiguration; |
| | | |
| | | @Autowired |
| | | private MqttPahoClientFactory mqttClientFactory; |
| | | |
| | | /** |
| | | * Clients of outbound message channels. |
| | | * @return |
| | | */ |
| | | @Bean |
| | | @ServiceActivator(inputChannel = ChannelName.OUTBOUND) |
| | | public MessageHandler mqttOutbound() { |
| | | MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler( |
| | | mqttConfiguration.getClientId() + "_producer_" + System.currentTimeMillis(), |
| | | mqttClientFactory); |
| | | DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter(); |
| | | // use byte types uniformly |
| | | converter.setPayloadAsBytes(true); |
| | | |
| | | messageHandler.setAsync(true); |
| | | messageHandler.setDefaultQos(0); |
| | | messageHandler.setConverter(converter); |
| | | return messageHandler; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | /** |
| | | * The name of all channels. |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | public class ChannelName { |
| | | |
| | | public static final String INBOUND = "inbound"; |
| | | |
| | | public static final String INBOUND_STATUS = "inboundStatus"; |
| | | |
| | | public static final String INBOUND_STATUS_ROUTER = "inboundStatusRouter"; |
| | | |
| | | public static final String INBOUND_STATUS_ONLINE = "inboundStatusOnline"; |
| | | |
| | | public static final String INBOUND_STATUS_OFFLINE = "inboundStatusOffline"; |
| | | |
| | | public static final String INBOUND_STATE = "inboundState"; |
| | | |
| | | public static final String INBOUND_STATE_SPLITTER = "inboundStateSplitter"; |
| | | |
| | | public static final String INBOUND_STATE_ROUTER = "inboundStateRouter"; |
| | | |
| | | public static final String INBOUND_STATE_BASIC = "inboundStateBasic"; |
| | | |
| | | public static final String INBOUND_STATE_PAYLOAD = "inboundStatePayload"; |
| | | |
| | | public static final String INBOUND_STATE_PAYLOAD_UPDATE = "inboundStatePayloadUpdate"; |
| | | |
| | | public static final String INBOUND_STATE_CAPACITY = "inboundStateCapacity"; |
| | | |
| | | public static final String INBOUND_STATE_LIST = "inboundStateList"; |
| | | |
| | | public static final String INBOUND_SERVICE_REPLY = "inboundStateServiceReply"; |
| | | |
| | | public static final String INBOUND_OSD = "inboundOsd"; |
| | | |
| | | public static final String DEFAULT = "default"; |
| | | |
| | | public static final String OUTBOUND = "outbound"; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * Unified topic receiving format. |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | public class CommonTopicReceiver<T> { |
| | | |
| | | /** |
| | | * The command is sent and the response is matched by the tid and bid fields in the message, |
| | | * and the reply should keep the tid and bid the same. |
| | | */ |
| | | private String tid; |
| | | |
| | | private String bid; |
| | | |
| | | private String method; |
| | | |
| | | private Long timestamp; |
| | | |
| | | private T data; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * Unified Topic response format |
| | | * @author sean.zhou |
| | | * @date 2021/11/15 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Builder |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | public class CommonTopicResponse<T> { |
| | | |
| | | /** |
| | | * The command is sent and the response is matched by the tid and bid fields in the message, |
| | | * and the reply should keep the tid and bid the same. |
| | | */ |
| | | private String tid; |
| | | |
| | | private String bid; |
| | | |
| | | private String method; |
| | | |
| | | private T data; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | /** |
| | | * All the topics that need to be used in the project. |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | public class TopicConst { |
| | | |
| | | public static final String BASIC_PRE = "sys/"; |
| | | |
| | | public static final String THING_MODEL_PRE = "thing/"; |
| | | |
| | | public static final String PRODUCT = "product/"; |
| | | |
| | | public static final String STATUS_SUF = "/status"; |
| | | |
| | | public static final String _REPLY_SUF = "_reply"; |
| | | |
| | | public static final String STATE_SUF = "/state"; |
| | | |
| | | public static final String SERVICES_SUF = "/services"; |
| | | |
| | | public static final String OSD_SUF = "/osd"; |
| | | |
| | | public static final String REGEX_SN = "[A-Za-z0-9]+"; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * The data format of the state topic. |
| | | * @author sean.zhou |
| | | * @date 2021/11/17 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | public class TopicStateReceiver<T> { |
| | | |
| | | private String tid; |
| | | |
| | | private String bid; |
| | | |
| | | private Long timestamp; |
| | | |
| | | /** |
| | | * The sn of the gateway device. |
| | | */ |
| | | private String gateway; |
| | | |
| | | private T data; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.service; |
| | | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/25 |
| | | */ |
| | | public interface IMessageSenderService { |
| | | |
| | | /** |
| | | * Publish a message to a specific topic. |
| | | * @param topic target |
| | | * @param response message |
| | | */ |
| | | void publish(String topic, CommonTopicResponse response); |
| | | |
| | | /** |
| | | * Use a specific qos to push messages to a specific topic. |
| | | * @param topic target |
| | | * @param qos qos |
| | | * @param response message |
| | | */ |
| | | void publish(String topic, int qos, CommonTopicResponse response); |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.service; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import org.springframework.integration.annotation.MessagingGateway; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.handler.annotation.Header; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Component |
| | | @MessagingGateway(defaultRequestChannel = ChannelName.OUTBOUND) |
| | | public interface IMqttMessageGateway { |
| | | |
| | | /** |
| | | * Publish a message to a specific topic. |
| | | * @param topic target |
| | | * @param payload message |
| | | */ |
| | | void publish(@Header(MqttHeaders.TOPIC) String topic, byte[] payload); |
| | | |
| | | /** |
| | | * Use a specific qos to push messages to a specific topic. |
| | | * @param topic target |
| | | * @param payload message |
| | | * @param qos qos |
| | | */ |
| | | void publish(@Header(MqttHeaders.TOPIC) String topic, byte[] payload, @Header(MqttHeaders.QOS) int qos); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.service; |
| | | |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.handler.annotation.Header; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | public interface IMqttTopicService { |
| | | |
| | | /** |
| | | * Subscribe to a specific topic. |
| | | * @param topic target |
| | | */ |
| | | void subscribe(@Header(MqttHeaders.TOPIC) String topic); |
| | | |
| | | /** |
| | | * Subscribe to a specific topic using a specific qos. |
| | | * @param topic target |
| | | * @param qos qos |
| | | */ |
| | | void subscribe(@Header(MqttHeaders.TOPIC) String topic, int qos); |
| | | |
| | | /** |
| | | * Unsubscribe from a specific topic. |
| | | * @param topic target |
| | | */ |
| | | void unsubscribe(@Header(MqttHeaders.TOPIC) String topic); |
| | | |
| | | /** |
| | | * Get all the subscribed topics. |
| | | * @return topics |
| | | */ |
| | | String[] getSubscribedTopic(); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.mqtt.service.IMqttMessageGateway; |
| | | import com.fasterxml.jackson.annotation.JsonInclude; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/16 |
| | | * @version 0.1 |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | public class MessageSenderServiceImpl implements IMessageSenderService { |
| | | |
| | | @Autowired |
| | | private IMqttMessageGateway messageGateway; |
| | | |
| | | public void publish(String topic, CommonTopicResponse response) { |
| | | try { |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | // Only parameters whose value is not null will be serialised. |
| | | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); |
| | | |
| | | messageGateway.publish(topic, mapper.writeValueAsBytes(response)); |
| | | } catch (JsonProcessingException e) { |
| | | log.info("Failed to publish the message. {}", response.toString()); |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | public void publish(String topic, int qos, CommonTopicResponse response) { |
| | | try { |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | // Only parameters whose value is not null will be serialised. |
| | | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); |
| | | |
| | | messageGateway.publish(topic, mapper.writeValueAsBytes(response), qos); |
| | | } catch (JsonProcessingException e) { |
| | | log.info("Failed to publish the message. {}", response.toString()); |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.service.IMqttTopicService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class MqttTopicServiceImpl implements IMqttTopicService { |
| | | |
| | | @Resource |
| | | private MqttPahoMessageDrivenChannelAdapter adapter; |
| | | |
| | | @Override |
| | | public void subscribe(String topic) { |
| | | log.debug("subscribe topic: {}", topic); |
| | | adapter.addTopic(topic); |
| | | } |
| | | |
| | | @Override |
| | | public void subscribe(String topic, int qos) { |
| | | log.debug("subscribe topic: {}", topic); |
| | | adapter.addTopic(topic, qos); |
| | | } |
| | | |
| | | @Override |
| | | public void unsubscribe(String topic) { |
| | | log.debug("unsubscribe topic: {}", topic); |
| | | adapter.removeTopic(topic); |
| | | } |
| | | |
| | | public String[] getSubscribedTopic() { |
| | | return adapter.getTopic(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mybatis; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.DbType; |
| | | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
| | | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | @Configuration |
| | | public class MybatisPlusConfiguration { |
| | | |
| | | @Bean |
| | | public MybatisPlusInterceptor mybatisPlusInterceptor() { |
| | | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); |
| | | // select database |
| | | interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); |
| | | return interceptor; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mybatis; |
| | | |
| | | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
| | | import org.apache.ibatis.reflection.MetaObject; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneOffset; |
| | | |
| | | /** |
| | | * Automatic filling for set values |
| | | */ |
| | | @Component |
| | | public class MybatisPlusMetaObjectHandler implements MetaObjectHandler { |
| | | |
| | | /** |
| | | * Automatic filling when inserting into the database. |
| | | * @param metaObject |
| | | */ |
| | | @Override |
| | | public void insertFill(MetaObject metaObject) { |
| | | this.strictInsertFill(metaObject, "createTime", Long.class, |
| | | LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli()); |
| | | this.strictInsertFill(metaObject, "updateTime", Long.class, |
| | | LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli()); |
| | | } |
| | | |
| | | /** |
| | | * Automatic filling when updating the data. |
| | | * @param metaObject |
| | | */ |
| | | @Override |
| | | public void updateFill(MetaObject metaObject) { |
| | | this.strictUpdateFill(metaObject, "updateTime", Long.class, |
| | | LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.oss.model; |
| | | |
| | | import com.aliyun.oss.OSS; |
| | | import com.aliyun.oss.OSSClientBuilder; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.context.annotation.Lazy; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | @Configuration |
| | | public class AliyunOSSConfiguration { |
| | | |
| | | /** |
| | | * default |
| | | */ |
| | | public static final String PROVIDER = "ali"; |
| | | |
| | | /** |
| | | * Whether to use the current storage service. |
| | | */ |
| | | public static boolean enable; |
| | | |
| | | /** |
| | | * The protocol needs to be included at the beginning of the address. |
| | | */ |
| | | public static String endpoint; |
| | | |
| | | public static String accessKey; |
| | | |
| | | public static String secretKey; |
| | | |
| | | public static String region; |
| | | |
| | | public static Long expire; |
| | | |
| | | public static String roleSessionName; |
| | | |
| | | public static String roleArn; |
| | | |
| | | public static String bucket; |
| | | |
| | | public static String objectDirPrefix; |
| | | |
| | | @Value("${aliyun.oss.endpoint}") |
| | | private void setEndpoint(String endpoint) { |
| | | AliyunOSSConfiguration.endpoint = endpoint; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.access-key}") |
| | | private void setAccessKey(String accessKey) { |
| | | AliyunOSSConfiguration.accessKey = accessKey; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.secret-key}") |
| | | private void setSecretKey(String secretKey) { |
| | | AliyunOSSConfiguration.secretKey = secretKey; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.region}") |
| | | private void setRegion(String region) { |
| | | AliyunOSSConfiguration.region = region; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.expire: 3600}") |
| | | private void setExpire(Long expire) { |
| | | AliyunOSSConfiguration.expire = expire; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.enable: false}") |
| | | private void setEnable(boolean enable) { |
| | | AliyunOSSConfiguration.enable = enable; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.role-session-name}") |
| | | private void setRoleSessionName(String roleSessionName) { |
| | | AliyunOSSConfiguration.roleSessionName = roleSessionName; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.role-arn}") |
| | | private void setRoleArn(String roleArn) { |
| | | AliyunOSSConfiguration.roleArn = roleArn; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.bucket}") |
| | | private void setBucket(String bucket) { |
| | | AliyunOSSConfiguration.bucket = bucket; |
| | | } |
| | | |
| | | @Value("${aliyun.oss.object-dir-prefix: wayline}") |
| | | private void setObjectDir(String objectDirPrefix) { |
| | | AliyunOSSConfiguration.objectDirPrefix = objectDirPrefix; |
| | | } |
| | | |
| | | @Bean |
| | | @Lazy |
| | | public OSS ossClient() { |
| | | return new OSSClientBuilder().build(endpoint, accessKey, secretKey); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.oss.model; |
| | | |
| | | import io.minio.MinioClient; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/7 |
| | | */ |
| | | @Configuration |
| | | public class MinIOConfiguration { |
| | | |
| | | /** |
| | | * default |
| | | */ |
| | | public static final String PROVIDER = "aws"; |
| | | |
| | | /** |
| | | * Whether to use the current storage service. |
| | | */ |
| | | public static boolean enable; |
| | | |
| | | public static String endpoint; |
| | | |
| | | public static String accessKey; |
| | | |
| | | public static String secretKey; |
| | | |
| | | public static String region; |
| | | |
| | | public static String bucket; |
| | | |
| | | public static Integer expire; |
| | | |
| | | public static String objectDirPrefix; |
| | | |
| | | @Value("${minio.endpoint: http://localhost:9000/}") |
| | | private void setEndpoint(String endpoint) { |
| | | MinIOConfiguration.endpoint = endpoint; |
| | | } |
| | | |
| | | @Value("${minio.access-key: minioadmin}") |
| | | private void setAccessKey(String accessKey) { |
| | | MinIOConfiguration.accessKey = accessKey; |
| | | } |
| | | |
| | | @Value("${minio.secret-key: minioadmin}") |
| | | private void setSecretKey(String secretKey) { |
| | | MinIOConfiguration.secretKey = secretKey; |
| | | } |
| | | |
| | | @Value("${minio.region: }") |
| | | private void setRegion(String region) { |
| | | MinIOConfiguration.region = region; |
| | | } |
| | | |
| | | @Value("${minio.bucket: test}") |
| | | private void setBucket(String bucket) { |
| | | MinIOConfiguration.bucket = bucket; |
| | | } |
| | | |
| | | @Value("${minio.expire: 3600}") |
| | | private void setExpire(Integer expire) { |
| | | MinIOConfiguration.expire = expire; |
| | | } |
| | | |
| | | @Value("${minio.enable: false}") |
| | | private void setEnable(boolean enable) { |
| | | MinIOConfiguration.enable = enable; |
| | | } |
| | | |
| | | @Value("${minio.object-dir-prefix: wayline}") |
| | | private void setObjectDir(String objectDirPrefix) { |
| | | MinIOConfiguration.objectDirPrefix = objectDirPrefix; |
| | | } |
| | | |
| | | @Bean |
| | | @Lazy |
| | | public MinioClient minioClient() { |
| | | MinioClient.Builder builder = MinioClient.builder() |
| | | .endpoint(endpoint) |
| | | .credentials(accessKey, secretKey); |
| | | if (StringUtils.hasText(region)) { |
| | | builder.region(MinIOConfiguration.region); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.oss.service; |
| | | |
| | | import com.dji.sample.media.model.CredentialsDTO; |
| | | |
| | | import java.net.URL; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/23 |
| | | */ |
| | | public interface IOssService { |
| | | |
| | | /** |
| | | * Get temporary credentials. |
| | | * @return |
| | | */ |
| | | CredentialsDTO getCredentials(); |
| | | |
| | | /** |
| | | * Get the address of the object based on the bucket name and the object name. |
| | | * @param bucket bucket name |
| | | * @param objectKey object name |
| | | * @return download link |
| | | */ |
| | | URL getObjectUrl(String bucket, String objectKey); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.oss.service.impl; |
| | | |
| | | import com.aliyun.oss.OSS; |
| | | import com.aliyuncs.DefaultAcsClient; |
| | | import com.aliyuncs.IAcsClient; |
| | | import com.aliyuncs.exceptions.ClientException; |
| | | import com.aliyuncs.profile.DefaultProfile; |
| | | import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest; |
| | | import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse; |
| | | import com.dji.sample.component.oss.model.AliyunOSSConfiguration; |
| | | import com.dji.sample.component.oss.service.IOssService; |
| | | import com.dji.sample.media.model.CredentialsDTO; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.net.URL; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/23 |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | public class AliyunOssServiceImpl implements IOssService { |
| | | |
| | | @Autowired |
| | | private OSS ossClient; |
| | | |
| | | @Override |
| | | public CredentialsDTO getCredentials() { |
| | | |
| | | try { |
| | | DefaultProfile profile = DefaultProfile.getProfile( |
| | | AliyunOSSConfiguration.region, AliyunOSSConfiguration.accessKey, AliyunOSSConfiguration.secretKey); |
| | | IAcsClient client = new DefaultAcsClient(profile); |
| | | |
| | | AssumeRoleRequest request = new AssumeRoleRequest(); |
| | | request.setDurationSeconds(AliyunOSSConfiguration.expire); |
| | | request.setRoleArn(AliyunOSSConfiguration.roleArn); |
| | | request.setRoleSessionName(AliyunOSSConfiguration.roleSessionName); |
| | | |
| | | AssumeRoleResponse response = client.getAcsResponse(request); |
| | | return new CredentialsDTO(response.getCredentials(), AliyunOSSConfiguration.expire); |
| | | |
| | | } catch (ClientException e) { |
| | | log.debug("Failed to obtain sts."); |
| | | e.printStackTrace(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public URL getObjectUrl(String bucket, String objectKey) { |
| | | if (!StringUtils.hasText(bucket) || !StringUtils.hasText(objectKey)) { |
| | | return null; |
| | | } |
| | | // First check if the object can be fetched. |
| | | ossClient.getObject(bucket, objectKey); |
| | | |
| | | return ossClient.generatePresignedUrl(bucket, objectKey, |
| | | new Date(System.currentTimeMillis() + AliyunOSSConfiguration.expire * 1000)); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.oss.service.impl; |
| | | |
| | | import com.dji.sample.component.oss.model.MinIOConfiguration; |
| | | import com.dji.sample.component.oss.service.IOssService; |
| | | import com.dji.sample.media.model.CredentialsDTO; |
| | | import io.minio.GetPresignedObjectUrlArgs; |
| | | import io.minio.MinioClient; |
| | | import io.minio.credentials.AssumeRoleProvider; |
| | | import io.minio.errors.*; |
| | | import io.minio.http.Method; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.io.IOException; |
| | | import java.net.URL; |
| | | import java.security.InvalidKeyException; |
| | | import java.security.NoSuchAlgorithmException; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/23 |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | public class MinIOServiceImpl implements IOssService { |
| | | |
| | | @Autowired |
| | | private MinioClient client; |
| | | |
| | | @Override |
| | | public CredentialsDTO getCredentials() { |
| | | try { |
| | | AssumeRoleProvider provider = new AssumeRoleProvider(MinIOConfiguration.endpoint, MinIOConfiguration.accessKey, |
| | | MinIOConfiguration.secretKey, MinIOConfiguration.expire, |
| | | null, null, null, null, null, null); |
| | | return new CredentialsDTO(provider.fetch(), MinIOConfiguration.expire); |
| | | } catch (NoSuchAlgorithmException e) { |
| | | log.debug("Failed to obtain sts."); |
| | | e.printStackTrace(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public URL getObjectUrl(String bucket, String objectKey) { |
| | | try { |
| | | return new URL( |
| | | client.getPresignedObjectUrl( |
| | | GetPresignedObjectUrlArgs.builder() |
| | | .method(Method.GET) |
| | | .bucket(bucket) |
| | | .object(objectKey) |
| | | .expiry(MinIOConfiguration.expire) |
| | | .build())); |
| | | } catch (ErrorResponseException | InsufficientDataException | InternalException | |
| | | InvalidKeyException | InvalidResponseException | IOException | |
| | | NoSuchAlgorithmException | XmlParserException | ServerException e) { |
| | | log.error("The file does not exist on the oss."); |
| | | e.printStackTrace(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.config; |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.util.JwtUtil; |
| | | import com.dji.sample.component.AuthInterceptor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.http.server.ServerHttpRequest; |
| | | import org.springframework.http.server.ServletServerHttpRequest; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.socket.WebSocketHandler; |
| | | import org.springframework.web.socket.server.support.DefaultHandshakeHandler; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.security.Principal; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/16 |
| | | * @version 0.1 |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class AuthPrincipalHandler extends DefaultHandshakeHandler { |
| | | |
| | | @Override |
| | | protected boolean isValidOrigin(ServerHttpRequest request) { |
| | | |
| | | if (request instanceof ServletServerHttpRequest) { |
| | | HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); |
| | | String token = servletRequest.getParameter(AuthInterceptor.PARAM_TOKEN); |
| | | |
| | | if (!StringUtils.hasText(token)) { |
| | | return false; |
| | | } |
| | | |
| | | Optional<CustomClaim> customClaim = JwtUtil.parseToken(token); |
| | | if (customClaim.isEmpty()) { |
| | | return false; |
| | | } |
| | | |
| | | servletRequest.setAttribute(AuthInterceptor.TOKEN_CLAIM, customClaim.get()); |
| | | return true; |
| | | } |
| | | return false; |
| | | |
| | | } |
| | | |
| | | /** |
| | | * The principal's name: {workspaceId}/{userType}/{userId} |
| | | * @param request |
| | | * @param wsHandler |
| | | * @param attributes |
| | | * @return |
| | | */ |
| | | @Override |
| | | protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) { |
| | | if (request instanceof ServletServerHttpRequest) { |
| | | |
| | | // get the custom claim |
| | | CustomClaim claim = (CustomClaim) ((ServletServerHttpRequest) request).getServletRequest() |
| | | .getAttribute(AuthInterceptor.TOKEN_CLAIM); |
| | | |
| | | return () -> claim.getWorkspaceId() + "/" + claim.getUserType() + "/" + claim.getId(); |
| | | } |
| | | return () -> null; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.config; |
| | | |
| | | import org.springframework.web.socket.WebSocketSession; |
| | | import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/24 |
| | | */ |
| | | public class ConcurrentWebSocketSession extends ConcurrentWebSocketSessionDecorator { |
| | | |
| | | private static final int SEND_BUFFER_SIZE_LIMIT = 1024 * 1024; |
| | | |
| | | private static final int SEND_TIME_LIMIT = 1000; |
| | | |
| | | private ConcurrentWebSocketSession(WebSocketSession delegate, int sendTimeLimit, int bufferSizeLimit) { |
| | | super(delegate, sendTimeLimit, bufferSizeLimit); |
| | | } |
| | | |
| | | ConcurrentWebSocketSession(WebSocketSession delegate) { |
| | | this(delegate, SEND_TIME_LIMIT, SEND_BUFFER_SIZE_LIMIT); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.config; |
| | | |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.socket.WebSocketHandler; |
| | | import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/16 |
| | | * @version 0.1 |
| | | */ |
| | | @Component |
| | | public class WebSocketDefaultFactory implements WebSocketHandlerDecoratorFactory { |
| | | |
| | | @Override |
| | | public WebSocketHandler decorate(WebSocketHandler handler) { |
| | | return new WebSocketDefaultHandler(handler); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.config; |
| | | |
| | | import com.dji.sample.component.websocket.model.WebSocketManager; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.socket.CloseStatus; |
| | | import org.springframework.web.socket.WebSocketHandler; |
| | | import org.springframework.web.socket.WebSocketMessage; |
| | | import org.springframework.web.socket.WebSocketSession; |
| | | import org.springframework.web.socket.handler.WebSocketHandlerDecorator; |
| | | |
| | | import java.security.Principal; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/16 |
| | | * @version 0.1 |
| | | */ |
| | | @Slf4j |
| | | public class WebSocketDefaultHandler extends WebSocketHandlerDecorator { |
| | | |
| | | @Autowired |
| | | private ISendMessageService sendMessageService; |
| | | |
| | | WebSocketDefaultHandler(WebSocketHandler delegate) { |
| | | super(delegate); |
| | | } |
| | | |
| | | @Override |
| | | public void afterConnectionEstablished(WebSocketSession session) throws Exception { |
| | | Principal principal = session.getPrincipal(); |
| | | if (StringUtils.hasText(principal.getName())) { |
| | | WebSocketManager.put(principal.getName(), new ConcurrentWebSocketSession(session)); |
| | | log.debug("{} is connected. ID: {}. WebSocketSession[current count: {}]", |
| | | principal.getName(), session.getId(), WebSocketManager.getConnectedCount()); |
| | | return; |
| | | } |
| | | session.close(); |
| | | } |
| | | |
| | | @Override |
| | | public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { |
| | | Principal principal = session.getPrincipal(); |
| | | if (StringUtils.hasText(principal.getName())) { |
| | | WebSocketManager.remove(principal.getName(), session.getId()); |
| | | log.debug("{} is disconnected. ID: {}. WebSocketSession[current count: {}]", |
| | | principal.getName(), session.getId(), WebSocketManager.getConnectedCount()); |
| | | } |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { |
| | | log.debug("received message: {}", message.getPayload()); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.config; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; |
| | | import org.springframework.web.socket.config.annotation.StompEndpointRegistry; |
| | | import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; |
| | | import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/17 |
| | | * @version 0.1 |
| | | */ |
| | | @EnableWebSocketMessageBroker |
| | | @Configuration |
| | | public class WebSocketMessageConfiguration implements WebSocketMessageBrokerConfigurer { |
| | | |
| | | @Autowired |
| | | private AuthPrincipalHandler authPrincipalHandler; |
| | | |
| | | @Autowired |
| | | private WebSocketDefaultFactory webSocketDefaultFactory; |
| | | |
| | | @Override |
| | | public void registerStompEndpoints(StompEndpointRegistry registry) { |
| | | // Set the WebSocket connection address |
| | | registry.addEndpoint("/api/v1/ws").setAllowedOriginPatterns("*") |
| | | .setHandshakeHandler(authPrincipalHandler); |
| | | } |
| | | |
| | | @Override |
| | | public void configureWebSocketTransport(WebSocketTransportRegistration registry) { |
| | | registry.addDecoratorFactory(webSocketDefaultFactory); |
| | | registry.setTimeToFirstMessage(60000 * 60 * 24 * 10); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.model; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.1 |
| | | * @date 2021/11/26 |
| | | */ |
| | | public enum BizCodeEnum { |
| | | |
| | | DEVICE_ONLINE("device_online"), |
| | | |
| | | DEVICE_OFFLINE("device_offline"), |
| | | |
| | | DEVICE_UPDATE_TOPO("device_update_topo"), |
| | | |
| | | DEVICE_OSD("device_osd"), |
| | | |
| | | GATEWAY_OSD("gateway_osd"), |
| | | |
| | | MAP_ELEMENT_CREATE("map_element_create"), |
| | | |
| | | MAP_ELEMENT_UPDATE("map_element_update"), |
| | | |
| | | MAP_ELEMENT_DELETE("map_element_delete"), |
| | | |
| | | MAP_GROUP_REFRESH("map_group_refresh"); |
| | | |
| | | private String code; |
| | | |
| | | BizCodeEnum(String code) { |
| | | this.code = code; |
| | | } |
| | | |
| | | public String getCode() { |
| | | return code; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.model; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * The format of WebSocket messages that the pilot can receive. |
| | | * @author sean.zhou |
| | | * @date 2021/11/17 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | public class CustomWebSocketMessage<T> { |
| | | |
| | | /** |
| | | * @see BizCodeEnum |
| | | * specific value |
| | | */ |
| | | @JsonProperty("biz_code") |
| | | private String bizCode; |
| | | |
| | | @Builder.Default |
| | | private String version = "1.0"; |
| | | |
| | | private Long timestamp; |
| | | |
| | | private T data; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.model; |
| | | |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.HashSet; |
| | | import java.util.Set; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * Manage all WebSocket connection objects. |
| | | * @author sean.zhou |
| | | * @date 2021/11/16 |
| | | * @version 0.1 |
| | | */ |
| | | @Slf4j |
| | | public class WebSocketManager { |
| | | |
| | | private static final ConcurrentHashMap<String, |
| | | ConcurrentHashMap<String, |
| | | ConcurrentHashMap<String, ConcurrentWebSocketSession>>> MANAGER = new ConcurrentHashMap<>(16); |
| | | |
| | | /** |
| | | * WebSocket connection from the pilot. |
| | | */ |
| | | private static final Set<ConcurrentWebSocketSession> PILOT_SESSION = ConcurrentHashMap.newKeySet(16); |
| | | |
| | | /** |
| | | * WebSocket connection from the web. |
| | | */ |
| | | private static final Set<ConcurrentWebSocketSession> WEB_SESSION = ConcurrentHashMap.newKeySet(16); |
| | | |
| | | public static void put(String key, ConcurrentWebSocketSession val) { |
| | | |
| | | String[] name = key.split("/"); |
| | | if (name.length != 3) { |
| | | log.debug("The key is out of format. [{workspaceId}/{userType}/{userId}]"); |
| | | return; |
| | | } |
| | | |
| | | ConcurrentHashMap<String, ConcurrentHashMap<String, ConcurrentWebSocketSession>> workspaceSessions = |
| | | MANAGER.getOrDefault(name[0], new ConcurrentHashMap<>(16)); |
| | | |
| | | ConcurrentHashMap<String, ConcurrentWebSocketSession> userSessions = workspaceSessions.getOrDefault( |
| | | name[2], new ConcurrentHashMap<>(16)); |
| | | userSessions.put(val.getId(), val); |
| | | workspaceSessions.put(name[2], userSessions); |
| | | MANAGER.put(name[0], workspaceSessions); |
| | | |
| | | getSetByUserType(Integer.valueOf(name[1])).add(val); |
| | | |
| | | } |
| | | |
| | | public static void remove(String key, String sessionId) { |
| | | String[] name = key.split("/"); |
| | | if (name.length != 3) { |
| | | log.debug("The key is out of format. [{workspaceId}/{userType}/{userId}]"); |
| | | return; |
| | | } |
| | | ConcurrentHashMap<String, ConcurrentWebSocketSession> userSession = MANAGER.get(name[0]).get(name[2]); |
| | | |
| | | Set<ConcurrentWebSocketSession> typeSession = getSetByUserType(Integer.valueOf(name[1])); |
| | | |
| | | ConcurrentWebSocketSession session = userSession.get(sessionId); |
| | | typeSession.remove(session); |
| | | userSession.remove(sessionId); |
| | | } |
| | | |
| | | public static int getConnectedCount() { |
| | | return PILOT_SESSION.size() + WEB_SESSION.size(); |
| | | } |
| | | |
| | | public static Collection<ConcurrentWebSocketSession> getValueWithWorkspace(String workspaceId) { |
| | | Set<ConcurrentWebSocketSession> sessions = ConcurrentHashMap.newKeySet(); |
| | | |
| | | MANAGER.get(workspaceId) |
| | | .forEach((userId, userSessions) -> { |
| | | sessions.addAll(userSessions.values()); |
| | | }); |
| | | return sessions; |
| | | } |
| | | |
| | | public static Collection<ConcurrentWebSocketSession> getValueWithWorkspaceAndUserType(String workspaceId, Integer userType) { |
| | | Set<ConcurrentWebSocketSession> sessions = ConcurrentHashMap.newKeySet(); |
| | | Set<ConcurrentWebSocketSession> typeSessions = getSetByUserType(userType); |
| | | |
| | | MANAGER.getOrDefault(workspaceId, new ConcurrentHashMap<>()) |
| | | .forEach((userId, userSessions) -> { |
| | | Collection<ConcurrentWebSocketSession> sessionList = userSessions.values(); |
| | | if (!sessionList.isEmpty()) { |
| | | ConcurrentWebSocketSession session = sessionList.iterator().next(); |
| | | if (typeSessions.contains(session)) { |
| | | sessions.addAll(sessionList); |
| | | } |
| | | } |
| | | }); |
| | | return sessions; |
| | | } |
| | | |
| | | private static Set<ConcurrentWebSocketSession> getSetByUserType(Integer userType) { |
| | | if (UserTypeEnum.PILOT.getVal() == userType) { |
| | | return PILOT_SESSION; |
| | | } |
| | | |
| | | if (UserTypeEnum.WEB.getVal() == userType) { |
| | | return WEB_SESSION; |
| | | } |
| | | return new HashSet<>(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.service; |
| | | |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | |
| | | import java.util.Collection; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/24 |
| | | * @version 0.1 |
| | | */ |
| | | public interface ISendMessageService { |
| | | |
| | | /** |
| | | * Send a message to the specific connection. |
| | | * @param session A WebSocket connection object |
| | | * @param message message |
| | | */ |
| | | void sendMessage(ConcurrentWebSocketSession session, CustomWebSocketMessage message); |
| | | |
| | | /** |
| | | * Send the same message to specific connection. |
| | | * @param sessions A collection of WebSocket connection objects. |
| | | * @param message message |
| | | */ |
| | | void sendBatch(Collection<ConcurrentWebSocketSession> sessions, CustomWebSocketMessage message); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.websocket.service.impl; |
| | | |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.web.socket.TextMessage; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.Collection; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/24 |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | public class SendMessageServiceImpl implements ISendMessageService { |
| | | |
| | | @Override |
| | | public void sendMessage(ConcurrentWebSocketSession session, CustomWebSocketMessage message) { |
| | | if (session == null) { |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | if (!session.isOpen()) { |
| | | session.close(); |
| | | log.debug("This session is closed."); |
| | | return; |
| | | } |
| | | |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | |
| | | session.sendMessage(new TextMessage(mapper.writeValueAsBytes(message))); |
| | | } catch (IOException e) { |
| | | log.info("Failed to publish the message. {}", message.toString()); |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void sendBatch(Collection<ConcurrentWebSocketSession> sessions, CustomWebSocketMessage message) { |
| | | if (sessions.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | TextMessage data = new TextMessage(mapper.writeValueAsBytes(message)); |
| | | |
| | | for (ConcurrentWebSocketSession session : sessions) { |
| | | if (!session.isOpen()) { |
| | | session.close(); |
| | | log.debug("This session is closed."); |
| | | return; |
| | | } |
| | | session.sendMessage(data); |
| | | |
| | | } |
| | | |
| | | } catch (IOException e) { |
| | | log.info("Failed to publish the message. {}", message.toString()); |
| | | |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.configuration; |
| | | |
| | | import com.dji.sample.component.AuthInterceptor; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
| | | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Configuration |
| | | public class GlobalMVCConfigurer implements WebMvcConfigurer { |
| | | |
| | | @Autowired |
| | | private AuthInterceptor authInterceptor; |
| | | |
| | | private static List<String> excludePaths = new ArrayList<>(); |
| | | |
| | | @Value("${url.manage.prefix}") |
| | | private String managePrefix; |
| | | |
| | | @Value("${url.manage.version}") |
| | | private String manageVersion; |
| | | |
| | | @Override |
| | | public void addInterceptors(InterceptorRegistry registry) { |
| | | // Exclude the login interface. |
| | | excludePaths.add(managePrefix + manageVersion + "/login"); |
| | | excludePaths.add(managePrefix + manageVersion + "/token/refresh"); |
| | | // Intercept for all request interfaces. |
| | | registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns(excludePaths); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.configuration; |
| | | |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | import java.util.concurrent.*; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | @Configuration |
| | | public class GlobalThreadPoolConfiguration { |
| | | |
| | | @Value("${thread.pool.core-pool-size: 10}") |
| | | private int corePoolSize; |
| | | |
| | | @Value("${thread.pool.maximum-pool-size: 20}") |
| | | private int maximumPoolSize; |
| | | |
| | | @Value("${thread.pool.keep-alive-time: 60}") |
| | | private long keepAliveTime; |
| | | |
| | | @Value("${thread.pool.queue.capacity: 1000}") |
| | | private int capacity; |
| | | |
| | | /** |
| | | * A custom thread pool. |
| | | * @return |
| | | */ |
| | | @Bean |
| | | public Executor threadPool() { |
| | | return new ThreadPoolExecutor(corePoolSize, |
| | | maximumPoolSize, keepAliveTime, |
| | | TimeUnit.SECONDS, new LinkedBlockingQueue<>(capacity), |
| | | Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.controller; |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.AuthInterceptor; |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.model.WebSocketManager; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.dto.WorkspaceDTO; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.Message; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/15 |
| | | */ |
| | | @RestController |
| | | @Slf4j |
| | | @RequestMapping("${url.manage.prefix}${url.manage.version}/devices") |
| | | public class DeviceController { |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Autowired |
| | | private ISendMessageService sendMessageService; |
| | | |
| | | /** |
| | | * Handles the message that the drone goes online. |
| | | * @param receiver The drone information is not empty. |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_ONLINE, outputChannel = ChannelName.OUTBOUND) |
| | | public void deviceOnline(CommonTopicReceiver<StatusGatewayReceiver> receiver) { |
| | | boolean online = deviceService.deviceOnline(receiver.getData()); |
| | | if (online) { |
| | | // Notify pilot that the drone is online successfully. |
| | | deviceService.publishStatusReply(receiver.getData().getSn(), |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .build()); |
| | | |
| | | // Publish the latest device topology information in the current workspace to the pilot. |
| | | deviceService.pushDeviceOnlineTopo(WorkspaceDTO.DEFAULT_WORKSPACE_ID, |
| | | receiver.getData().getSn(), receiver.getData().getSubDevices().get(0).getSn()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handles the message that the drone goes offline. |
| | | * @param receiver The drone information is empty. |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_OFFLINE, outputChannel = ChannelName.OUTBOUND) |
| | | public void deviceOffline(CommonTopicReceiver<StatusGatewayReceiver> receiver) { |
| | | |
| | | boolean offline = deviceService.deviceOffline(receiver.getData().getSn()); |
| | | if (offline) { |
| | | // Notify pilot that the device is offline successfully. |
| | | deviceService.publishStatusReply(receiver.getData().getSn(), |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .build()); |
| | | |
| | | // Publish the latest device topology information in the current workspace to the pilot. |
| | | deviceService.pushDeviceOfflineTopo(WorkspaceDTO.DEFAULT_WORKSPACE_ID, receiver.getData().getSn()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Get the topology list of all devices in the current user workspace. |
| | | * @param request |
| | | * @return |
| | | */ |
| | | @GetMapping("/devices") |
| | | public ResponseResult<List<DeviceDTO>> getDevices(HttpServletRequest request) { |
| | | // Get information about the current user. |
| | | CustomClaim claim = (CustomClaim)request.getAttribute(AuthInterceptor.TOKEN_CLAIM); |
| | | String workspaceId = claim.getWorkspaceId(); |
| | | // Get information about the devices in the current user's workspace. |
| | | List<DeviceDTO> devicesList = deviceService.getDevicesTopoForWeb(workspaceId); |
| | | |
| | | return ResponseResult.success(devicesList); |
| | | } |
| | | |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD) |
| | | public void osdRealTime(Message<?> message) { |
| | | String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString(); |
| | | byte[] payload = (byte[])message.getPayload(); |
| | | deviceService.handleOSD(topic, payload); |
| | | } |
| | | |
| | | /** |
| | | * Handles the payloads data of the drone. |
| | | * @param deviceSn drone's sn |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE) |
| | | public void pushWebSocketDevices(String deviceSn) { |
| | | List<DeviceDTO> devicesList = deviceService.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .deviceSn(deviceSn) |
| | | .build()); |
| | | // Get drone information based on the sn of the drone. The sn of the drone is unique. |
| | | DeviceDTO device = devicesList.get(0); |
| | | // Set the remote controller and payloads information of the drone. |
| | | deviceService.spliceDeviceTopo(device); |
| | | |
| | | CustomWebSocketMessage wsMessage = CustomWebSocketMessage.builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.DEVICE_UPDATE_TOPO.getCode()) |
| | | .data(device) |
| | | .build(); |
| | | // Update the topology of the drone via WebSocket notifications to the web side. |
| | | sendMessageService.sendBatch(WebSocketManager |
| | | .getValueWithWorkspaceAndUserType( |
| | | device.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | wsMessage); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.controller; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.manage.model.receiver.DeviceBasicReceiver; |
| | | import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; |
| | | import com.dji.sample.manage.service.IDevicePayloadService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | @RestController |
| | | @Slf4j |
| | | public class DevicePayloadController { |
| | | |
| | | @Autowired |
| | | private IDevicePayloadService devicePayloadService; |
| | | |
| | | /** |
| | | * Handles the data for the payload messages in the state topic. |
| | | * @param payloadsList List of payload information. |
| | | * @return drone's sn |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_PAYLOAD, |
| | | outputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE) |
| | | public String statePayload(List<DevicePayloadReceiver> payloadsList) { |
| | | // Delete all payload information for the drone based on the drone's sn. |
| | | devicePayloadService.deletePayloadsByDeviceSn(List.of(payloadsList.get(0).getDeviceSn())); |
| | | |
| | | // Save the new payload information. |
| | | devicePayloadService.savePayloadDTOs(payloadsList); |
| | | |
| | | log.debug("The result of saving the payload is successful."); |
| | | |
| | | return payloadsList.get(0).getDeviceSn(); |
| | | } |
| | | |
| | | /** |
| | | * Handles messages in the state topic about basic drone data. |
| | | * |
| | | * Note: Only the data of the drone payload is handled here. You can handle other data from the drone |
| | | * according to your business needs. |
| | | * @param deviceBasic basic drone data |
| | | * @return drone's sn |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_BASIC, |
| | | outputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE) |
| | | public String stateBasic(DeviceBasicReceiver deviceBasic) { |
| | | // Delete all payload information for the drone based on the drone's sn. |
| | | devicePayloadService.deletePayloadsByDeviceSn(List.of(deviceBasic.getDeviceSn())); |
| | | |
| | | // Save the new payload information. |
| | | boolean isSave = devicePayloadService.savePayloadDTOs(deviceBasic.getPayloads()); |
| | | |
| | | log.debug("The result of saving the payloads is {}.", isSave); |
| | | |
| | | return deviceBasic.getDeviceSn(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.controller; |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.manage.model.Chan; |
| | | import com.dji.sample.manage.model.dto.CapacityDeviceDTO; |
| | | import com.dji.sample.manage.model.dto.LiveTypeDTO; |
| | | import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; |
| | | import com.dji.sample.manage.model.receiver.ServiceReplyReceiver; |
| | | import com.dji.sample.manage.service.ILiveStreamService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.messaging.Message; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.io.IOException; |
| | | import java.util.List; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/19 |
| | | */ |
| | | |
| | | @RestController |
| | | @Slf4j |
| | | @RequestMapping("${url.manage.prefix}${url.manage.version}/live") |
| | | public class LiveStreamController { |
| | | |
| | | @Autowired |
| | | private ILiveStreamService liveStreamService; |
| | | |
| | | /** |
| | | * Analyze the live streaming capabilities of drones. |
| | | * This data is necessary if drones are required for live streaming. |
| | | * @param device the capacity of drone |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_CAPACITY) |
| | | public void stateCapacity(CapacityDeviceReceiver device) { |
| | | boolean parseCapacity = liveStreamService.saveLiveCapacity(device); |
| | | log.debug("The result of parsing the live capacity is {}.", parseCapacity); |
| | | } |
| | | |
| | | /** |
| | | * Get live capability data of all drones in the current user's workspace from the database. |
| | | * @param request |
| | | * @return live capability |
| | | */ |
| | | @GetMapping("/capacity") |
| | | public ResponseResult<List<CapacityDeviceDTO>> getLiveCapacity(HttpServletRequest request) { |
| | | // Get information about the current user. |
| | | CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); |
| | | |
| | | List<CapacityDeviceDTO> liveCapacity = liveStreamService.getLiveCapacity(customClaim.getWorkspaceId()); |
| | | |
| | | return ResponseResult.success(liveCapacity); |
| | | } |
| | | |
| | | /** |
| | | * Live streaming according to the parameters passed in from the web side. |
| | | * @param liveParam Live streaming parameters. |
| | | * @return |
| | | */ |
| | | @PostMapping("/streams/start") |
| | | public ResponseResult liveStart(@RequestBody LiveTypeDTO liveParam) { |
| | | return liveStreamService.liveStart(liveParam); |
| | | } |
| | | |
| | | /** |
| | | * Stop live streaming according to the parameters passed in from the web side. |
| | | * @param liveParam Live streaming parameters. |
| | | * @return |
| | | */ |
| | | @PostMapping("/streams/stop") |
| | | public ResponseResult liveStop(@RequestBody LiveTypeDTO liveParam) { |
| | | return liveStreamService.liveStop(liveParam.getVideoId()); |
| | | } |
| | | |
| | | /** |
| | | * Set the quality of the live streaming according to the parameters passed in from the web side. |
| | | * @param liveParam Live streaming parameters. |
| | | * @return |
| | | */ |
| | | @PostMapping("/streams/update") |
| | | public ResponseResult liveSetQuality(@RequestBody LiveTypeDTO liveParam) { |
| | | return liveStreamService.liveSetQuality(liveParam); |
| | | } |
| | | |
| | | /** |
| | | * Handle the reply message from the pilot side to the on-demand video. |
| | | * @param message reply message |
| | | * @throws IOException |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_SERVICE_REPLY) |
| | | public void serviceReply(Message<?> message) throws IOException { |
| | | byte[] payload = (byte[])message.getPayload(); |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | CommonTopicReceiver<ServiceReplyReceiver> receiver = mapper.readValue(payload, |
| | | new TypeReference<CommonTopicReceiver<ServiceReplyReceiver>>() { |
| | | }); |
| | | Chan<CommonTopicReceiver> chan = Chan.getInstance(); |
| | | // Put the message to the chan object. |
| | | chan.put(receiver); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.controller; |
| | | |
| | | import com.dji.sample.common.error.CommonErrorEnum; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.manage.model.dto.UserDTO; |
| | | import com.dji.sample.manage.model.dto.UserLoginDTO; |
| | | import com.dji.sample.manage.service.IUserService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.util.Optional; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.PARAM_TOKEN; |
| | | |
| | | @RestController |
| | | @RequestMapping("${url.manage.prefix}${url.manage.version}") |
| | | public class LoginController { |
| | | |
| | | @Autowired |
| | | private IUserService userService; |
| | | |
| | | @PostMapping("/login") |
| | | public ResponseResult login(@RequestBody UserLoginDTO loginDTO) { |
| | | String username = loginDTO.getUsername(); |
| | | String password = loginDTO.getPassword(); |
| | | return userService.userLogin(username, password); |
| | | } |
| | | |
| | | @PostMapping("/token/refresh") |
| | | public ResponseResult refreshToken(HttpServletRequest request, HttpServletResponse response) { |
| | | String token = request.getHeader(PARAM_TOKEN); |
| | | Optional<UserDTO> user = userService.refreshToken(token); |
| | | |
| | | if (user.isEmpty()) { |
| | | response.setStatus(HttpStatus.UNAUTHORIZED.value()); |
| | | return ResponseResult.error(CommonErrorEnum.NO_TOKEN.getErrorMsg()); |
| | | } |
| | | |
| | | return ResponseResult.success(user); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.controller; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.manage.model.dto.TopologyDTO; |
| | | import com.dji.sample.manage.service.ITopologyService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/8 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("${url.manage.prefix}${url.manage.version}/workspaces") |
| | | public class TopologyController { |
| | | |
| | | @Autowired |
| | | private ITopologyService topologyService; |
| | | |
| | | /** |
| | | * Get the topology list of all devices in the current user workspace for pilot display. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | @GetMapping("/{workspace_id}/devices/topologies") |
| | | public ResponseResult<Map<String, List<TopologyDTO>>> getDevicesTopologiesForPilot( |
| | | @PathVariable(name = "workspace_id") String workspaceId) { |
| | | List<TopologyDTO> topologyList = topologyService.getDeviceTopology(workspaceId); |
| | | return ResponseResult.success(new ConcurrentHashMap<>(Map.of("list", topologyList))); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.controller; |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.manage.service.IUserService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | | |
| | | @RestController |
| | | @RequestMapping("${url.manage.prefix}${url.manage.version}/users") |
| | | public class UserController { |
| | | |
| | | @Autowired |
| | | private IUserService userService; |
| | | |
| | | @GetMapping("/current") |
| | | public ResponseResult getCurrentUserInfo(HttpServletRequest request) { |
| | | CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); |
| | | return userService.getUserByUsername(customClaim.getUsername(), customClaim.getWorkspaceId()); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.controller; |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.manage.model.dto.WorkspaceDTO; |
| | | import com.dji.sample.manage.service.IWorkspaceService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.util.Optional; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("${url.manage.prefix}${url.manage.version}/workspaces") |
| | | public class WorkspaceController { |
| | | |
| | | @Autowired |
| | | private IWorkspaceService workspaceService; |
| | | |
| | | /** |
| | | * Gets information about the workspace that the current user is in. |
| | | * @param request |
| | | * @return |
| | | */ |
| | | @GetMapping("/current") |
| | | public ResponseResult getCurrentWorkspace(HttpServletRequest request) { |
| | | CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); |
| | | Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(customClaim.getWorkspaceId()); |
| | | |
| | | return workspaceOpt.isEmpty() ? ResponseResult.error() : ResponseResult.success(workspaceOpt.get()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.manage.model.entity.CameraVideoEntity; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/19 |
| | | */ |
| | | public interface ICameraVideoMapper extends BaseMapper<CameraVideoEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.manage.model.entity.CapacityCameraEntity; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | public interface ICapacityCameraMapper extends BaseMapper<CapacityCameraEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.manage.model.entity.DeviceDictionaryEntity; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/15 |
| | | * @version 0.1 |
| | | */ |
| | | public interface IDeviceDictionaryMapper extends BaseMapper<DeviceDictionaryEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.manage.model.entity.DeviceEntity; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | public interface IDeviceMapper extends BaseMapper<DeviceEntity> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.manage.model.entity.DevicePayloadEntity; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | public interface IDevicePayloadMapper extends BaseMapper<DevicePayloadEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.manage.model.entity.UserEntity; |
| | | |
| | | public interface IUserMapper extends BaseMapper<UserEntity> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.manage.model.entity.WorkspaceEntity; |
| | | |
| | | public interface IWorkspaceMapper extends BaseMapper<WorkspaceEntity> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.DeserializationFeature; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/2/21 |
| | | */ |
| | | public abstract class AbstractStateTopicHandler { |
| | | |
| | | protected AbstractStateTopicHandler handler; |
| | | protected static ObjectMapper mapper = new ObjectMapper();; |
| | | |
| | | protected AbstractStateTopicHandler(AbstractStateTopicHandler handler){ |
| | | this.handler = handler; |
| | | mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); |
| | | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
| | | } |
| | | |
| | | /** |
| | | * Passing dataNode data, using different processing methods depending on the data selection. |
| | | * @param dataNode |
| | | * @param stateReceiver |
| | | * @param sn |
| | | * @return |
| | | * @throws JsonProcessingException |
| | | */ |
| | | public abstract TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/3/21 |
| | | */ |
| | | @Service |
| | | public class StateDefaultHandler extends AbstractStateTopicHandler { |
| | | |
| | | protected StateDefaultHandler() { |
| | | super(null); |
| | | } |
| | | |
| | | @Override |
| | | public TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException { |
| | | // If no suitable handler is found for the data, it is not processed. |
| | | return stateReceiver; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.manage.model.receiver.DeviceBasicReceiver; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/2/21 |
| | | */ |
| | | @Service |
| | | public class StateDeviceBasicHandler extends AbstractStateTopicHandler { |
| | | |
| | | public StateDeviceBasicHandler(@Autowired @Qualifier("statePayloadHandler") AbstractStateTopicHandler handler) { |
| | | super(handler); |
| | | } |
| | | |
| | | @Override |
| | | public TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException { |
| | | // handle device basic data |
| | | if (dataNode.size() != 1) { |
| | | DeviceBasicReceiver data = mapper.treeToValue(dataNode, DeviceBasicReceiver.class); |
| | | data.setDeviceSn(sn); |
| | | stateReceiver.setData(data); |
| | | return stateReceiver; |
| | | } |
| | | return handler.handleState(dataNode, stateReceiver, sn); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.manage.model.enums.StateDataEnum; |
| | | import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/2/21 |
| | | */ |
| | | @Service |
| | | public class StateLiveCapacityHandler extends AbstractStateTopicHandler { |
| | | |
| | | private static final String DEVICE_LIST = "device_list"; |
| | | |
| | | protected StateLiveCapacityHandler(@Autowired @Qualifier("stateDefaultHandler") AbstractStateTopicHandler handler) { |
| | | super(handler); |
| | | } |
| | | |
| | | @Override |
| | | public TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException { |
| | | String name = dataNode.fieldNames().next(); |
| | | JsonNode childNode = dataNode.findPath(name); |
| | | // Determine if it is live capacity data based on name. |
| | | if (name.equals(StateDataEnum.LIVE_CAPACITY.getDesc())) { |
| | | JsonNode deviceNode = childNode.findPath(DEVICE_LIST); |
| | | stateReceiver.setData( |
| | | mapper.treeToValue(deviceNode.get(0), CapacityDeviceReceiver.class)); |
| | | return stateReceiver; |
| | | } |
| | | return handler.handleState(dataNode, stateReceiver, sn); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.manage.model.enums.StateDataEnum; |
| | | import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/2/21 |
| | | */ |
| | | @Service |
| | | public class StatePayloadHandler extends AbstractStateTopicHandler { |
| | | |
| | | protected StatePayloadHandler(@Autowired @Qualifier("stateLiveCapacityHandler") AbstractStateTopicHandler handler) { |
| | | super(handler); |
| | | } |
| | | |
| | | @Override |
| | | public TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException { |
| | | String name = dataNode.fieldNames().next(); |
| | | JsonNode childNode = dataNode.findPath(name); |
| | | // Determine if it is payload data based on name. |
| | | if (name.equals(StateDataEnum.PAYLOADS.getDesc())) { |
| | | List<DevicePayloadReceiver> payloadsList = new ArrayList<>(); |
| | | |
| | | Iterator<JsonNode> payloadsNode = childNode.elements(); |
| | | while (payloadsNode.hasNext()) { |
| | | DevicePayloadReceiver payloadReceiver = mapper.treeToValue( |
| | | payloadsNode.next(), DevicePayloadReceiver.class); |
| | | payloadReceiver.setDeviceSn(sn); |
| | | payloadsList.add(payloadReceiver); |
| | | } |
| | | |
| | | if (payloadsList.isEmpty()) { |
| | | DevicePayloadReceiver payloadReceiver = new DevicePayloadReceiver(); |
| | | payloadReceiver.setDeviceSn(sn); |
| | | payloadsList.add(payloadReceiver); |
| | | } |
| | | |
| | | stateReceiver.setData(payloadsList); |
| | | return stateReceiver; |
| | | } |
| | | return handler.handleState(dataNode, stateReceiver, sn); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; |
| | | import com.dji.sample.manage.model.receiver.DeviceBasicReceiver; |
| | | import com.fasterxml.jackson.databind.DeserializationFeature; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.integration.annotation.MessageEndpoint; |
| | | import org.springframework.integration.annotation.Router; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.integration.router.MessageRouter; |
| | | import org.springframework.integration.router.PayloadTypeRouter; |
| | | import org.springframework.messaging.Message; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.io.IOException; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import static com.dji.sample.component.mqtt.model.TopicConst.*; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/17 |
| | | * @version 0.1 |
| | | */ |
| | | @MessageEndpoint |
| | | @Slf4j |
| | | @Configuration |
| | | public class StateRouter { |
| | | |
| | | @Resource(name = "stateDeviceBasicHandler") |
| | | private AbstractStateTopicHandler handler; |
| | | |
| | | /** |
| | | * Handles the routing of state topic messages. Depending on the data, it is assigned to different channels for handling. |
| | | * @param message |
| | | * @return |
| | | * @throws IOException |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE, outputChannel = ChannelName.INBOUND_STATE_SPLITTER) |
| | | public TopicStateReceiver<?> resolveStateData(Message<?> message) throws IOException { |
| | | byte[] payload = (byte[])message.getPayload(); |
| | | String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString(); |
| | | |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); |
| | | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
| | | |
| | | TopicStateReceiver stateReceiver = mapper.readValue(payload, TopicStateReceiver.class); |
| | | // Get the sn of the topic source. |
| | | String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), |
| | | topic.indexOf(STATE_SUF)); |
| | | |
| | | try { |
| | | JsonNode dataNode = mapper.readTree(payload).findPath("data"); |
| | | return handler.handleState(dataNode, stateReceiver, from); |
| | | |
| | | } catch (UnrecognizedPropertyException e) { |
| | | log.info("The {} data is not processed.", e.getPropertyName()); |
| | | } |
| | | return stateReceiver; |
| | | } |
| | | |
| | | @Bean |
| | | @Router(inputChannel = ChannelName.INBOUND_STATE_ROUTER) |
| | | public MessageRouter resolveStateRouter() { |
| | | PayloadTypeRouter router = new PayloadTypeRouter(); |
| | | // // Channel mapping for basic data. |
| | | router.setChannelMapping(DeviceBasicReceiver.class.getName(), |
| | | ChannelName.INBOUND_STATE_BASIC); |
| | | // Channel mapping for live streaming capabilities. |
| | | router.setChannelMapping(CapacityDeviceReceiver.class.getName(), |
| | | ChannelName.INBOUND_STATE_CAPACITY); |
| | | // Channel mapping for payload data. |
| | | router.setChannelMapping(List.class.getName(), |
| | | ChannelName.INBOUND_STATE_PAYLOAD); |
| | | router.setChannelMapping(Map.class.getName(), |
| | | ChannelName.DEFAULT); |
| | | return router; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.integration.annotation.MessageEndpoint; |
| | | import org.springframework.integration.annotation.Splitter; |
| | | import org.springframework.integration.dsl.IntegrationFlow; |
| | | import org.springframework.integration.dsl.IntegrationFlows; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/17 |
| | | * @version 0.1 |
| | | */ |
| | | @MessageEndpoint |
| | | @Configuration |
| | | public class StateSplitter { |
| | | |
| | | /** |
| | | * Split the state message data to different channels for handling according to their different types. |
| | | * @param receiver state message |
| | | * @return |
| | | */ |
| | | @Splitter(inputChannel = ChannelName.INBOUND_STATE_SPLITTER, outputChannel = ChannelName.INBOUND_STATE_ROUTER) |
| | | public Collection<Object> splitState(TopicStateReceiver receiver) { |
| | | ArrayList<Object> type = new ArrayList<>(); |
| | | type.add(receiver.getData()); |
| | | return type; |
| | | } |
| | | |
| | | /** |
| | | * Split according to the different types in the list. |
| | | * @return |
| | | */ |
| | | @Bean |
| | | public IntegrationFlow splitList() { |
| | | return IntegrationFlows |
| | | .from(ChannelName.INBOUND_STATE_LIST) |
| | | .split() |
| | | .<Object, String> route(dataType -> { |
| | | Class<?> clazz = dataType.getClass(); |
| | | if (DevicePayloadReceiver.class.isAssignableFrom(clazz)) { |
| | | return ChannelName.INBOUND_STATE_PAYLOAD; |
| | | } |
| | | return null; |
| | | }, mapping -> { |
| | | mapping.channelMapping(ChannelName.INBOUND_STATE_PAYLOAD, |
| | | ChannelName.INBOUND_STATE_PAYLOAD); |
| | | }) |
| | | .get(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import org.springframework.integration.annotation.MessageEndpoint; |
| | | import org.springframework.integration.annotation.Router; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.Message; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import static com.dji.sample.component.mqtt.model.TopicConst.*; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/12 |
| | | * @version 0.1 |
| | | */ |
| | | @MessageEndpoint |
| | | public class StatusRouter { |
| | | |
| | | /** |
| | | * Converts the status data sent by the gateway device into an object. |
| | | * @param message |
| | | * @return |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS, outputChannel = ChannelName.INBOUND_STATUS_ROUTER) |
| | | public CommonTopicReceiver<StatusGatewayReceiver> resolveStatus(Message<?> message) { |
| | | |
| | | CommonTopicReceiver<StatusGatewayReceiver> statusReceiver = new CommonTopicReceiver<>(); |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); |
| | | try { |
| | | statusReceiver = mapper.readValue( |
| | | (byte[])message.getPayload(), |
| | | new TypeReference<CommonTopicReceiver<StatusGatewayReceiver>>() {}); |
| | | |
| | | String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString(); |
| | | |
| | | // set gateway's domain |
| | | statusReceiver.getData().setDomain(DeviceDomainEnum.GATEWAY.getVal()); |
| | | // set gateway's sn |
| | | statusReceiver.getData().setSn( |
| | | topic.substring((BASIC_PRE + PRODUCT).length(), |
| | | topic.indexOf(STATUS_SUF))); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return statusReceiver; |
| | | } |
| | | |
| | | /** |
| | | * Handles the routing of status topic messages. Depending on the data, it is assigned to different channels for handling. |
| | | * @param receiver |
| | | * @return |
| | | */ |
| | | @Router(inputChannel = ChannelName.INBOUND_STATUS_ROUTER) |
| | | public String resolveStatusRouter(CommonTopicReceiver<StatusGatewayReceiver> receiver) { |
| | | // Determine whether the drone is online or offline according to whether the data of the sub-device is empty. |
| | | return CollectionUtils.isEmpty(receiver.getData().getSubDevices()) ? |
| | | ChannelName.INBOUND_STATUS_OFFLINE : ChannelName.INBOUND_STATUS_ONLINE; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model; |
| | | |
| | | import java.util.concurrent.locks.LockSupport; |
| | | |
| | | /** |
| | | * The demo is only for functional closure, which is not recommended. |
| | | * @author sean.zhou |
| | | * @date 2021/11/22 |
| | | * @version 0.1 |
| | | */ |
| | | public class Chan<T> { |
| | | |
| | | private static final long THREAD_WAIT_TIME = 1000_000 * 500; |
| | | |
| | | private volatile T data; |
| | | |
| | | private volatile Thread t; |
| | | |
| | | private Chan () { |
| | | |
| | | } |
| | | |
| | | public static Chan getInstance() { |
| | | return ChanSingleton.INSTANCE; |
| | | } |
| | | |
| | | public T get(Object blocker) { |
| | | this.t = Thread.currentThread(); |
| | | LockSupport.parkNanos(blocker, THREAD_WAIT_TIME); |
| | | this.t = null; |
| | | return data; |
| | | } |
| | | |
| | | public void put(T data) { |
| | | this.data = data; |
| | | if (t == null) { |
| | | return; |
| | | } |
| | | LockSupport.unpark(t); |
| | | } |
| | | |
| | | private static class ChanSingleton { |
| | | private static final Chan<?> INSTANCE = new Chan<>(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * The demo is only for functional closure, which is not recommended, |
| | | * and it is recommended to use caching for handling. |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/25 |
| | | */ |
| | | public class DeviceStatusManager { |
| | | |
| | | public static final ConcurrentHashMap<String, LocalDateTime> STATUS_MANAGER = |
| | | new ConcurrentHashMap<>(16); |
| | | |
| | | public static final Integer DEFAULT_ALIVE_SECOND = 30; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/22 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class CapacityCameraDTO { |
| | | |
| | | private Integer id; |
| | | |
| | | private String deviceSn; |
| | | |
| | | private String name; |
| | | |
| | | private String description; |
| | | |
| | | private String index; |
| | | |
| | | private String type; |
| | | |
| | | private List<CapacityVideoDTO> videosList; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/22 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class CapacityDeviceDTO { |
| | | |
| | | private String sn; |
| | | |
| | | private String name; |
| | | |
| | | private List<CapacityCameraDTO> camerasList; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/22 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class CapacityVideoDTO { |
| | | |
| | | private Integer id; |
| | | |
| | | private String index; |
| | | |
| | | private String type; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Builder |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class DeviceDTO { |
| | | |
| | | private String deviceSn; |
| | | |
| | | private String deviceName; |
| | | |
| | | private String workspaceId; |
| | | |
| | | private String deviceIndex; |
| | | |
| | | private String deviceDesc; |
| | | |
| | | private String childDeviceSn; |
| | | |
| | | private String domain; |
| | | |
| | | private Integer type; |
| | | |
| | | private Integer subType; |
| | | |
| | | private List<DeviceDTO> gatewaysList; |
| | | |
| | | private List<DevicePayloadDTO> payloadsList; |
| | | |
| | | private IconUrlDTO iconUrl; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @Data |
| | | public class DeviceDictionaryDTO { |
| | | |
| | | private Integer domain; |
| | | |
| | | private Integer deviceType; |
| | | |
| | | private Integer subType; |
| | | |
| | | private String deviceName; |
| | | |
| | | private String deviceDesc; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/8 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class DeviceModelDTO { |
| | | |
| | | private String key; |
| | | |
| | | private String domain; |
| | | |
| | | private String type; |
| | | |
| | | private String subType; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class DevicePayloadDTO { |
| | | |
| | | private String payloadSn; |
| | | |
| | | private String payloadName; |
| | | |
| | | private Integer payloadIndex; |
| | | |
| | | private String payloadDesc; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/1/5 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class IconUrlDTO { |
| | | |
| | | @JsonProperty("normal_icon_url") |
| | | private String normalUrl; |
| | | |
| | | @JsonProperty("selected_icon_url") |
| | | private String selectUrl; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonInclude; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @Data |
| | | @JsonInclude(JsonInclude.Include.NON_NULL) |
| | | public class LiveDTO { |
| | | |
| | | private String url; |
| | | |
| | | private String username; |
| | | |
| | | private String password; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * Receive live parameters. |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/22 |
| | | */ |
| | | @Data |
| | | public class LiveTypeDTO { |
| | | |
| | | @JsonProperty("url_type") |
| | | private Integer urlType; |
| | | |
| | | private String url; |
| | | |
| | | @JsonProperty("video_id") |
| | | private String videoId; |
| | | |
| | | @JsonProperty("video_quality") |
| | | private Integer videoQuality; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/23 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | public class LiveUrlAgoraDTO { |
| | | |
| | | private String channel; |
| | | |
| | | private String sn; |
| | | |
| | | private String token; |
| | | |
| | | private Integer uid; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @Data |
| | | public class LiveUrlGB28181DTO { |
| | | |
| | | private String serverIP; |
| | | |
| | | private Integer serverPort; |
| | | |
| | | private String serverID; |
| | | |
| | | private String agentID; |
| | | |
| | | private String agentPassword; |
| | | |
| | | private Integer localPort; |
| | | |
| | | private String channel; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @Data |
| | | public class LiveUrlRTSPDTO { |
| | | |
| | | private String userName; |
| | | |
| | | private String password; |
| | | |
| | | private Integer port; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/8 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class TelemetryDTO { |
| | | |
| | | private TelemetryDeviceDTO host; |
| | | |
| | | private String sn; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/8 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class TelemetryDeviceDTO { |
| | | |
| | | private Double latitude; |
| | | |
| | | private Double longitude; |
| | | |
| | | private Double altitude; |
| | | |
| | | private Float attitudeHead; |
| | | |
| | | private Double elevation; |
| | | |
| | | private Float horizontalSpeed; |
| | | |
| | | private Float verticalSpeed; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/8 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class TopologyDTO { |
| | | |
| | | private List<TopologyDeviceDTO> hosts; |
| | | |
| | | private List<TopologyDeviceDTO> parents; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonInclude; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/8 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @JsonInclude(JsonInclude.Include.NON_NULL) |
| | | public class TopologyDeviceDTO { |
| | | |
| | | private String sn; |
| | | |
| | | private DeviceModelDTO deviceModel; |
| | | |
| | | @Builder.Default |
| | | private Boolean onlineStatus = true; |
| | | |
| | | private String deviceCallsign; |
| | | |
| | | private String userId; |
| | | |
| | | private String userCallsign; |
| | | |
| | | private IconUrlDTO iconUrls; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class UserDTO { |
| | | |
| | | @JsonProperty("user_id") |
| | | private String userId; |
| | | |
| | | private String username; |
| | | |
| | | @JsonProperty("workspace_id") |
| | | private String workspaceId; |
| | | |
| | | @JsonProperty("user_type") |
| | | private Integer userType; |
| | | |
| | | @JsonProperty("mqtt_username") |
| | | private String mqttUsername; |
| | | |
| | | @JsonProperty("mqtt_password") |
| | | private String mqttPassword; |
| | | |
| | | @JsonProperty("access_token") |
| | | private String accessToken; |
| | | |
| | | @JsonProperty("mqtt_addr") |
| | | private String mqttAddr; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.Data; |
| | | import lombok.NonNull; |
| | | |
| | | @Data |
| | | public class UserLoginDTO { |
| | | |
| | | @NonNull |
| | | private String username; |
| | | |
| | | @NonNull |
| | | private String password; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/22 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Builder |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class WorkspaceDTO { |
| | | |
| | | public static final String DEFAULT_WORKSPACE_ID = "e3dea0f5-37f2-4d79-ae58-490af3228069"; |
| | | |
| | | private Integer id; |
| | | |
| | | private String workspaceId; |
| | | |
| | | private String workspaceName; |
| | | |
| | | private String workspaceDesc; |
| | | |
| | | private String platformName; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | @TableName(value = "manage_camera_video") |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Builder |
| | | public class CameraVideoEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField(value = "camera_id") |
| | | private Integer cameraId; |
| | | |
| | | @TableField(value = "video_index") |
| | | private String videoIndex; |
| | | |
| | | @TableField(value = "video_type") |
| | | private String videoType; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @TableName(value = "manage_capacity_camera") |
| | | public class CapacityCameraEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField(value = "device_sn") |
| | | private String deviceSn; |
| | | |
| | | @TableField(value = "name") |
| | | private String name; |
| | | |
| | | @TableField(value = "description") |
| | | private String description; |
| | | |
| | | @TableField(value = "camera_index") |
| | | private String cameraIndex; |
| | | |
| | | @TableField(value = "coexist_video_number_max") |
| | | private Integer coexistVideoNumberMax; |
| | | |
| | | @TableField(value = "available_video_number") |
| | | private Integer availableVideoNumber; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/15 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @TableName("manage_device_dictionary") |
| | | public class DeviceDictionaryEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField(value = "domain") |
| | | private Integer domain; |
| | | |
| | | @TableField(value = "device_type") |
| | | private Integer deviceType; |
| | | |
| | | @TableField(value = "sub_type") |
| | | private Integer subType; |
| | | |
| | | @TableField(value = "device_name") |
| | | private String deviceName; |
| | | |
| | | @TableField(value = "device_desc") |
| | | private String deviceDesc; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * The entity class of the device |
| | | * |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/10 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @TableName(value = "manage_device") |
| | | public class DeviceEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField(value = "device_sn") |
| | | private String deviceSn; |
| | | |
| | | @TableField(value = "device_name") |
| | | private String deviceName; |
| | | |
| | | @TableField(value = "workspace_id") |
| | | private String workspaceId; |
| | | |
| | | @TableField(value = "device_type") |
| | | private Integer deviceType; |
| | | |
| | | @TableField(value = "sub_type") |
| | | private Integer subType; |
| | | |
| | | @TableField(value = "domain") |
| | | private Integer domain; |
| | | |
| | | @TableField(value = "version") |
| | | private Integer version; |
| | | |
| | | @TableField(value = "device_index") |
| | | private String deviceIndex; |
| | | |
| | | @TableField(value = "child_sn") |
| | | private String childSn; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | |
| | | @TableField(value = "device_desc") |
| | | private String deviceDesc; |
| | | |
| | | @TableField(value = "url_normal") |
| | | private String urlNormal; |
| | | |
| | | @TableField(value = "url_select") |
| | | private String urlSelect; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @TableName(value = "manage_device_payload") |
| | | public class DevicePayloadEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField(value = "payload_sn") |
| | | private String payloadSn; |
| | | |
| | | @TableField(value = "payload_name") |
| | | private String payloadName; |
| | | |
| | | @TableField(value = "payload_type") |
| | | private Integer payloadType; |
| | | |
| | | @TableField(value = "sub_type") |
| | | private Integer subType; |
| | | |
| | | @TableField(value = "version") |
| | | private Integer version; |
| | | |
| | | @TableField(value = "payload_index") |
| | | private Integer payloadIndex; |
| | | |
| | | @TableField(value = "device_sn") |
| | | private String deviceSn; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | |
| | | @TableField(value = "payload_desc") |
| | | private String payloadDesc; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | @TableName(value = "manage_user") |
| | | @Data |
| | | public class UserEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField(value = "user_id") |
| | | private String userId; |
| | | |
| | | @TableField(value = "username") |
| | | private String username; |
| | | |
| | | @TableField(value = "password") |
| | | private String password; |
| | | |
| | | @TableField(value = "workspace_id") |
| | | private Integer workspaceId; |
| | | |
| | | @TableField(value = "user_type") |
| | | private Integer userType; |
| | | |
| | | @TableField(value = "mqtt_username") |
| | | private String mqttUsername; |
| | | |
| | | @TableField(value = "mqtt_password") |
| | | private String mqttPassword; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | @TableName(value = "manage_workspace") |
| | | @Data |
| | | public class WorkspaceEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField(value = "workspace_id") |
| | | private String workspaceId; |
| | | |
| | | @TableField(value = "workspace_name") |
| | | private String workspaceName; |
| | | |
| | | @TableField(value = "workspace_desc") |
| | | private String workspaceDesc; |
| | | |
| | | @TableField(value = "platform_name") |
| | | private String platformName; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/15 |
| | | * @version 0.1 |
| | | */ |
| | | public enum DeviceDomainEnum { |
| | | |
| | | SUB_DEVICE(0, "sub-device"), |
| | | |
| | | GATEWAY(2, "gateway"), |
| | | |
| | | PAYLOAD(1, "payload"), |
| | | |
| | | UNKNOWN(-1, "unknown"); |
| | | |
| | | private int val; |
| | | |
| | | private String desc; |
| | | |
| | | DeviceDomainEnum(int val, String desc) { |
| | | this.val = val; |
| | | this.desc = desc; |
| | | } |
| | | |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | public static String getDesc(int val) { |
| | | if (SUB_DEVICE.val == val) { |
| | | return SUB_DEVICE.desc; |
| | | } |
| | | |
| | | if (GATEWAY.val == val) { |
| | | return GATEWAY.desc; |
| | | } |
| | | |
| | | if (PAYLOAD.val == val) { |
| | | return PAYLOAD.desc; |
| | | } |
| | | return UNKNOWN.desc; |
| | | } |
| | | |
| | | public static int getVal(String desc) { |
| | | if (SUB_DEVICE.desc.equals(desc)) { |
| | | return SUB_DEVICE.val; |
| | | } |
| | | |
| | | if (GATEWAY.desc.equals(desc)) { |
| | | return GATEWAY.val; |
| | | } |
| | | |
| | | if (PAYLOAD.desc.equals(desc)) { |
| | | return PAYLOAD.val; |
| | | } |
| | | return UNKNOWN.val; |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | /** |
| | | * The system icon that comes with the pilot. |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/1/5 |
| | | */ |
| | | public enum IconUrlEnum { |
| | | |
| | | SELECT_CAR("resource://pilot/drawable/tsa_car_select"), |
| | | |
| | | NORMAL_CAR("resource://pilot/drawable/tsa_car_normal"), |
| | | |
| | | SELECT_PERSON("resource://pilot/drawable/tsa_person_select"), |
| | | |
| | | NORMAL_PERSON("resource://pilot/drawable/tsa_person_normal"), |
| | | |
| | | SELECT_EQUIPMENT("resource://pilot/drawable/tsa_equipment_select"), |
| | | |
| | | NORMAL_EQUIPMENT("resource://pilot/drawable/tsa_equipment_normal"); |
| | | |
| | | /** |
| | | * You can use icons from the web, and the App internally downloads and caches these icons and |
| | | * loads them at a fixed size (28dp) to display on the map. |
| | | * Example: http://r56978dr7.hn-bkt.clouddn.com/tsa_equipment_normal.png |
| | | */ |
| | | private String url; |
| | | |
| | | IconUrlEnum(String url) { |
| | | this.url = url; |
| | | } |
| | | |
| | | public String getUrl() { |
| | | return url; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/22 |
| | | * @version 0.1 |
| | | */ |
| | | public enum LiveMethodEnum { |
| | | |
| | | LIVE_START_PUSH("live_start_push"), |
| | | |
| | | LIVE_STOP_PUSH("live_stop_push"), |
| | | |
| | | LIVE_SET_QUALITY("live_set_quality"), |
| | | |
| | | UNKNOWN("unknown"); |
| | | |
| | | private String method; |
| | | |
| | | LiveMethodEnum(String method) { |
| | | this.method = method; |
| | | } |
| | | |
| | | public String getMethod() { |
| | | return method; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/22 |
| | | */ |
| | | public enum LiveUrlTypeEnum { |
| | | |
| | | AGORA(0), |
| | | |
| | | RTMP(1), |
| | | |
| | | RTSP(2), |
| | | |
| | | GB28181(3), |
| | | |
| | | UNKNOWN(-1); |
| | | |
| | | private int val; |
| | | |
| | | LiveUrlTypeEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | public static LiveUrlTypeEnum find(Integer val) { |
| | | if (AGORA.val == val) { |
| | | return AGORA; |
| | | } |
| | | if (RTMP.val == val) { |
| | | return RTMP; |
| | | } |
| | | if (RTSP.val == val) { |
| | | return RTSP; |
| | | } |
| | | if (GB28181.val == val) { |
| | | return GB28181; |
| | | } |
| | | return UNKNOWN; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.1 |
| | | * @date 2021/11/26 |
| | | */ |
| | | public enum LiveVideoQualityEnum { |
| | | |
| | | AUTO (0), |
| | | |
| | | SMOOTH(1), |
| | | |
| | | STANDARD_DEFINITION(2), |
| | | |
| | | HIGH_DEFINITION(3), |
| | | |
| | | ULTRA_HD(4), |
| | | |
| | | UNKNOWN(-1); |
| | | |
| | | private int val; |
| | | |
| | | LiveVideoQualityEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | public static LiveVideoQualityEnum find(int val) { |
| | | if (AUTO.val == val) { |
| | | return AUTO; |
| | | } |
| | | |
| | | if (SMOOTH.val == val) { |
| | | return SMOOTH; |
| | | } |
| | | |
| | | if (STANDARD_DEFINITION.val == val) { |
| | | return STANDARD_DEFINITION; |
| | | } |
| | | |
| | | if (HIGH_DEFINITION.val == val) { |
| | | return HIGH_DEFINITION; |
| | | } |
| | | |
| | | if (ULTRA_HD.val == val) { |
| | | return ULTRA_HD; |
| | | } |
| | | |
| | | return UNKNOWN; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/18 |
| | | * @version 0.1 |
| | | */ |
| | | public enum StateDataEnum { |
| | | |
| | | BATTERIES("batteries"), |
| | | |
| | | LIVE_CAPACITY("live_capacity"), |
| | | |
| | | PAYLOADS("payloads"); |
| | | |
| | | private String desc; |
| | | |
| | | StateDataEnum(String desc) { |
| | | this.desc = desc; |
| | | } |
| | | |
| | | public String getDesc() { |
| | | return this.desc; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/2 |
| | | */ |
| | | public enum UserTypeEnum { |
| | | |
| | | WEB(1), |
| | | |
| | | PILOT(2); |
| | | |
| | | private int val; |
| | | |
| | | |
| | | UserTypeEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.param; |
| | | |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * The object of the device query field. |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/16 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | public class DeviceQueryParam { |
| | | |
| | | private String deviceSn; |
| | | |
| | | private String workspaceId; |
| | | |
| | | private Integer deviceType; |
| | | |
| | | private Integer subType; |
| | | |
| | | private Integer domain; |
| | | |
| | | private String childSn; |
| | | |
| | | private boolean orderBy; |
| | | |
| | | private boolean isAsc; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/1/27 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class BatteryReceiver { |
| | | |
| | | private List<BatteryStateReceiver> batteries; |
| | | |
| | | private Integer capacityPercent; |
| | | |
| | | private Integer landingPower; |
| | | |
| | | private Integer remainFlightTime; |
| | | |
| | | private Integer returnHomePower; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/24 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class BatteryStateReceiver { |
| | | |
| | | private String firmwareVersion; |
| | | |
| | | private Integer index; |
| | | |
| | | private Integer loopTimes; |
| | | |
| | | private Integer capacityPercent; |
| | | |
| | | private String sn; |
| | | |
| | | private Integer subType; |
| | | |
| | | private Float temperature; |
| | | |
| | | private Integer type; |
| | | |
| | | private Integer voltage; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/18 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class CapacityCameraReceiver { |
| | | |
| | | private Integer availableVideoNumber; |
| | | |
| | | private Integer coexistVideoNumberMax; |
| | | |
| | | private String cameraIndex; |
| | | |
| | | @JsonProperty(value = "video_list") |
| | | private List<CapacityVideoReceiver> videosList; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/18 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class CapacityDeviceReceiver { |
| | | |
| | | private String sn; |
| | | |
| | | private Integer availableVideoNumber; |
| | | |
| | | private Integer coexistVideoNumberMax; |
| | | |
| | | private List<CapacityCameraReceiver> cameraList; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/18 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class CapacityVideoReceiver { |
| | | |
| | | private String videoIndex; |
| | | |
| | | private String videoType; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/18 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class DeviceBasicReceiver { |
| | | |
| | | private String controlSource; |
| | | |
| | | private String deviceSn; |
| | | |
| | | private Integer flightMode; |
| | | |
| | | private Integer flightStatus; |
| | | |
| | | private Double homeLatitude; |
| | | |
| | | private Double homeLongitude; |
| | | |
| | | private Integer lowBatteryWarningThreshold; |
| | | |
| | | private Integer positionMode; |
| | | |
| | | private Integer seriousLowBatteryWarningThreshold; |
| | | |
| | | private List<DevicePayloadReceiver> payloads; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/18 |
| | | * @version 0.1 |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class DevicePayloadReceiver { |
| | | |
| | | private String deviceSn; |
| | | |
| | | private String controlSource; |
| | | |
| | | private String payloadIndex; |
| | | |
| | | private String sn; |
| | | |
| | | private Integer version; |
| | | |
| | | private Integer workMode; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class LiveStatusReceiver { |
| | | |
| | | private Long liveTime; |
| | | |
| | | private Integer liveTrendline; |
| | | |
| | | private String videoId; |
| | | |
| | | private Integer videoQuality; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class OsdGatewayReceiver { |
| | | |
| | | private Double latitude; |
| | | |
| | | private Double longitude; |
| | | |
| | | @JsonProperty("capacity_percent") |
| | | private Integer remainPower; |
| | | |
| | | private Integer transmissionSignalQuality; |
| | | |
| | | private LiveStatusReceiver liveStatus; |
| | | |
| | | private WirelessLinkStateReceiver wirelessLinkState; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class OsdSubDeviceReceiver { |
| | | |
| | | private Float attitudeHead; |
| | | |
| | | private Double attitudePitch; |
| | | |
| | | private Double attitudeRoll; |
| | | |
| | | private Double elevation; |
| | | |
| | | private BatteryReceiver battery; |
| | | |
| | | private String firmwareVersion; |
| | | |
| | | private Integer gear; |
| | | |
| | | private Double height; |
| | | |
| | | private Double homeDistance; |
| | | |
| | | private Float horizontalSpeed; |
| | | |
| | | private Double latitude; |
| | | |
| | | private Double longitude; |
| | | |
| | | private Integer modeCode; |
| | | |
| | | private Double totalFlightDistance; |
| | | |
| | | private Double totalFlightTime; |
| | | |
| | | private Float verticalSpeed; |
| | | |
| | | private Double windDirection; |
| | | |
| | | private Double windSpeed; |
| | | |
| | | private PositionStateReceiver positionState; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/1/27 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class PositionStateReceiver { |
| | | |
| | | private Integer gpsNumber; |
| | | |
| | | private Integer isFixed; |
| | | |
| | | private Integer quality; |
| | | |
| | | private Integer rtkNumber; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/24 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class RTKStateReceiver { |
| | | |
| | | private Integer bdsNumber; |
| | | |
| | | private Integer galNumber; |
| | | |
| | | private Integer gloNumber; |
| | | |
| | | private Integer gpsNumber; |
| | | |
| | | private Boolean isFixed; |
| | | |
| | | private Integer quality; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/22 |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | public class ServiceReplyReceiver<T> { |
| | | |
| | | private Integer result; |
| | | |
| | | private String info; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/12 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | public class StatusGatewayReceiver { |
| | | |
| | | private String sn; |
| | | |
| | | private Integer domain; |
| | | |
| | | private Integer type; |
| | | |
| | | @JsonProperty(value = "sub_type") |
| | | private Integer subType; |
| | | |
| | | @JsonProperty(value = "device_secret") |
| | | private String deviceSecret; |
| | | |
| | | private String nonce; |
| | | |
| | | private Integer version; |
| | | |
| | | @JsonProperty(value = "sub_devices") |
| | | private List<StatusSubDeviceReceiver> subDevices; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/12 |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | public class StatusSubDeviceReceiver { |
| | | |
| | | private String sn; |
| | | |
| | | private Integer type; |
| | | |
| | | @JsonProperty(value = "sub_type") |
| | | private Integer subType; |
| | | |
| | | private String index; |
| | | |
| | | @JsonProperty(value = "device_secret") |
| | | private String deviceSecret; |
| | | |
| | | private String nonce; |
| | | |
| | | private Integer version; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/23 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class WirelessLinkStateReceiver { |
| | | |
| | | private Integer downloadQuality; |
| | | |
| | | private Integer frequencyBand; |
| | | |
| | | private Integer upwardQuality; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.manage.model.dto.CapacityVideoDTO; |
| | | import com.dji.sample.manage.model.receiver.CapacityVideoReceiver; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | public interface ICameraVideoService { |
| | | |
| | | /** |
| | | * Queries all lens data contained in the camera based on camera id. |
| | | * @param cameraId |
| | | * @return |
| | | */ |
| | | List<CapacityVideoDTO> getCameraVideosByCameraId(Integer cameraId); |
| | | |
| | | /** |
| | | * Deletes all the data of this lens, according to the lens id. |
| | | * @param ids A collection of lens ids. |
| | | * @return true |
| | | */ |
| | | Boolean deleteCameraVideosById(List<Integer> ids); |
| | | |
| | | /** |
| | | * Save the live capability of all lenses of this camera. |
| | | * @param capacityVideoReceivers live capability of lens |
| | | * @param cameraId |
| | | * @return |
| | | */ |
| | | Boolean saveCameraVideoDTOList(List<CapacityVideoReceiver> capacityVideoReceivers, Integer cameraId); |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.manage.model.dto.CapacityCameraDTO; |
| | | import com.dji.sample.manage.model.receiver.CapacityCameraReceiver; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | public interface ICapacityCameraService { |
| | | |
| | | /** |
| | | * Query all camera data that can be live streamed from this device based on the device sn. |
| | | * @param deviceSn |
| | | * @return |
| | | */ |
| | | List<CapacityCameraDTO> getCapacityCameraByDeviceSn(String deviceSn); |
| | | |
| | | /** |
| | | * Query whether this camera data has been saved based on the device sn and camera location. |
| | | * @param deviceSn |
| | | * @param cameraIndex |
| | | * @return |
| | | */ |
| | | Boolean checkExist(String deviceSn, String cameraIndex); |
| | | |
| | | /** |
| | | * Delete all live capability data for this device based on the device sn. |
| | | * @param deviceSn |
| | | * @return |
| | | */ |
| | | Boolean deleteCapacityCameraByDeviceSn(String deviceSn); |
| | | |
| | | /** |
| | | * Save the live capability data of the device. |
| | | * @param capacityCameraReceivers |
| | | * @param deviceSn |
| | | * @return |
| | | */ |
| | | Boolean saveCapacityCameraReceiverList(List<CapacityCameraReceiver> capacityCameraReceivers, String deviceSn); |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/15 |
| | | */ |
| | | public interface IDeviceDictionaryService { |
| | | |
| | | /** |
| | | * Query the type data of the device based on domain, device type and sub type. |
| | | * @param domain |
| | | * @param deviceType |
| | | * @param subType |
| | | * @return |
| | | */ |
| | | Optional<DeviceDictionaryDTO> getOneDictionaryInfoByDomainTypeSubType( |
| | | Integer domain, Integer deviceType, Integer subType); |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.manage.model.dto.DevicePayloadDTO; |
| | | import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | public interface IDevicePayloadService { |
| | | |
| | | /** |
| | | * Query if the payload has been saved based on the sn of the payload. |
| | | * @param payloadSn |
| | | * @return |
| | | */ |
| | | Integer checkPayloadExist(String payloadSn); |
| | | |
| | | /** |
| | | * Save all payload data. |
| | | * @param payloadReceiverList |
| | | * @return |
| | | */ |
| | | Boolean savePayloadDTOs(List<DevicePayloadReceiver> payloadReceiverList); |
| | | |
| | | /** |
| | | * Save a payload data. |
| | | * @param payloadReceiver |
| | | * @return |
| | | */ |
| | | Integer saveOnePayloadDTO(DevicePayloadReceiver payloadReceiver); |
| | | |
| | | /** |
| | | * Query all payload data on this device based on the device sn. |
| | | * @param deviceSn |
| | | * @return |
| | | */ |
| | | List<DevicePayloadDTO> getDevicePayloadEntitiesByDeviceSn(String deviceSn); |
| | | |
| | | /** |
| | | * Delete all payload data on these devices based on the collection of device sns. |
| | | * @param deviceSns |
| | | */ |
| | | void deletePayloadsByDeviceSn(List<String> deviceSns); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.dto.TopologyDeviceDTO; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/10 |
| | | * @version 0.1 |
| | | */ |
| | | public interface IDeviceService { |
| | | |
| | | /** |
| | | * The device goes online. |
| | | * @param deviceGateway gateway |
| | | * @return Whether the online is successful. |
| | | */ |
| | | Boolean deviceOnline(StatusGatewayReceiver deviceGateway); |
| | | |
| | | /** |
| | | * The device goes offline. |
| | | * @param gatewaySn |
| | | * @return Whether the offline is successful. |
| | | */ |
| | | Boolean deviceOffline(String gatewaySn); |
| | | |
| | | /** |
| | | * The aircraft goes offline. |
| | | * @param deviceSn aircraft's SN |
| | | * @return Whether the offline is successful. |
| | | */ |
| | | Boolean subDeviceOffline(String deviceSn); |
| | | |
| | | /** |
| | | * When the device goes online, it needs to subscribe to topics. |
| | | * @param sn device's SN |
| | | */ |
| | | void subscribeTopicOnline(String sn); |
| | | |
| | | /** |
| | | * When the device goes offine, it needs to cancel the subscribed topics. |
| | | * @param sn device's SN |
| | | */ |
| | | void unsubscribeTopicOffline(String sn); |
| | | |
| | | /** |
| | | * Delete all device data according to the SN of the device. |
| | | * @param ids device's SN |
| | | * @return |
| | | */ |
| | | Boolean delDeviceByDeviceSns(List<String> ids); |
| | | |
| | | /** |
| | | * Obtain device data according to different query conditions. |
| | | * @param param query parameters |
| | | * @return |
| | | */ |
| | | List<DeviceDTO> getDevicesByParams(DeviceQueryParam param); |
| | | |
| | | /** |
| | | * When you receive a status topic message, you need to reply to it. |
| | | * @param sn the target of sn |
| | | * @param response |
| | | */ |
| | | void publishStatusReply(String sn, CommonTopicResponse<Object> response); |
| | | |
| | | /** |
| | | * The business interface on the web side. Get all information about all devices in this workspace. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | List<DeviceDTO> getDevicesTopoForWeb(String workspaceId); |
| | | |
| | | /** |
| | | * Set the remote controller and payloads information of the drone. |
| | | * @param device |
| | | */ |
| | | void spliceDeviceTopo(DeviceDTO device); |
| | | |
| | | /** |
| | | * Push the topology information to the pilot after one device is online. |
| | | * @param sessions The collection of connection objects on the pilot side. |
| | | * @param sn |
| | | */ |
| | | void pushDeviceOnlineTopo(Collection<ConcurrentWebSocketSession> sessions, String sn); |
| | | |
| | | /** |
| | | * Query the information of the device according to the sn of the device. |
| | | * @param sn device sn |
| | | * @return |
| | | */ |
| | | Optional<TopologyDeviceDTO> getDeviceTopoForPilot(String sn); |
| | | |
| | | /** |
| | | * Convert individual device information into topology objects. |
| | | * @param device |
| | | * @return |
| | | */ |
| | | TopologyDeviceDTO deviceConvertToTopologyDTO(DeviceDTO device); |
| | | |
| | | /** |
| | | * When the server receives the request of any device online, offline and topology update in the same workspace, |
| | | * it also broadcasts a push of device online, offline and topology update to PILOT via websocket, |
| | | * and PILOT will get the device topology list again after receiving the push. |
| | | * @param workspaceId |
| | | * @param gatewaySn |
| | | */ |
| | | void pushDeviceOfflineTopo(String workspaceId, String gatewaySn); |
| | | |
| | | /** |
| | | * When the server receives the request of any device online, offline and topology update in the same workspace, |
| | | * it also broadcasts a push of device online, offline and topology update to PILOT via websocket, |
| | | * and PILOT will get the device topology list again after receiving the push. |
| | | * @param workspaceId |
| | | * @param deviceSn |
| | | * @param gatewaySn |
| | | */ |
| | | void pushDeviceOnlineTopo(String workspaceId, String deviceSn, String gatewaySn); |
| | | |
| | | /** |
| | | * Handle messages from the osd topic. |
| | | * @param topic osd |
| | | * @param payload |
| | | */ |
| | | void handleOSD(String topic, byte[] payload); |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.manage.model.dto.CapacityDeviceDTO; |
| | | import com.dji.sample.manage.model.dto.LiveTypeDTO; |
| | | import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | public interface ILiveStreamService { |
| | | |
| | | /** |
| | | * Get all the drone data that can be broadcast live in this workspace. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | List<CapacityDeviceDTO> getLiveCapacity(String workspaceId); |
| | | |
| | | /** |
| | | * Save live capability data from drone. |
| | | * @param capacityDeviceReceiver |
| | | * @return |
| | | */ |
| | | Boolean saveLiveCapacity(CapacityDeviceReceiver capacityDeviceReceiver); |
| | | |
| | | /** |
| | | * Initiate a live streaming by publishing mqtt message. |
| | | * @param liveParam Parameters needed for on-demand. |
| | | * @return |
| | | */ |
| | | ResponseResult liveStart(LiveTypeDTO liveParam); |
| | | |
| | | /** |
| | | * Stop the live streaming by publishing mqtt message. |
| | | * @param videoId |
| | | * @return |
| | | */ |
| | | ResponseResult liveStop(String videoId); |
| | | |
| | | /** |
| | | * Readjust the clarity of the live streaming by publishing mqtt messages. |
| | | * @param liveParam |
| | | * @return |
| | | */ |
| | | ResponseResult liveSetQuality(LiveTypeDTO liveParam); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/2/21 |
| | | */ |
| | | public interface ITSAService { |
| | | |
| | | /** |
| | | * Real-time push telemetry data. |
| | | * @param workspaceId |
| | | * @param osdData |
| | | * @param sn |
| | | */ |
| | | void pushTelemetryData(String workspaceId, Object osdData, String sn); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.manage.model.dto.TopologyDTO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/8 |
| | | */ |
| | | public interface ITopologyService { |
| | | |
| | | /** |
| | | * Get the topology list of all devices in the workspace for pilot display. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | List<TopologyDTO> getDeviceTopology(String workspaceId); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.manage.model.dto.UserDTO; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | public interface IUserService { |
| | | |
| | | /** |
| | | * Query user's details based on username. |
| | | * @param username |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | ResponseResult getUserByUsername(String username, String workspaceId); |
| | | |
| | | /** |
| | | * Verify the username and password to log in. |
| | | * @param username |
| | | * @param password |
| | | * @return |
| | | */ |
| | | ResponseResult userLogin(String username, String password); |
| | | |
| | | /** |
| | | * Create a user object containing a new token. |
| | | * @param token |
| | | * @return |
| | | */ |
| | | Optional<UserDTO> refreshToken(String token); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | |
| | | import com.dji.sample.manage.model.dto.WorkspaceDTO; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | public interface IWorkspaceService { |
| | | |
| | | /** |
| | | * Query the workspace information based on the primary key id of the database. |
| | | * @param id primary key id |
| | | * @return |
| | | */ |
| | | Optional<WorkspaceDTO> getWorkspaceById(int id); |
| | | |
| | | /** |
| | | * Query the information of a workspace based on its workspace id. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | Optional<WorkspaceDTO> getWorkspaceByWorkspaceId(String workspaceId); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.model.WebSocketManager; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.manage.model.dto.TelemetryDTO; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.service.ITSAService; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | |
| | | import java.util.Collection; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/2/21 |
| | | */ |
| | | public abstract class AbstractTSAService implements ITSAService { |
| | | |
| | | protected AbstractTSAService tsaService; |
| | | |
| | | public AbstractTSAService(AbstractTSAService tsaService) { |
| | | this.tsaService = tsaService; |
| | | } |
| | | |
| | | @Autowired |
| | | protected ISendMessageService sendMessageService; |
| | | |
| | | @Override |
| | | public void pushTelemetryData(String workspaceId, Object osdData, String sn) { |
| | | // All connected accounts on the pilot side of this workspace. |
| | | Collection<ConcurrentWebSocketSession> pilotSessions = WebSocketManager |
| | | .getValueWithWorkspaceAndUserType( |
| | | workspaceId, UserTypeEnum.PILOT.getVal()); |
| | | |
| | | TelemetryDTO telemetry = TelemetryDTO.builder() |
| | | .sn(sn) |
| | | .build(); |
| | | CustomWebSocketMessage<TelemetryDTO> pilotMessage = CustomWebSocketMessage.<TelemetryDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.DEVICE_OSD.getCode()) |
| | | .data(telemetry) |
| | | .build(); |
| | | |
| | | this.pushTelemetryData(pilotSessions, pilotMessage, osdData); |
| | | } |
| | | |
| | | public abstract void pushTelemetryData(Collection<ConcurrentWebSocketSession> sessions, |
| | | CustomWebSocketMessage<TelemetryDTO> message, Object Object); |
| | | |
| | | protected abstract void handleOSD(TopicStateReceiver receiver, String sn, String workspaceId, JsonNode hostNode, |
| | | Collection<ConcurrentWebSocketSession> webSessions, CustomWebSocketMessage wsMessage) |
| | | throws JsonProcessingException; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.manage.dao.ICameraVideoMapper; |
| | | import com.dji.sample.manage.model.dto.CapacityVideoDTO; |
| | | import com.dji.sample.manage.model.entity.CameraVideoEntity; |
| | | import com.dji.sample.manage.model.receiver.CapacityVideoReceiver; |
| | | import com.dji.sample.manage.service.ICameraVideoService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/19 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class CameraVideoServiceImpl implements ICameraVideoService { |
| | | |
| | | @Autowired |
| | | private ICameraVideoMapper mapper; |
| | | |
| | | @Override |
| | | public List<CapacityVideoDTO> getCameraVideosByCameraId(Integer cameraId) { |
| | | return mapper.selectList( |
| | | new LambdaQueryWrapper<CameraVideoEntity>() |
| | | .eq(CameraVideoEntity::getCameraId, cameraId)) |
| | | .stream() |
| | | .map(this::entityConvertToDto) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean deleteCameraVideosById(List<Integer> ids) { |
| | | if (ids.isEmpty()) { |
| | | return true; |
| | | } |
| | | return mapper.deleteBatchIds(ids) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean saveCameraVideoDTOList(List<CapacityVideoReceiver> capacityVideoReceivers, Integer cameraId) { |
| | | for (CapacityVideoReceiver videoDTO : capacityVideoReceivers) { |
| | | CameraVideoEntity videoEntity = videoDTOConvertToEntity(videoDTO); |
| | | videoEntity.setCameraId(cameraId); |
| | | int saveId = this.saveOneCameraVideoEntity(videoEntity); |
| | | if (saveId <= 0) { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Save the live capability of the lens of this camera. |
| | | * @param entity lens data |
| | | * @return |
| | | */ |
| | | private Integer saveOneCameraVideoEntity(CameraVideoEntity entity) { |
| | | return mapper.insert(entity) > 0 ? entity.getId() : 0; |
| | | } |
| | | |
| | | /** |
| | | * Convert the received lens capability object into a database entity object. |
| | | * @param dto received lens object |
| | | * @return entity |
| | | */ |
| | | private CameraVideoEntity videoDTOConvertToEntity(CapacityVideoReceiver dto) { |
| | | CameraVideoEntity.CameraVideoEntityBuilder builder = CameraVideoEntity.builder(); |
| | | if (dto != null) { |
| | | builder |
| | | .videoIndex(dto.getVideoIndex()) |
| | | .videoType(dto.getVideoType()); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into lens data transfer object. |
| | | * @param entity |
| | | * @return data transfer object |
| | | */ |
| | | private CapacityVideoDTO entityConvertToDto(CameraVideoEntity entity) { |
| | | CapacityVideoDTO.CapacityVideoDTOBuilder builder = CapacityVideoDTO.builder(); |
| | | |
| | | if (entity != null) { |
| | | builder |
| | | .id(entity.getId()) |
| | | .index(entity.getVideoIndex()) |
| | | .type(entity.getVideoType()); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.manage.dao.ICapacityCameraMapper; |
| | | import com.dji.sample.manage.model.dto.CapacityCameraDTO; |
| | | import com.dji.sample.manage.model.dto.CapacityVideoDTO; |
| | | import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; |
| | | import com.dji.sample.manage.model.entity.CapacityCameraEntity; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.receiver.CapacityCameraReceiver; |
| | | import com.dji.sample.manage.service.ICameraVideoService; |
| | | import com.dji.sample.manage.service.ICapacityCameraService; |
| | | import com.dji.sample.manage.service.IDeviceDictionaryService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/19 |
| | | * @version 0.1 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class CapacityCameraServiceImpl implements ICapacityCameraService { |
| | | |
| | | @Autowired |
| | | private ICapacityCameraMapper mapper; |
| | | |
| | | @Autowired |
| | | private ICameraVideoService cameraVideoService; |
| | | |
| | | @Autowired |
| | | private IDeviceDictionaryService dictionaryService; |
| | | |
| | | @Override |
| | | public List<CapacityCameraDTO> getCapacityCameraByDeviceSn(String deviceSn) { |
| | | List<CapacityCameraDTO> capacityCamerasList = mapper.selectList( |
| | | new LambdaQueryWrapper<CapacityCameraEntity>() |
| | | .eq(CapacityCameraEntity::getDeviceSn, deviceSn)) |
| | | .stream() |
| | | .map(this::entityConvertToDto) |
| | | .collect(Collectors.toList()); |
| | | capacityCamerasList.forEach(capacityCamera -> { |
| | | // Set the lens data for this camera. |
| | | capacityCamera.setVideosList( |
| | | cameraVideoService.getCameraVideosByCameraId(capacityCamera.getId())); |
| | | }); |
| | | |
| | | return capacityCamerasList; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean checkExist(String deviceSn, String cameraIndex) { |
| | | return mapper.selectOne( |
| | | new LambdaQueryWrapper<CapacityCameraEntity>() |
| | | .eq(CapacityCameraEntity::getDeviceSn, deviceSn) |
| | | .eq(CapacityCameraEntity::getCameraIndex, cameraIndex) |
| | | .last(" limit 1")) != null; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean deleteCapacityCameraByDeviceSn(String deviceSn) { |
| | | |
| | | List<CapacityCameraDTO> capacityCamerasList = this.getCapacityCameraByDeviceSn(deviceSn); |
| | | // Return directly if no data exists in the database. |
| | | if (capacityCamerasList.isEmpty()) { |
| | | return true; |
| | | } |
| | | |
| | | List<Integer> cameraIds = capacityCamerasList |
| | | .stream() |
| | | .map(CapacityCameraDTO::getId) |
| | | .collect(Collectors.toList()); |
| | | |
| | | List<Integer> videoIds = capacityCamerasList |
| | | .stream() |
| | | .flatMap(camera -> camera.getVideosList().stream()) |
| | | .map(CapacityVideoDTO::getId) |
| | | .collect(Collectors.toList()); |
| | | |
| | | return mapper.deleteBatchIds(cameraIds) > 0 && cameraVideoService.deleteCameraVideosById(videoIds); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean saveCapacityCameraReceiverList(List<CapacityCameraReceiver> capacityCameraReceivers, String deviceSn) { |
| | | for (CapacityCameraReceiver cameraDTO : capacityCameraReceivers) { |
| | | CapacityCameraEntity cameraEntity = receiverConvertToEntity(cameraDTO); |
| | | cameraEntity.setDeviceSn(deviceSn); |
| | | int cameraId = this.saveOneCapacityCameraEntity(cameraEntity); |
| | | if (cameraId <= 0) { |
| | | continue; |
| | | } |
| | | boolean saveVideo = cameraVideoService.saveCameraVideoDTOList( |
| | | cameraDTO.getVideosList(), cameraId); |
| | | |
| | | if (!saveVideo) { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Save the camera live capability data of the device. |
| | | * @param cameraEntity |
| | | * @return |
| | | */ |
| | | private Integer saveOneCapacityCameraEntity(CapacityCameraEntity cameraEntity) { |
| | | boolean exist = checkExist( |
| | | cameraEntity.getDeviceSn(), cameraEntity.getCameraIndex()); |
| | | if (exist) { |
| | | return -1; |
| | | } |
| | | return mapper.insert(cameraEntity) > 0 ? cameraEntity.getId() : 0; |
| | | } |
| | | |
| | | /** |
| | | * Convert the received camera capability object into a database entity object. |
| | | * @param receiver |
| | | * @return |
| | | */ |
| | | private CapacityCameraEntity receiverConvertToEntity(CapacityCameraReceiver receiver) { |
| | | CapacityCameraEntity.CapacityCameraEntityBuilder builder = CapacityCameraEntity.builder(); |
| | | if (receiver == null) { |
| | | return builder.build(); |
| | | } |
| | | int[] indexArr = Arrays.stream(receiver.getCameraIndex().split("-")) |
| | | .map(Integer::valueOf) |
| | | .mapToInt(Integer::intValue) |
| | | .toArray(); |
| | | // The cameraIndex consists of type and subType and the index of the payload hanging on the drone. |
| | | // type-subType-index |
| | | if (indexArr.length == 3) { |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByDomainTypeSubType( |
| | | DeviceDomainEnum.PAYLOAD.getVal(), indexArr[0], indexArr[1]); |
| | | dictionaryOpt.ifPresent(dictionary -> |
| | | builder.name(dictionary.getDeviceName())); |
| | | } |
| | | return builder |
| | | .availableVideoNumber(receiver.getAvailableVideoNumber()) |
| | | .coexistVideoNumberMax(receiver.getCoexistVideoNumberMax()) |
| | | .cameraIndex(receiver.getCameraIndex()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into camera data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private CapacityCameraDTO entityConvertToDto(CapacityCameraEntity entity) { |
| | | CapacityCameraDTO.CapacityCameraDTOBuilder builder = CapacityCameraDTO.builder(); |
| | | |
| | | if (entity != null) { |
| | | builder |
| | | .id(entity.getId()) |
| | | .name(entity.getName()) |
| | | .description(entity.getDescription()) |
| | | .deviceSn(entity.getDeviceSn()) |
| | | .index(entity.getCameraIndex()) |
| | | .build(); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.manage.dao.IDeviceDictionaryMapper; |
| | | import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; |
| | | import com.dji.sample.manage.model.entity.DeviceDictionaryEntity; |
| | | import com.dji.sample.manage.service.IDeviceDictionaryService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/15 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class DeviceDictionaryServiceImpl implements IDeviceDictionaryService { |
| | | |
| | | @Autowired |
| | | private IDeviceDictionaryMapper mapper; |
| | | |
| | | @Override |
| | | public Optional<DeviceDictionaryDTO> getOneDictionaryInfoByDomainTypeSubType( |
| | | Integer domain, Integer deviceType, Integer subType) { |
| | | if (domain == null || deviceType == null || subType == null) { |
| | | return Optional.empty(); |
| | | } |
| | | return Optional.ofNullable( |
| | | entityConvertToDTO( |
| | | mapper.selectOne( |
| | | new LambdaQueryWrapper<DeviceDictionaryEntity>() |
| | | .eq(DeviceDictionaryEntity::getDomain, domain) |
| | | .eq(DeviceDictionaryEntity::getDeviceType, deviceType) |
| | | .eq(DeviceDictionaryEntity::getSubType, subType)))); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into dictionary data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private DeviceDictionaryDTO entityConvertToDTO(DeviceDictionaryEntity entity) { |
| | | DeviceDictionaryDTO.DeviceDictionaryDTOBuilder builder = DeviceDictionaryDTO.builder(); |
| | | |
| | | if (entity != null) { |
| | | builder.deviceName(entity.getDeviceName()) |
| | | .deviceDesc(entity.getDeviceDesc()) |
| | | .deviceType(entity.getDeviceType()) |
| | | .domain(entity.getDomain()) |
| | | .subType(entity.getSubType()); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.manage.model.DeviceStatusManager; |
| | | import com.dji.sample.manage.model.dto.TelemetryDTO; |
| | | import com.dji.sample.manage.model.dto.TelemetryDeviceDTO; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.DeserializationFeature; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.Collection; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/2/21 |
| | | */ |
| | | @Service |
| | | public class DeviceOSDServiceImpl extends AbstractTSAService { |
| | | |
| | | protected DeviceOSDServiceImpl() { |
| | | super(null); |
| | | } |
| | | |
| | | @Override |
| | | public void pushTelemetryData(Collection<ConcurrentWebSocketSession> sessions, |
| | | CustomWebSocketMessage<TelemetryDTO> message, Object osdData) { |
| | | if (osdData instanceof OsdSubDeviceReceiver) { |
| | | OsdSubDeviceReceiver data = (OsdSubDeviceReceiver) osdData; |
| | | TelemetryDTO telemetry = message.getData(); |
| | | telemetry.setHost(TelemetryDeviceDTO.builder() |
| | | .latitude(data.getLatitude()) |
| | | .longitude(data.getLongitude()) |
| | | .altitude(data.getElevation()) |
| | | .attitudeHead(data.getAttitudeHead()) |
| | | .elevation(data.getElevation()) |
| | | .horizontalSpeed(data.getHorizontalSpeed()) |
| | | .verticalSpeed(data.getVerticalSpeed()) |
| | | .build()); |
| | | |
| | | this.sendMessageService.sendBatch(sessions, message); |
| | | } |
| | | } |
| | | @Override |
| | | protected void handleOSD(TopicStateReceiver receiver, String sn, String workspaceId, JsonNode hostNode, |
| | | Collection<ConcurrentWebSocketSession> webSessions, CustomWebSocketMessage wsMessage) throws JsonProcessingException { |
| | | // Real-time update of device status in memory |
| | | DeviceStatusManager.STATUS_MANAGER.put( |
| | | DeviceDomainEnum.SUB_DEVICE.getVal() + "/" + sn, LocalDateTime.now()); |
| | | wsMessage.setBizCode(BizCodeEnum.DEVICE_OSD.getCode()); |
| | | |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
| | | OsdSubDeviceReceiver data = mapper.treeToValue(hostNode, OsdSubDeviceReceiver.class); |
| | | |
| | | wsMessage.setData(data); |
| | | |
| | | sendMessageService.sendBatch(webSessions, wsMessage); |
| | | this.pushTelemetryData(workspaceId, data, sn); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.manage.dao.IDevicePayloadMapper; |
| | | import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; |
| | | import com.dji.sample.manage.model.dto.DevicePayloadDTO; |
| | | import com.dji.sample.manage.model.entity.DevicePayloadEntity; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; |
| | | import com.dji.sample.manage.service.ICapacityCameraService; |
| | | import com.dji.sample.manage.service.IDeviceDictionaryService; |
| | | import com.dji.sample.manage.service.IDevicePayloadService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/19 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class DevicePayloadServiceImpl implements IDevicePayloadService { |
| | | |
| | | @Autowired |
| | | private IDevicePayloadMapper mapper; |
| | | |
| | | @Autowired |
| | | private IDeviceDictionaryService dictionaryService; |
| | | |
| | | @Autowired |
| | | private ICapacityCameraService capacityCameraService; |
| | | |
| | | @Override |
| | | public Integer checkPayloadExist(String payloadSn) { |
| | | DevicePayloadEntity devicePayload = mapper.selectOne( |
| | | new LambdaQueryWrapper<DevicePayloadEntity>() |
| | | .eq(DevicePayloadEntity::getPayloadSn, payloadSn)); |
| | | return devicePayload != null ? devicePayload.getId() : -1; |
| | | } |
| | | |
| | | private Integer saveOnePayloadEntity(DevicePayloadEntity entity) { |
| | | int id = this.checkPayloadExist(entity.getPayloadSn()); |
| | | // If it already exists, update the data directly. |
| | | if (id > 0) { |
| | | entity.setId(id); |
| | | return mapper.updateById(entity); |
| | | } |
| | | return mapper.insert(entity) > 0 ? entity.getId() : 0; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean savePayloadDTOs(List<DevicePayloadReceiver> payloadReceiverList) { |
| | | for (DevicePayloadReceiver payloadReceiver : payloadReceiverList) { |
| | | int payloadId = this.saveOnePayloadDTO(payloadReceiver); |
| | | if (payloadId <= 0) { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public Integer saveOnePayloadDTO(DevicePayloadReceiver payloadReceiver) { |
| | | return this.saveOnePayloadEntity(payloadDTOConvertToEntity(payloadReceiver)); |
| | | } |
| | | |
| | | @Override |
| | | public List<DevicePayloadDTO> getDevicePayloadEntitiesByDeviceSn(String deviceSn) { |
| | | return mapper.selectList( |
| | | new LambdaQueryWrapper<DevicePayloadEntity>() |
| | | .eq(DevicePayloadEntity::getDeviceSn, deviceSn)) |
| | | .stream() |
| | | .map(this::payloadEntityConvertToDTO) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | @Override |
| | | public void deletePayloadsByDeviceSn(List<String> deviceSns) { |
| | | deviceSns.forEach(deviceSn -> { |
| | | mapper.delete( |
| | | new LambdaQueryWrapper<DevicePayloadEntity>() |
| | | .eq(DevicePayloadEntity::getDeviceSn, deviceSn)); |
| | | capacityCameraService.deleteCapacityCameraByDeviceSn(deviceSn); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into payload data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private DevicePayloadDTO payloadEntityConvertToDTO(DevicePayloadEntity entity) { |
| | | DevicePayloadDTO.DevicePayloadDTOBuilder builder = DevicePayloadDTO.builder(); |
| | | if (entity != null) { |
| | | builder.payloadSn(entity.getPayloadSn()) |
| | | .payloadName(entity.getPayloadName()) |
| | | .payloadDesc(entity.getPayloadDesc()) |
| | | .payloadIndex(entity.getPayloadIndex()); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert the received payload object into a database entity object. |
| | | * @param dto payload |
| | | * @return |
| | | */ |
| | | private DevicePayloadEntity payloadDTOConvertToEntity(DevicePayloadReceiver dto) { |
| | | if (dto == null) { |
| | | return new DevicePayloadEntity(); |
| | | } |
| | | DevicePayloadEntity.DevicePayloadEntityBuilder builder = DevicePayloadEntity.builder(); |
| | | |
| | | // The cameraIndex consists of type and subType and the index of the payload hanging on the drone. |
| | | // type-subType-index |
| | | String[] payloadIndexArr = dto.getPayloadIndex().split("-"); |
| | | try { |
| | | int[] arr = Arrays.stream(payloadIndexArr) |
| | | .map(Integer::valueOf) |
| | | .mapToInt(Integer::intValue) |
| | | .toArray(); |
| | | |
| | | if (arr.length == 3) { |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByDomainTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), |
| | | arr[0], arr[1]); |
| | | dictionaryOpt.ifPresent(dictionary -> |
| | | builder.payloadName(dictionary.getDeviceName()) |
| | | .payloadDesc(dictionary.getDeviceDesc())); |
| | | |
| | | } |
| | | builder.payloadType(arr[0]) |
| | | .subType(arr[1]) |
| | | .payloadIndex(arr[2]); |
| | | } catch (NumberFormatException e) { |
| | | builder.payloadType(Integer.valueOf(payloadIndexArr[0])) |
| | | .subType(-1) |
| | | .payloadIndex(Integer.valueOf(payloadIndexArr[2])); |
| | | } |
| | | |
| | | return builder |
| | | .payloadSn(dto.getSn()) |
| | | .version(dto.getVersion()) |
| | | .deviceSn(dto.getSn() |
| | | .substring(0, |
| | | dto.getSn().indexOf("-"))) |
| | | .build(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.mqtt.service.IMqttTopicService; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.model.WebSocketManager; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.manage.dao.IDeviceMapper; |
| | | import com.dji.sample.manage.model.dto.*; |
| | | import com.dji.sample.manage.model.entity.DeviceEntity; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.enums.IconUrlEnum; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; |
| | | import com.dji.sample.manage.model.receiver.StatusSubDeviceReceiver; |
| | | import com.dji.sample.manage.service.IDeviceDictionaryService; |
| | | import com.dji.sample.manage.service.IDevicePayloadService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.manage.service.IWorkspaceService; |
| | | import com.fasterxml.jackson.databind.DeserializationFeature; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static com.dji.sample.component.mqtt.model.TopicConst.*; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | | * @date 2021/11/10 |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | @Transactional |
| | | public class DeviceServiceImpl implements IDeviceService { |
| | | |
| | | @Autowired |
| | | private IMessageSenderService messageSender; |
| | | |
| | | @Autowired |
| | | private IDeviceMapper mapper; |
| | | |
| | | @Autowired |
| | | private IDeviceDictionaryService dictionaryService; |
| | | |
| | | @Autowired |
| | | private IMqttTopicService topicService; |
| | | |
| | | @Autowired |
| | | private IWorkspaceService workspaceService; |
| | | |
| | | @Autowired |
| | | private IDevicePayloadService payloadService; |
| | | |
| | | @Autowired |
| | | private ISendMessageService sendMessageService; |
| | | |
| | | @Autowired |
| | | @Qualifier("gatewayOSDServiceImpl") |
| | | private AbstractTSAService tsaService; |
| | | |
| | | @Override |
| | | public Boolean deviceOffline(String gatewaySn) { |
| | | List<DeviceDTO> gatewaysList = this.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .deviceSn(gatewaySn) |
| | | .build()); |
| | | |
| | | // If no information about this gateway device exists in the database, the drone is considered to be offline. |
| | | if (gatewaysList.isEmpty()) { |
| | | log.debug("The drone is already offline."); |
| | | return true; |
| | | } |
| | | // Handle the drone connected to the gateway device offline. |
| | | return this.subDeviceOffline(gatewaysList.get(0).getChildDeviceSn()); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean subDeviceOffline(String deviceSn) { |
| | | // Cancel drone-related subscriptions. |
| | | this.unsubscribeTopicOffline(deviceSn); |
| | | |
| | | List<DeviceDTO> devicesList = this.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .deviceSn(deviceSn) |
| | | .build()); |
| | | |
| | | // If no information about this drone exists in the database, the drone is considered to be offline. |
| | | if (devicesList.isEmpty()) { |
| | | log.debug("{} is already offline.", deviceSn); |
| | | return true; |
| | | } |
| | | |
| | | List<String> ids = devicesList.stream() |
| | | .map(DeviceDTO::getDeviceSn) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // Delete all data related to the drone. |
| | | boolean isDel = this.delDeviceByDeviceSns(ids); |
| | | payloadService.deletePayloadsByDeviceSn(ids); |
| | | |
| | | log.debug("{} offline status: {}.", deviceSn, isDel); |
| | | return isDel; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean deviceOnline(StatusGatewayReceiver deviceGateway) { |
| | | String deviceSn = deviceGateway.getSubDevices().get(0).getSn(); |
| | | |
| | | List<DeviceDTO> devicesList = this.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .deviceSn(deviceSn) |
| | | .build()); |
| | | // If the information about this drone exists in the database, the drone is considered to be online. |
| | | if (!devicesList.isEmpty()) { |
| | | log.warn("{} is already online.", deviceSn); |
| | | // Subscribe to topic related to drone and gateway devices. |
| | | this.subscribeTopicOnline(deviceGateway.getSn()); |
| | | this.subscribeTopicOnline(deviceSn); |
| | | return true; |
| | | } |
| | | |
| | | // Delete the gateway device information that was previously bound to the drone. |
| | | this.delDeviceByDeviceSns( |
| | | this.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .childSn(deviceSn) |
| | | .build()) |
| | | .stream() |
| | | .map(DeviceDTO::getDeviceSn) |
| | | .collect(Collectors.toList())); |
| | | |
| | | DeviceEntity gateway = deviceGatewayConvertToDeviceEntity(deviceGateway); |
| | | gateway.setWorkspaceId(WorkspaceDTO.DEFAULT_WORKSPACE_ID); |
| | | // Set the icon of the gateway device displayed in the pilot's map, required in the TSA module. |
| | | gateway.setUrlNormal(IconUrlEnum.NORMAL_PERSON.getUrl()); |
| | | // Set the icon of the gateway device displayed in the pilot's map when it is selected, required in the TSA module. |
| | | gateway.setUrlSelect(IconUrlEnum.SELECT_PERSON.getUrl()); |
| | | |
| | | DeviceEntity subDevice = subDeviceConvertToDeviceEntity(deviceGateway.getSubDevices().get(0)); |
| | | subDevice.setWorkspaceId(WorkspaceDTO.DEFAULT_WORKSPACE_ID); |
| | | // Set the icon of the drone device displayed in the pilot's map, required in the TSA module. |
| | | subDevice.setUrlSelect(IconUrlEnum.SELECT_EQUIPMENT.getUrl()); |
| | | // Set the icon of the drone device displayed in the pilot's map when it is selected, required in the TSA module. |
| | | subDevice.setUrlNormal(IconUrlEnum.NORMAL_EQUIPMENT.getUrl()); |
| | | |
| | | gateway.setChildSn(subDevice.getDeviceSn()); |
| | | |
| | | boolean isSave = this.saveDevice(gateway) > 0 && this.saveDevice(subDevice) > 0; |
| | | |
| | | log.debug(subDevice.getDeviceSn() + " online status: {}", isSave); |
| | | if (isSave) { |
| | | // Subscribe to topic related to drone and gateway devices. |
| | | this.subscribeTopicOnline(subDevice.getDeviceSn()); |
| | | this.subscribeTopicOnline(gateway.getDeviceSn()); |
| | | |
| | | } |
| | | return isSave; |
| | | } |
| | | |
| | | @Override |
| | | public void subscribeTopicOnline(String sn) { |
| | | String[] subscribedTopic = topicService.getSubscribedTopic(); |
| | | for (String s : subscribedTopic) { |
| | | // If you have already subscribed to the topic of the device, you do not need to subscribe again. |
| | | if (s.contains(sn)) { |
| | | return; |
| | | } |
| | | } |
| | | topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + OSD_SUF); |
| | | topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + STATE_SUF); |
| | | topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + SERVICES_SUF + _REPLY_SUF); |
| | | } |
| | | |
| | | @Override |
| | | public void unsubscribeTopicOffline(String sn) { |
| | | topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + OSD_SUF); |
| | | topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + STATE_SUF); |
| | | topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + SERVICES_SUF + _REPLY_SUF); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delDeviceByDeviceSns(List<String> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | return true; |
| | | } |
| | | return mapper.delete(new LambdaQueryWrapper<DeviceEntity>() |
| | | .in(DeviceEntity::getDeviceSn, ids)) |
| | | > 0; |
| | | } |
| | | |
| | | @Override |
| | | public void publishStatusReply(String sn, CommonTopicResponse<Object> response) { |
| | | Map<String, Integer> result = new ConcurrentHashMap<>(1); |
| | | result.put("result", 0); |
| | | response.setData(result); |
| | | |
| | | messageSender.publish( |
| | | new StringBuilder() |
| | | .append(BASIC_PRE) |
| | | .append(PRODUCT) |
| | | .append(sn) |
| | | .append(STATUS_SUF) |
| | | .append(_REPLY_SUF) |
| | | .toString(), |
| | | response); |
| | | } |
| | | |
| | | @Override |
| | | public List<DeviceDTO> getDevicesByParams(DeviceQueryParam param) { |
| | | return mapper.selectList( |
| | | new LambdaQueryWrapper<DeviceEntity>() |
| | | .eq(StringUtils.hasText(param.getDeviceSn()), |
| | | DeviceEntity::getDeviceSn, param.getDeviceSn()) |
| | | .eq(param.getDeviceType() != null, |
| | | DeviceEntity::getDeviceType, param.getDeviceType()) |
| | | .eq(param.getSubType() != null, |
| | | DeviceEntity::getSubType, param.getSubType()) |
| | | .eq(StringUtils.hasText(param.getChildSn()), |
| | | DeviceEntity::getChildSn, param.getChildSn()) |
| | | .eq(param.getDomain() != null, |
| | | DeviceEntity::getDomain, param.getDomain()) |
| | | .eq(StringUtils.hasText(param.getWorkspaceId()), |
| | | DeviceEntity::getWorkspaceId, param.getWorkspaceId()) |
| | | .orderBy(param.isOrderBy(), |
| | | param.isAsc(), DeviceEntity::getId)) |
| | | .stream() |
| | | .map(this::deviceEntityConvertToDTO) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | @Override |
| | | public List<DeviceDTO> getDevicesTopoForWeb(String workspaceId) { |
| | | List<DeviceDTO> devicesList = this.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .workspaceId(workspaceId) |
| | | .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) |
| | | .build()); |
| | | |
| | | devicesList.forEach(device -> { |
| | | this.spliceDeviceTopo(device); |
| | | device.setWorkspaceId(workspaceId); |
| | | |
| | | }); |
| | | return devicesList; |
| | | } |
| | | |
| | | @Override |
| | | public void spliceDeviceTopo(DeviceDTO device) { |
| | | |
| | | // remote controller |
| | | List<DeviceDTO> gatewaysList = getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .childSn(device.getDeviceSn()) |
| | | .build()); |
| | | |
| | | // payloads |
| | | List<DevicePayloadDTO> payloadsList = payloadService |
| | | .getDevicePayloadEntitiesByDeviceSn(device.getDeviceSn()); |
| | | |
| | | |
| | | device.setGatewaysList(gatewaysList); |
| | | device.setPayloadsList(payloadsList); |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public Optional<TopologyDeviceDTO> getDeviceTopoForPilot(String sn) { |
| | | List<TopologyDeviceDTO> topologyDeviceList = this.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .deviceSn(sn) |
| | | .build()) |
| | | .stream() |
| | | .map(this::deviceConvertToTopologyDTO) |
| | | .collect(Collectors.toList()); |
| | | if (topologyDeviceList.isEmpty()) { |
| | | return Optional.empty(); |
| | | } |
| | | return Optional.of(topologyDeviceList.get(0)); |
| | | } |
| | | |
| | | @Override |
| | | public void pushDeviceOnlineTopo(Collection<ConcurrentWebSocketSession> sessions, String sn) { |
| | | |
| | | CustomWebSocketMessage<TopologyDeviceDTO> pilotMessage = |
| | | CustomWebSocketMessage.<TopologyDeviceDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.DEVICE_ONLINE.getCode()) |
| | | .data(new TopologyDeviceDTO()) |
| | | .build(); |
| | | |
| | | this.getDeviceTopoForPilot(sn) |
| | | .ifPresent(pilotMessage::setData); |
| | | |
| | | sendMessageService.sendBatch(sessions, pilotMessage); |
| | | } |
| | | |
| | | @Override |
| | | public TopologyDeviceDTO deviceConvertToTopologyDTO(DeviceDTO device) { |
| | | TopologyDeviceDTO.TopologyDeviceDTOBuilder builder = TopologyDeviceDTO.builder(); |
| | | |
| | | if (device != null) { |
| | | String domain = String.valueOf(DeviceDomainEnum.getVal(device.getDomain())); |
| | | String subType = String.valueOf(device.getSubType()); |
| | | String type = String.valueOf(device.getType()); |
| | | |
| | | builder.sn(device.getDeviceSn()) |
| | | .deviceCallsign(device.getDeviceName()) |
| | | .deviceModel(DeviceModelDTO.builder() |
| | | .domain(domain) |
| | | .subType(subType) |
| | | .type(type) |
| | | .key(domain + "-" + type + "-" + subType) |
| | | .build()) |
| | | .iconUrls(device.getIconUrl()) |
| | | .build(); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | |
| | | @Override |
| | | public void pushDeviceOnlineTopo(String workspaceId, String deviceSn, String gatewaySn) { |
| | | |
| | | // All connected accounts on the pilot side of this workspace. |
| | | Collection<ConcurrentWebSocketSession> pilotSessions = WebSocketManager |
| | | .getValueWithWorkspaceAndUserType( |
| | | workspaceId, UserTypeEnum.PILOT.getVal()); |
| | | |
| | | this.pushDeviceOnlineTopo(pilotSessions, deviceSn); |
| | | this.pushDeviceOnlineTopo(pilotSessions, gatewaySn); |
| | | this.pushDeviceUpdateTopo(pilotSessions, deviceSn); |
| | | this.pushDeviceUpdateTopo(pilotSessions, gatewaySn); |
| | | } |
| | | |
| | | @Override |
| | | public void pushDeviceOfflineTopo(String workspaceId, String gatewaySn) { |
| | | // All connected accounts on the pilot side of this workspace. |
| | | Collection<ConcurrentWebSocketSession> pilotSessions = WebSocketManager |
| | | .getValueWithWorkspaceAndUserType( |
| | | workspaceId, UserTypeEnum.PILOT.getVal()); |
| | | |
| | | |
| | | List<DeviceDTO> gatewaysList = this.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .deviceSn(gatewaySn) |
| | | .build()); |
| | | |
| | | if (!gatewaysList.isEmpty()) { |
| | | String deviceSn = gatewaysList.get(0).getChildDeviceSn(); |
| | | this.pushDeviceOfflineTopo(pilotSessions, deviceSn); |
| | | this.pushDeviceUpdateTopo(pilotSessions, deviceSn); |
| | | } |
| | | |
| | | this.pushDeviceOfflineTopo(pilotSessions, gatewaySn); |
| | | this.pushDeviceUpdateTopo(pilotSessions, gatewaySn); |
| | | } |
| | | |
| | | @Override |
| | | public void handleOSD(String topic, byte[] payload) { |
| | | TopicStateReceiver receiver; |
| | | try { |
| | | String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), |
| | | topic.indexOf(OSD_SUF)); |
| | | |
| | | List<DeviceDTO> deviceList = this.getDevicesByParams( |
| | | DeviceQueryParam.builder().deviceSn(from).build()); |
| | | if (deviceList.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
| | | |
| | | receiver = mapper.readValue(payload, TopicStateReceiver.class); |
| | | |
| | | CustomWebSocketMessage wsMessage = CustomWebSocketMessage.builder() |
| | | .timestamp(System.currentTimeMillis()).build(); |
| | | |
| | | JsonNode hostNode = mapper.readTree(payload).findPath("data"); |
| | | |
| | | String workspaceId = deviceList.get(0).getWorkspaceId(); |
| | | Collection<ConcurrentWebSocketSession> webSessions = WebSocketManager |
| | | .getValueWithWorkspaceAndUserType( |
| | | workspaceId, UserTypeEnum.WEB.getVal()); |
| | | |
| | | |
| | | tsaService.handleOSD(receiver, from, workspaceId, hostNode, webSessions, wsMessage); |
| | | |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Notify the pilot side that there is an update of the device topology. |
| | | * @param sessions |
| | | * @param deviceSn |
| | | */ |
| | | private void pushDeviceUpdateTopo(Collection<ConcurrentWebSocketSession> sessions, String deviceSn) { |
| | | |
| | | CustomWebSocketMessage pilotMessage = |
| | | CustomWebSocketMessage.builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.DEVICE_UPDATE_TOPO.getCode()) |
| | | .data(new TopologyDeviceDTO()) |
| | | .build(); |
| | | sendMessageService.sendBatch(sessions, pilotMessage); |
| | | } |
| | | |
| | | /** |
| | | * Notify the pilot side that device is offline and needs to reacquire topology information. |
| | | * @param sessions |
| | | * @param sn |
| | | */ |
| | | private void pushDeviceOfflineTopo(Collection<ConcurrentWebSocketSession> sessions, String sn) { |
| | | CustomWebSocketMessage<TopologyDeviceDTO> pilotMessage = |
| | | CustomWebSocketMessage.<TopologyDeviceDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.DEVICE_OFFLINE.getCode()) |
| | | .data(TopologyDeviceDTO.builder() |
| | | .sn(sn) |
| | | .onlineStatus(false) |
| | | .build()) |
| | | .build(); |
| | | sendMessageService.sendBatch(sessions, pilotMessage); |
| | | } |
| | | |
| | | /** |
| | | * Save the device information and update the information directly if the device already exists. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private Integer saveDevice(DeviceEntity entity) { |
| | | DeviceEntity deviceEntity = mapper.selectOne( |
| | | new LambdaQueryWrapper<DeviceEntity>() |
| | | .eq(DeviceEntity::getDeviceSn, entity.getDeviceSn())); |
| | | // Update the information directly if the device already exists. |
| | | if (deviceEntity != null) { |
| | | entity.setId(deviceEntity.getId()); |
| | | mapper.updateById(entity); |
| | | return deviceEntity.getId(); |
| | | } |
| | | return mapper.insert(entity) > 0 ? entity.getId() : 0; |
| | | } |
| | | |
| | | /** |
| | | * Convert the received gateway device object into a database entity object. |
| | | * @param gateway |
| | | * @return |
| | | */ |
| | | private DeviceEntity deviceGatewayConvertToDeviceEntity(StatusGatewayReceiver gateway) { |
| | | if (gateway == null) { |
| | | return new DeviceEntity(); |
| | | } |
| | | DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder(); |
| | | |
| | | // Query the model information of this gateway device. |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByDomainTypeSubType(gateway.getDomain(), |
| | | gateway.getType(), gateway.getSubType()); |
| | | |
| | | dictionaryOpt.ifPresent(entity -> |
| | | builder.deviceName(entity.getDeviceName()) |
| | | .deviceDesc(entity.getDeviceDesc())); |
| | | |
| | | return builder |
| | | .deviceSn(gateway.getSn()) |
| | | .domain(gateway.getDomain()) |
| | | .subType(gateway.getSubType()) |
| | | .deviceType(gateway.getType()) |
| | | .version(gateway.getVersion()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert the received drone device object into a database entity object. |
| | | * @param device |
| | | * @return |
| | | */ |
| | | private DeviceEntity subDeviceConvertToDeviceEntity(StatusSubDeviceReceiver device) { |
| | | if (device == null) { |
| | | return new DeviceEntity(); |
| | | } |
| | | DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder(); |
| | | |
| | | // Query the model information of this drone device. |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByDomainTypeSubType(DeviceDomainEnum.SUB_DEVICE.getVal(), |
| | | device.getType(), device.getSubType()); |
| | | |
| | | dictionaryOpt.ifPresent(dictionary -> |
| | | builder.deviceName(dictionary.getDeviceName()) |
| | | .deviceDesc(dictionary.getDeviceDesc())); |
| | | |
| | | return builder |
| | | .deviceSn(device.getSn()) |
| | | .deviceType(device.getType()) |
| | | .subType(device.getSubType()) |
| | | .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) |
| | | .version(device.getVersion()) |
| | | .deviceIndex(device.getIndex()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into device data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private DeviceDTO deviceEntityConvertToDTO(DeviceEntity entity) { |
| | | DeviceDTO.DeviceDTOBuilder builder = DeviceDTO.builder(); |
| | | |
| | | if (entity != null) { |
| | | builder.deviceSn(entity.getDeviceSn()) |
| | | .childDeviceSn(entity.getChildSn()) |
| | | .deviceName(entity.getDeviceName()) |
| | | .deviceDesc(entity.getDeviceDesc()) |
| | | .deviceIndex(entity.getDeviceIndex()) |
| | | .workspaceId(entity.getWorkspaceId()) |
| | | .type(entity.getDeviceType()) |
| | | .subType(entity.getSubType()) |
| | | .domain(DeviceDomainEnum.getDesc(entity.getDomain())) |
| | | .iconUrl(IconUrlDTO.builder() |
| | | .normalUrl(entity.getUrlNormal()) |
| | | .selectUrl(entity.getUrlSelect()) |
| | | .build()) |
| | | .build(); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.TopicStateReceiver; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.manage.model.DeviceStatusManager; |
| | | import com.dji.sample.manage.model.dto.TelemetryDTO; |
| | | import com.dji.sample.manage.model.dto.TelemetryDeviceDTO; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.receiver.OsdGatewayReceiver; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.databind.DeserializationFeature; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.Collection; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/2/21 |
| | | */ |
| | | @Service |
| | | public class GatewayOSDServiceImpl extends AbstractTSAService { |
| | | |
| | | public GatewayOSDServiceImpl(@Autowired @Qualifier("deviceOSDServiceImpl") AbstractTSAService tsaService) { |
| | | super(tsaService); |
| | | } |
| | | |
| | | @Override |
| | | public void pushTelemetryData(Collection<ConcurrentWebSocketSession> sessions, |
| | | CustomWebSocketMessage<TelemetryDTO> message, Object osdData) { |
| | | if (osdData instanceof OsdGatewayReceiver) { |
| | | OsdGatewayReceiver data = (OsdGatewayReceiver) osdData; |
| | | TelemetryDTO telemetry = message.getData(); |
| | | telemetry.setHost(TelemetryDeviceDTO.builder() |
| | | .latitude(data.getLatitude()) |
| | | .longitude(data.getLongitude()) |
| | | .build()); |
| | | this.sendMessageService.sendBatch(sessions, message); |
| | | return; |
| | | } |
| | | tsaService.pushTelemetryData(sessions, message, osdData); |
| | | } |
| | | |
| | | @Override |
| | | protected void handleOSD(TopicStateReceiver receiver, String sn, String workspaceId, JsonNode hostNode, |
| | | Collection<ConcurrentWebSocketSession> webSessions, CustomWebSocketMessage wsMessage) throws JsonProcessingException { |
| | | if (sn.equals(receiver.getGateway())) { |
| | | // Real-time update of device status in memory |
| | | DeviceStatusManager.STATUS_MANAGER.put(DeviceDomainEnum.GATEWAY.getVal() + "/" + sn, |
| | | LocalDateTime.now()); |
| | | |
| | | ObjectMapper mapper = new ObjectMapper(); |
| | | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
| | | |
| | | wsMessage.setBizCode(BizCodeEnum.GATEWAY_OSD.getCode()); |
| | | OsdGatewayReceiver data = mapper.treeToValue(hostNode, OsdGatewayReceiver.class); |
| | | wsMessage.setData(data); |
| | | |
| | | this.sendMessageService.sendBatch(webSessions, wsMessage); |
| | | |
| | | this.pushTelemetryData(workspaceId, data, sn); |
| | | return; |
| | | } |
| | | |
| | | tsaService.handleOSD(receiver, sn, workspaceId, hostNode, webSessions, wsMessage); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.common.error.LiveErrorEnum; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.manage.model.Chan; |
| | | import com.dji.sample.manage.model.dto.*; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.enums.LiveMethodEnum; |
| | | import com.dji.sample.manage.model.enums.LiveUrlTypeEnum; |
| | | import com.dji.sample.manage.model.enums.LiveVideoQualityEnum; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; |
| | | import com.dji.sample.manage.model.receiver.ServiceReplyReceiver; |
| | | import com.dji.sample.manage.service.ICapacityCameraService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.manage.service.ILiveStreamService; |
| | | import com.dji.sample.manage.service.IWorkspaceService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.lang.reflect.Field; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import static com.dji.sample.component.mqtt.model.TopicConst.*; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/22 |
| | | * @version 0.1 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class LiveStreamServiceImpl implements ILiveStreamService { |
| | | |
| | | @Autowired |
| | | private ICapacityCameraService capacityCameraService; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Autowired |
| | | private IWorkspaceService workspaceService; |
| | | |
| | | @Autowired |
| | | private IMessageSenderService messageSender; |
| | | |
| | | @Override |
| | | public List<CapacityDeviceDTO> getLiveCapacity(String workspaceId) { |
| | | |
| | | // Query all drone data in this workspace. |
| | | List<DeviceDTO> devicesList = deviceService.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .workspaceId(workspaceId) |
| | | .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) |
| | | .build()); |
| | | |
| | | List<CapacityDeviceDTO> capacityDevicesList = new ArrayList<>(); |
| | | // Query the live capability of each drone. |
| | | devicesList.forEach(device -> capacityDevicesList.add(CapacityDeviceDTO.builder() |
| | | .name(device.getDeviceName()) |
| | | .sn(device.getDeviceSn()) |
| | | .camerasList(capacityCameraService.getCapacityCameraByDeviceSn(device.getDeviceSn())) |
| | | .build())); |
| | | |
| | | return capacityDevicesList; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean saveLiveCapacity(CapacityDeviceReceiver capacityDeviceReceiver) { |
| | | return capacityCameraService.saveCapacityCameraReceiverList( |
| | | capacityDeviceReceiver.getCameraList(), |
| | | capacityDeviceReceiver.getSn()); |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult liveStart(LiveTypeDTO liveParam) { |
| | | // Check if this lens is available live. |
| | | ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId()); |
| | | if (responseResult.getCode() != 0) { |
| | | return responseResult; |
| | | } |
| | | |
| | | List<DeviceDTO> data = (List<DeviceDTO>)responseResult.getData(); |
| | | // target topic |
| | | String respTopic = THING_MODEL_PRE + PRODUCT + |
| | | data.get(0).getDeviceSn() + SERVICES_SUF; |
| | | Optional<ServiceReplyReceiver> receiveReplyOpt = this.publishLiveStart(respTopic, liveParam); |
| | | |
| | | if (receiveReplyOpt.isEmpty()) { |
| | | return ResponseResult.error(LiveErrorEnum.NO_REPLY); |
| | | } |
| | | if (receiveReplyOpt.get().getResult() != 0) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult())); |
| | | } |
| | | |
| | | LiveUrlTypeEnum urlType = LiveUrlTypeEnum.find(liveParam.getUrlType()); |
| | | LiveDTO live = new LiveDTO(); |
| | | |
| | | switch (urlType) { |
| | | case RTMP: |
| | | live.setUrl(liveParam.getUrl().replace("rtmp", "webrtc")); |
| | | break; |
| | | case GB28181: |
| | | LiveUrlGB28181DTO gb28181 = urlToGB28181(liveParam.getUrl()); |
| | | live.setUrl(new StringBuilder() |
| | | .append("webrtc://") |
| | | .append(gb28181.getServerIP()) |
| | | .append("/live/") |
| | | .append(gb28181.getAgentID()) |
| | | .append("@") |
| | | .append(gb28181.getChannel()) |
| | | .toString()); |
| | | break; |
| | | case RTSP: |
| | | String url = receiveReplyOpt.get().getInfo(); |
| | | this.resolveUrlUser(url, live); |
| | | break; |
| | | case UNKNOWN: |
| | | return ResponseResult.error(LiveErrorEnum.URL_TYPE_NOT_SUPPORTED); |
| | | } |
| | | |
| | | return ResponseResult.success(live); |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult liveStop(String videoId) { |
| | | ResponseResult<List<DeviceDTO>> responseResult = this.checkBeforeLive(videoId); |
| | | if (responseResult.getCode() != 0) { |
| | | return responseResult; |
| | | } |
| | | |
| | | String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().get(0).getDeviceSn() + SERVICES_SUF; |
| | | |
| | | Optional<ServiceReplyReceiver> receiveReplyOpt = this.publishLiveStop(respTopic, videoId); |
| | | if (receiveReplyOpt.isEmpty()) { |
| | | return ResponseResult.error(LiveErrorEnum.NO_REPLY); |
| | | } |
| | | if (receiveReplyOpt.get().getResult() != 0) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult())); |
| | | } |
| | | |
| | | return ResponseResult.success(); |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult liveSetQuality(LiveTypeDTO liveParam) { |
| | | if (liveParam.getVideoQuality() == null || |
| | | LiveVideoQualityEnum.UNKNOWN == LiveVideoQualityEnum.find(liveParam.getVideoQuality())) { |
| | | return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS); |
| | | } |
| | | |
| | | ResponseResult<List<DeviceDTO>> responseResult = this.checkBeforeLive(liveParam.getVideoId()); |
| | | |
| | | if (responseResult.getCode() != 0) { |
| | | return responseResult; |
| | | } |
| | | |
| | | String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().get(0).getDeviceSn() + SERVICES_SUF; |
| | | |
| | | Optional<ServiceReplyReceiver> receiveReplyOpt = this.publishLiveSetQuality(respTopic, liveParam); |
| | | if (receiveReplyOpt.isEmpty()) { |
| | | return ResponseResult.error(LiveErrorEnum.NO_REPLY); |
| | | } |
| | | if (receiveReplyOpt.get().getResult() != 0) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult())); |
| | | } |
| | | |
| | | return ResponseResult.success(); |
| | | } |
| | | |
| | | /** |
| | | * Check if this lens is available live. |
| | | * @param videoId |
| | | * @return |
| | | */ |
| | | private ResponseResult checkBeforeLive(String videoId) { |
| | | String[] videoIdArr = videoId.split("/"); |
| | | // drone sn / enumeration value of the location where the payload is mounted / payload lens |
| | | if (videoIdArr.length != 3) { |
| | | return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS); |
| | | } |
| | | |
| | | List<DeviceDTO> gatewayList = deviceService.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .childSn(videoIdArr[0]) |
| | | .build()); |
| | | // Check if the gateway device connected to this drone exists |
| | | if (gatewayList.isEmpty()) { |
| | | return ResponseResult.error(LiveErrorEnum.NO_FLIGHT_CONTROL); |
| | | } |
| | | return ResponseResult.success(gatewayList); |
| | | } |
| | | |
| | | /** |
| | | * When using rtsp live, the account and password are parsed from the information returned by the pilot. |
| | | * @param url |
| | | * @param live |
| | | */ |
| | | private void resolveUrlUser(String url, LiveDTO live) { |
| | | if (!StringUtils.hasText(url)) { |
| | | return; |
| | | } |
| | | int start = url.indexOf("//"); |
| | | int end = url.lastIndexOf("@"); |
| | | String user = url.substring(start + 2, end); |
| | | |
| | | url = url.replace(user + "@", ""); |
| | | String[] userArr = user.split(":"); |
| | | live.setUsername(userArr[0]); |
| | | live.setPassword(userArr[1]); |
| | | live.setUrl(url); |
| | | } |
| | | |
| | | /** |
| | | * When using GB28181 live, url parameters are resolved into objects. |
| | | * @param url |
| | | * @return |
| | | */ |
| | | private LiveUrlGB28181DTO urlToGB28181(String url) { |
| | | String[] arr = url.split("\\=|\\&"); |
| | | LiveUrlGB28181DTO gb28181 = new LiveUrlGB28181DTO(); |
| | | try { |
| | | Class<LiveUrlGB28181DTO> clazz = LiveUrlGB28181DTO.class; |
| | | for (int i = 0; i < arr.length - 1; i += 2) { |
| | | Field field = clazz.getDeclaredField(arr[i]); |
| | | field.setAccessible(true); |
| | | |
| | | if (field.getType().equals(Integer.class)) { |
| | | field.set(gb28181, Integer.valueOf(arr[i + 1])); |
| | | continue; |
| | | } |
| | | field.set(gb28181, arr[i + 1]); |
| | | } |
| | | } catch (NoSuchFieldException | IllegalAccessException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return gb28181; |
| | | } |
| | | |
| | | /** |
| | | * Send a message to the pilot via mqtt to start the live streaming. |
| | | * @param topic |
| | | * @param liveParam |
| | | * @return |
| | | */ |
| | | private Optional<ServiceReplyReceiver> publishLiveStart(String topic, LiveTypeDTO liveParam) { |
| | | CommonTopicResponse<LiveTypeDTO> response = new CommonTopicResponse<>(); |
| | | response.setTid(UUID.randomUUID().toString()); |
| | | response.setBid(UUID.randomUUID().toString()); |
| | | response.setData(liveParam); |
| | | response.setMethod(LiveMethodEnum.LIVE_START_PUSH.getMethod()); |
| | | |
| | | return this.publishLive(topic, response); |
| | | } |
| | | |
| | | /** |
| | | * Send a message to the pilot via mqtt to set quality. |
| | | * @param respTopic |
| | | * @param liveParam |
| | | * @return |
| | | */ |
| | | private Optional<ServiceReplyReceiver> publishLiveSetQuality(String respTopic, LiveTypeDTO liveParam) { |
| | | Map<String, Object> data = new ConcurrentHashMap<>(Map.of( |
| | | "video_id", liveParam.getVideoId(), |
| | | "video_quality", liveParam.getVideoQuality())); |
| | | CommonTopicResponse<Map<String, Object>> response = new CommonTopicResponse<>(); |
| | | response.setTid(UUID.randomUUID().toString()); |
| | | response.setBid(UUID.randomUUID().toString()); |
| | | response.setMethod(LiveMethodEnum.LIVE_SET_QUALITY.getMethod()); |
| | | response.setData(data); |
| | | |
| | | return this.publishLive(respTopic, response); |
| | | } |
| | | |
| | | /** |
| | | * Send a message to the pilot via mqtt to stop the live streaming. |
| | | * @param topic |
| | | * @param videoId |
| | | * @return |
| | | */ |
| | | private Optional<ServiceReplyReceiver> publishLiveStop(String topic, String videoId) { |
| | | Map<String, String> data = new ConcurrentHashMap<>(Map.of("video_id", videoId)); |
| | | CommonTopicResponse<Map<String, String>> response = new CommonTopicResponse<>(); |
| | | response.setTid(UUID.randomUUID().toString()); |
| | | response.setBid(UUID.randomUUID().toString()); |
| | | response.setData(data); |
| | | response.setMethod(LiveMethodEnum.LIVE_STOP_PUSH.getMethod()); |
| | | |
| | | return this.publishLive(topic, response); |
| | | } |
| | | |
| | | /** |
| | | * Send live streaming start message and receive a response at the same time |
| | | * @param topic |
| | | * @param response notification of whether the start is successful. |
| | | * @return |
| | | */ |
| | | private Optional<ServiceReplyReceiver> publishLive(String topic, CommonTopicResponse response) { |
| | | AtomicInteger time = new AtomicInteger(0); |
| | | // Retry three times |
| | | while (time.getAndIncrement() < 3) { |
| | | messageSender.publish(topic, response); |
| | | |
| | | Chan<CommonTopicReceiver<ServiceReplyReceiver>> chan = Chan.getInstance(); |
| | | // If the message is not received in 0.5 seconds then resend it again. |
| | | CommonTopicReceiver<ServiceReplyReceiver> receiver = chan.get(response.getMethod()); |
| | | if (receiver == null) { |
| | | continue; |
| | | } |
| | | // Need to match tid and bid. |
| | | if (receiver.getTid().equals(response.getTid()) && |
| | | receiver.getBid().equals(response.getBid())) { |
| | | return Optional.ofNullable(receiver.getData()); |
| | | } |
| | | } |
| | | return Optional.empty(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.dto.TopologyDTO; |
| | | import com.dji.sample.manage.model.dto.TopologyDeviceDTO; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.manage.service.ITopologyService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/8 |
| | | */ |
| | | @Service |
| | | public class TopologyServiceImpl implements ITopologyService { |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Override |
| | | public List<TopologyDTO> getDeviceTopology(String workspaceId) { |
| | | // Query the information of all gateway devices in the workspace. |
| | | List<DeviceDTO> gatewayList = deviceService.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .workspaceId(workspaceId) |
| | | .domain(DeviceDomainEnum.GATEWAY.getVal()) |
| | | .build()); |
| | | |
| | | List<TopologyDTO> topologyList = new ArrayList<>(); |
| | | |
| | | gatewayList.forEach(device -> { |
| | | List<TopologyDeviceDTO> parents = new ArrayList<>(); |
| | | TopologyDeviceDTO gateway = deviceService.deviceConvertToTopologyDTO(device); |
| | | parents.add(gateway); |
| | | |
| | | // Query the topology data of the drone based on the drone sn. |
| | | Optional<TopologyDeviceDTO> deviceTopo = deviceService.getDeviceTopoForPilot(device.getChildDeviceSn()); |
| | | List<TopologyDeviceDTO> deviceTopoList = new ArrayList<>(); |
| | | deviceTopo.ifPresent(deviceTopoList::add); |
| | | |
| | | topologyList.add(TopologyDTO.builder().parents(parents).hosts(deviceTopoList).build()); |
| | | }); |
| | | |
| | | return topologyList; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.auth0.jwt.JWT; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.common.util.JwtUtil; |
| | | import com.dji.sample.component.mqtt.config.MqttConfiguration; |
| | | import com.dji.sample.manage.dao.IUserMapper; |
| | | import com.dji.sample.manage.model.dto.UserDTO; |
| | | import com.dji.sample.manage.model.dto.WorkspaceDTO; |
| | | import com.dji.sample.manage.model.entity.UserEntity; |
| | | import com.dji.sample.manage.service.IUserService; |
| | | import com.dji.sample.manage.service.IWorkspaceService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class UserServiceImpl implements IUserService { |
| | | |
| | | @Autowired |
| | | private IUserMapper mapper; |
| | | |
| | | @Autowired |
| | | private MqttConfiguration mqttConfiguration; |
| | | |
| | | @Autowired |
| | | private IWorkspaceService workspaceService; |
| | | |
| | | @Override |
| | | public ResponseResult getUserByUsername(String username, String workspaceId) { |
| | | |
| | | UserEntity userEntity = this.getUserByUsername(username); |
| | | if (userEntity == null) { |
| | | return ResponseResult.builder() |
| | | .code(HttpStatus.UNAUTHORIZED.value()) |
| | | .message("invalid username") |
| | | .build(); |
| | | } |
| | | |
| | | UserDTO user = this.entityConvertToDTO(userEntity); |
| | | user.setWorkspaceId(workspaceId); |
| | | |
| | | return ResponseResult.success(user); |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult userLogin(String username, String password) { |
| | | // check user |
| | | UserEntity userEntity = this.getUserByUsername(username); |
| | | if (userEntity == null) { |
| | | return ResponseResult.builder() |
| | | .code(HttpStatus.UNAUTHORIZED.value()) |
| | | .message("invalid username") |
| | | .build(); |
| | | } |
| | | if (!password.equals(userEntity.getPassword())) { |
| | | return ResponseResult.builder() |
| | | .code(HttpStatus.UNAUTHORIZED.value()) |
| | | .message("invalid password") |
| | | .build(); |
| | | } |
| | | |
| | | Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceById(userEntity.getWorkspaceId()); |
| | | if (workspaceOpt.isEmpty()) { |
| | | return ResponseResult.builder() |
| | | .code(HttpStatus.UNAUTHORIZED.value()) |
| | | .message("invalid workspace id") |
| | | .build(); |
| | | } |
| | | |
| | | CustomClaim customClaim = new CustomClaim(userEntity.getUserId(), |
| | | userEntity.getUsername(), userEntity.getUserType(), |
| | | workspaceOpt.get().getWorkspaceId()); |
| | | |
| | | // create token |
| | | String token = JwtUtil.createToken(customClaim.convertToMap()); |
| | | |
| | | UserDTO userDTO = entityConvertToDTO(userEntity); |
| | | userDTO.setMqttAddr(new StringBuilder() |
| | | .append(mqttConfiguration.getProtocol().trim()) |
| | | .append("://") |
| | | .append(mqttConfiguration.getHost().trim()) |
| | | .append(":") |
| | | .append(mqttConfiguration.getPort()) |
| | | .toString()); |
| | | userDTO.setAccessToken(token); |
| | | userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId()); |
| | | return ResponseResult.success(userDTO); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<UserDTO> refreshToken(String token) { |
| | | if (!StringUtils.hasText(token)) { |
| | | return Optional.empty(); |
| | | } |
| | | CustomClaim customClaim = new CustomClaim(JWT.decode(token).getClaims()); |
| | | String refreshToken = JwtUtil.createToken(customClaim.convertToMap()); |
| | | |
| | | UserDTO user = entityConvertToDTO(this.getUserByUsername(customClaim.getUsername())); |
| | | user.setWorkspaceId(customClaim.getWorkspaceId()); |
| | | user.setAccessToken(refreshToken); |
| | | return Optional.of(user); |
| | | } |
| | | |
| | | /** |
| | | * Query a user by username. |
| | | * @param username |
| | | * @return |
| | | */ |
| | | private UserEntity getUserByUsername(String username) { |
| | | return mapper.selectOne(new QueryWrapper<UserEntity>() |
| | | .eq("username", username)); |
| | | } |
| | | |
| | | private UserDTO entityConvertToDTO(UserEntity entity) { |
| | | if (entity == null) { |
| | | return new UserDTO(); |
| | | } |
| | | return UserDTO.builder() |
| | | .userId(entity.getUserId()) |
| | | .username(entity.getUsername()) |
| | | .userType(entity.getUserType()) |
| | | .mqttUsername(entity.getMqttUsername()) |
| | | .mqttPassword(entity.getMqttPassword()) |
| | | .mqttAddr(new StringBuilder() |
| | | .append(mqttConfiguration.getProtocol().trim()) |
| | | .append("://") |
| | | .append(mqttConfiguration.getHost().trim()) |
| | | .append(":") |
| | | .append(mqttConfiguration.getPort()) |
| | | .toString()) |
| | | .build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.manage.dao.IWorkspaceMapper; |
| | | import com.dji.sample.manage.model.dto.WorkspaceDTO; |
| | | import com.dji.sample.manage.model.entity.WorkspaceEntity; |
| | | import com.dji.sample.manage.service.IWorkspaceService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class WorkspaceServiceImpl implements IWorkspaceService { |
| | | |
| | | @Autowired |
| | | private IWorkspaceMapper mapper; |
| | | |
| | | @Override |
| | | public Optional<WorkspaceDTO> getWorkspaceById(int id) { |
| | | return Optional.ofNullable(entityConvertToDto(mapper.selectById(id))); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<WorkspaceDTO> getWorkspaceByWorkspaceId(String workspaceId) { |
| | | return Optional.ofNullable(entityConvertToDto( |
| | | mapper.selectOne( |
| | | new LambdaQueryWrapper<WorkspaceEntity>() |
| | | .eq(WorkspaceEntity::getWorkspaceId, workspaceId)))); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into workspace data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private WorkspaceDTO entityConvertToDto(WorkspaceEntity entity) { |
| | | WorkspaceDTO.WorkspaceDTOBuilder builder = WorkspaceDTO.builder(); |
| | | if (entity == null) { |
| | | return builder.build(); |
| | | } |
| | | return builder |
| | | .id(entity.getId()) |
| | | .workspaceId(entity.getWorkspaceId()) |
| | | .platformName(entity.getPlatformName()) |
| | | .workspaceDesc(entity.getWorkspaceDesc()) |
| | | .workspaceName(entity.getWorkspaceName()) |
| | | .build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.controller; |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.model.WebSocketManager; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.map.model.dto.*; |
| | | import com.dji.sample.map.service.IWorkspaceElementService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("${url.map.prefix}${url.map.version}/workspaces") |
| | | public class WorkspaceElementController { |
| | | |
| | | @Autowired |
| | | private IWorkspaceElementService elementService; |
| | | |
| | | @Autowired |
| | | private ISendMessageService sendMessageService; |
| | | |
| | | /** |
| | | * In the first connection, pilot will send out this http request to obtain the group element list. |
| | | * Also, if pilot receives a group refresh instruction from WebSocket, |
| | | * it needs the same interface to request the group element list. |
| | | * @param workspaceId |
| | | * @param groupId |
| | | * @param isDistributed |
| | | * @return |
| | | */ |
| | | @GetMapping("/{workspace_id}/element-groups") |
| | | public ResponseResult<List<GroupDTO>> getAllElements(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @RequestParam(name = "group_id", required = false) String groupId, |
| | | @RequestParam(name = "is_distributed", required = false) Boolean isDistributed) { |
| | | List<GroupDTO> groupsList = elementService.getAllGroupsByWorkspaceId(workspaceId, groupId, isDistributed); |
| | | return ResponseResult.success(groupsList); |
| | | } |
| | | |
| | | /** |
| | | * When user draws a point, line or polygon on the PILOT/Web side. |
| | | * Save the element information to the database. |
| | | * @param request |
| | | * @param workspaceId |
| | | * @param groupId |
| | | * @param elementCreate |
| | | * @return |
| | | */ |
| | | @PostMapping("/{workspace_id}/element-groups/{group_id}/elements") |
| | | public ResponseResult saveElement(HttpServletRequest request, |
| | | @PathVariable(name = "workspace_id") String workspaceId, |
| | | @PathVariable(name = "group_id") String groupId, |
| | | @RequestBody ElementCreateDTO elementCreate) { |
| | | CustomClaim claims = (CustomClaim) request.getAttribute(TOKEN_CLAIM); |
| | | // Set the creator of the element |
| | | elementCreate.getResource().setUsername(claims.getUsername()); |
| | | |
| | | ResponseResult response = elementService.saveElement(groupId, elementCreate); |
| | | if (response.getCode() != ResponseResult.CODE_SUCCESS) { |
| | | return response; |
| | | } |
| | | |
| | | // Notify all WebSocket connections in this workspace to be updated when an element is created. |
| | | elementService.getElementByElementId(elementCreate.getId()) |
| | | .ifPresent(groupElement -> |
| | | sendMessageService.sendBatch( |
| | | WebSocketManager.getValueWithWorkspace(workspaceId), |
| | | CustomWebSocketMessage.<GroupElementDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.MAP_ELEMENT_CREATE.getCode()) |
| | | .data(groupElement) |
| | | .build())); |
| | | |
| | | return ResponseResult.success(new ConcurrentHashMap<>(Map.of("id", elementCreate.getId()))); |
| | | } |
| | | |
| | | /** |
| | | * When user edits a point, line or polygon on the PILOT/Web side. |
| | | * Update the element information to the database. |
| | | * @param request |
| | | * @param workspaceId |
| | | * @param elementId |
| | | * @param elementUpdate |
| | | * @return |
| | | */ |
| | | @PutMapping("/{workspace_id}/elements/{element_id}") |
| | | public ResponseResult updateElement(HttpServletRequest request, |
| | | @PathVariable(name = "workspace_id") String workspaceId, |
| | | @PathVariable(name = "element_id") String elementId, |
| | | @RequestBody ElementUpdateDTO elementUpdate) { |
| | | |
| | | CustomClaim claims = (CustomClaim) request.getAttribute(TOKEN_CLAIM); |
| | | |
| | | ResponseResult response = elementService.updateElement(elementId, elementUpdate, claims.getUsername()); |
| | | if (response.getCode() != ResponseResult.CODE_SUCCESS) { |
| | | return response; |
| | | } |
| | | |
| | | // Notify all WebSocket connections in this workspace to update when there is an element update. |
| | | elementService.getElementByElementId(elementId) |
| | | .ifPresent(groupElement -> |
| | | sendMessageService.sendBatch( |
| | | WebSocketManager.getValueWithWorkspace(workspaceId), |
| | | CustomWebSocketMessage.<GroupElementDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.MAP_ELEMENT_UPDATE.getCode()) |
| | | .data(groupElement) |
| | | .build())); |
| | | return response; |
| | | } |
| | | |
| | | /** |
| | | * When user delete a point, line or polygon on the PILOT/Web side, |
| | | * Delete the element information in the database. |
| | | * @param workspaceId |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | @DeleteMapping("/{workspace_id}/elements/{element_id}") |
| | | public ResponseResult deleteElement(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @PathVariable(name = "element_id") String elementId) { |
| | | |
| | | Optional<GroupElementDTO> elementOpt = elementService.getElementByElementId(elementId); |
| | | |
| | | ResponseResult response = elementService.deleteElement(elementId); |
| | | |
| | | // Notify all WebSocket connections in this workspace to update when an element is deleted. |
| | | if (ResponseResult.CODE_SUCCESS == response.getCode()) { |
| | | elementOpt.ifPresent(element -> |
| | | sendMessageService.sendBatch( |
| | | WebSocketManager.getValueWithWorkspace(workspaceId), |
| | | CustomWebSocketMessage.<WebSocketElementDelDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.MAP_ELEMENT_DELETE.getCode()) |
| | | .data(WebSocketElementDelDTO.builder() |
| | | .elementId(elementId) |
| | | .groupId(element.getGroupId()) |
| | | .build()) |
| | | .build())); |
| | | } |
| | | return response; |
| | | } |
| | | |
| | | /** |
| | | * Delete all the element information in this group based on the group id. |
| | | * @param workspaceId |
| | | * @param groupId |
| | | * @return |
| | | */ |
| | | @DeleteMapping("/{workspace_id}/element-groups/{group_id}/elements") |
| | | public ResponseResult deleteAllElementByGroupId(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @PathVariable(name = "group_id") String groupId) { |
| | | |
| | | ResponseResult response = elementService.deleteAllElementByGroupId(groupId); |
| | | |
| | | // Notify all WebSocket connections in this workspace to update when elements are deleted. |
| | | if (ResponseResult.CODE_SUCCESS == response.getCode()) { |
| | | |
| | | sendMessageService.sendBatch( |
| | | WebSocketManager.getValueWithWorkspace(workspaceId), |
| | | CustomWebSocketMessage.builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.MAP_GROUP_REFRESH.getCode()) |
| | | // Group ids that need to re-request data |
| | | .data(new ConcurrentHashMap<String, String[]>(Map.of("ids", new String[]{groupId}))) |
| | | .build()); |
| | | } |
| | | return response; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.map.model.entity.ElementCoordinateEntity; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | public interface IElementCoordinateMapper extends BaseMapper<ElementCoordinateEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.map.model.entity.GroupElementEntity; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | public interface IGroupElementMapper extends BaseMapper<GroupElementEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.map.model.entity.GroupEntity; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | public interface IGroupMapper extends BaseMapper<GroupEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Data |
| | | public class ContentPropertyDTO { |
| | | |
| | | private String color; |
| | | |
| | | @JsonProperty("clampToGround") |
| | | private Boolean clampToGround; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Builder |
| | | public class ElementCoordinateDTO { |
| | | |
| | | private Double longitude; |
| | | |
| | | private Double latitude; |
| | | |
| | | private Double altitude; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Data |
| | | public class ElementCreateDTO { |
| | | |
| | | private String id; |
| | | |
| | | private String name; |
| | | |
| | | private ElementResourceDTO resource; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.dji.sample.map.model.enums.ElementTypeEnum; |
| | | import lombok.Data; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Data |
| | | public class ElementLineStringDTO extends ElementType { |
| | | |
| | | private Double[][] coordinates; |
| | | |
| | | public ElementLineStringDTO() { |
| | | super(ElementTypeEnum.LINE_STRING.getDesc()); |
| | | } |
| | | |
| | | @Override |
| | | public List<ElementCoordinateDTO> convertToList() { |
| | | List<ElementCoordinateDTO> coordinateList = new ArrayList<>(); |
| | | for (Double[] coordinate : this.coordinates) { |
| | | coordinateList.add(ElementCoordinateDTO.builder() |
| | | .longitude(coordinate[0]) |
| | | .latitude(coordinate[1]) |
| | | .build()); |
| | | } |
| | | return coordinateList; |
| | | } |
| | | |
| | | @Override |
| | | public void adapterCoordinateType(List<ElementCoordinateDTO> coordinateList) { |
| | | if (CollectionUtils.isEmpty(coordinateList)) { |
| | | return; |
| | | } |
| | | this.coordinates = new Double[coordinateList.size()][2]; |
| | | for (int i = 0; i < this.coordinates.length; i++) { |
| | | this.coordinates[i][0] = coordinateList.get(i).getLongitude(); |
| | | this.coordinates[i][1] = coordinateList.get(i).getLatitude(); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.dji.sample.map.model.enums.ElementTypeEnum; |
| | | import lombok.Data; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Data |
| | | public class ElementPointDTO extends ElementType { |
| | | |
| | | private Double[] coordinates; |
| | | |
| | | public ElementPointDTO() { |
| | | super(ElementTypeEnum.POINT.getDesc()); |
| | | } |
| | | |
| | | @Override |
| | | public List<ElementCoordinateDTO> convertToList() { |
| | | List<ElementCoordinateDTO> coordinateList = new ArrayList<>(); |
| | | coordinateList.add(ElementCoordinateDTO.builder() |
| | | .longitude(this.coordinates[0]) |
| | | .latitude(this.coordinates[1]) |
| | | .altitude(this.coordinates.length == 3 ? this.coordinates[2] : null) |
| | | .build()); |
| | | return coordinateList; |
| | | } |
| | | |
| | | @Override |
| | | public void adapterCoordinateType(List<ElementCoordinateDTO> coordinateList) { |
| | | if (CollectionUtils.isEmpty(coordinateList)) { |
| | | return; |
| | | } |
| | | this.coordinates = new Double[]{ |
| | | coordinateList.get(0).getLongitude(), |
| | | coordinateList.get(0).getLatitude(), |
| | | coordinateList.get(0).getAltitude() |
| | | }; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.dji.sample.map.model.enums.ElementTypeEnum; |
| | | import lombok.Data; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Data |
| | | public class ElementPolygonDTO extends ElementType { |
| | | |
| | | private Double[][][] coordinates; |
| | | |
| | | public ElementPolygonDTO() { |
| | | super(ElementTypeEnum.POLYGON.getDesc()); |
| | | } |
| | | |
| | | @Override |
| | | public List<ElementCoordinateDTO> convertToList() { |
| | | List<ElementCoordinateDTO> coordinateList = new ArrayList<>(); |
| | | |
| | | for (Double[] coordinate : this.coordinates[0]) { |
| | | coordinateList.add(ElementCoordinateDTO.builder() |
| | | .longitude(coordinate[0]) |
| | | .latitude(coordinate[1]) |
| | | .build()); |
| | | } |
| | | return coordinateList; |
| | | } |
| | | |
| | | @Override |
| | | public void adapterCoordinateType(List<ElementCoordinateDTO> coordinateList) { |
| | | if (CollectionUtils.isEmpty(coordinateList)) { |
| | | return; |
| | | } |
| | | this.coordinates = new Double[1][coordinateList.size()][2]; |
| | | for (int i = 0; i < this.coordinates[0].length; i++) { |
| | | this.coordinates[0][i][0] = coordinateList.get(i).getLongitude(); |
| | | this.coordinates[0][i][1] = coordinateList.get(i).getLatitude(); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Data |
| | | public class ElementResourceDTO { |
| | | |
| | | private Integer type; |
| | | |
| | | @JsonProperty(value = "user_name") |
| | | private String username; |
| | | |
| | | private ResourceContentDTO content; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonSubTypes; |
| | | import com.fasterxml.jackson.annotation.JsonTypeInfo; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Data |
| | | @NoArgsConstructor |
| | | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", |
| | | include = JsonTypeInfo.As.EXISTING_PROPERTY, defaultImpl = ElementType.class) |
| | | @JsonSubTypes({ |
| | | @JsonSubTypes.Type(value = ElementPointDTO.class, name = "Point"), |
| | | @JsonSubTypes.Type(value = ElementLineStringDTO.class, name = "LineString"), |
| | | @JsonSubTypes.Type(value = ElementPolygonDTO.class, name = "Polygon") |
| | | }) |
| | | public abstract class ElementType { |
| | | |
| | | private String type; |
| | | |
| | | ElementType(String type) { |
| | | this.type = type; |
| | | } |
| | | |
| | | /** |
| | | * Convert coordinate data into objects and then add them to the collection. |
| | | * @return |
| | | */ |
| | | public abstract List<ElementCoordinateDTO> convertToList(); |
| | | |
| | | /** |
| | | * Converts coordinates in a collection of objects to a specific type. |
| | | * @param coordinateList |
| | | */ |
| | | public abstract void adapterCoordinateType(List<ElementCoordinateDTO> coordinateList); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/1 |
| | | */ |
| | | @Data |
| | | public class ElementUpdateDTO { |
| | | |
| | | private String name; |
| | | |
| | | private ResourceContentDTO content; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Builder |
| | | @Data |
| | | public class GroupDTO { |
| | | |
| | | private String id; |
| | | |
| | | private String name; |
| | | |
| | | private Integer type; |
| | | |
| | | private List<GroupElementDTO> elements; |
| | | |
| | | @JsonProperty(value = "is_distributed") |
| | | private Boolean isDistributed; |
| | | |
| | | @JsonProperty(value = "is_lock") |
| | | private Boolean isLock; |
| | | |
| | | @JsonProperty(value = "create_time") |
| | | private Long createTime; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonInclude; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Builder |
| | | @JsonInclude(JsonInclude.Include.NON_NULL) |
| | | public class GroupElementDTO { |
| | | private Integer display; |
| | | |
| | | @JsonProperty("id") |
| | | private String elementId; |
| | | |
| | | private String name; |
| | | |
| | | @JsonProperty(value = "create_time") |
| | | private Long createTime; |
| | | |
| | | @JsonProperty(value = "update_time") |
| | | private Long updateTime; |
| | | |
| | | private ElementResourceDTO resource; |
| | | |
| | | @JsonProperty("group_id") |
| | | private String groupId; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @Data |
| | | public class ResourceContentDTO { |
| | | |
| | | @Builder.Default |
| | | private String type = "Feature"; |
| | | |
| | | private ContentPropertyDTO properties; |
| | | |
| | | private ElementType geometry; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/2 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class WebSocketElementDelDTO { |
| | | |
| | | @JsonProperty("id") |
| | | private String elementId; |
| | | |
| | | @JsonProperty("group_id") |
| | | private String groupId; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @TableName(value = "map_element_coordinate") |
| | | public class ElementCoordinateEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField("element_id") |
| | | private String elementId; |
| | | |
| | | @TableField("longitude") |
| | | private Double longitude; |
| | | |
| | | @TableField("latitude") |
| | | private Double latitude; |
| | | |
| | | @TableField("altitude") |
| | | private Double altitude; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @TableName(value = "map_group_element") |
| | | public class GroupElementEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField("group_id") |
| | | private String groupId; |
| | | |
| | | @TableField("element_id") |
| | | private String elementId; |
| | | |
| | | @TableField("element_name") |
| | | private String elementName; |
| | | |
| | | @TableField("display") |
| | | private Integer display; |
| | | |
| | | @TableField("element_type") |
| | | private Integer elementType; |
| | | |
| | | @TableField("username") |
| | | private String username; |
| | | |
| | | @TableField("color") |
| | | private String color; |
| | | |
| | | @TableField("clamp_to_ground") |
| | | private Boolean clampToGround; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @TableName(value = "map_group") |
| | | public class GroupEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField("group_id") |
| | | private String groupId; |
| | | |
| | | @TableField("group_name") |
| | | private String groupName; |
| | | |
| | | @TableField("group_type") |
| | | private Integer groupType; |
| | | |
| | | @TableField("workspace_id") |
| | | private String workspaceId; |
| | | |
| | | @TableField("is_distributed") |
| | | private Boolean isDistributed; |
| | | |
| | | @TableField("is_lock") |
| | | private Boolean isLock; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.model.enums; |
| | | |
| | | import com.dji.sample.map.model.dto.ElementLineStringDTO; |
| | | import com.dji.sample.map.model.dto.ElementPointDTO; |
| | | import com.dji.sample.map.model.dto.ElementPolygonDTO; |
| | | import com.dji.sample.map.model.dto.ElementType; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | public enum ElementTypeEnum { |
| | | |
| | | POINT(0, "Point"), |
| | | |
| | | LINE_STRING(1, "LineString"), |
| | | |
| | | POLYGON(2, "Polygon"), |
| | | |
| | | UNKNOWN(-1, "Unknown"); |
| | | |
| | | private int val; |
| | | |
| | | private String desc; |
| | | |
| | | ElementTypeEnum(int val, String desc) { |
| | | this.val = val; |
| | | this.desc = desc; |
| | | } |
| | | |
| | | public static Optional<ElementType> findType(int val) { |
| | | if (POINT.val == val) { |
| | | return Optional.of(new ElementPointDTO()); |
| | | } |
| | | |
| | | if (LINE_STRING.val == val) { |
| | | return Optional.of(new ElementLineStringDTO()); |
| | | } |
| | | |
| | | if (POLYGON.val == val) { |
| | | return Optional.of(new ElementPolygonDTO()); |
| | | } |
| | | |
| | | return Optional.empty(); |
| | | } |
| | | |
| | | public String getDesc() { |
| | | return desc; |
| | | } |
| | | |
| | | public static int findVal(String desc) { |
| | | if (POINT.desc.equals(desc)) { |
| | | return POINT.val; |
| | | } |
| | | |
| | | if (LINE_STRING.desc.equals(desc)) { |
| | | return LINE_STRING.val; |
| | | } |
| | | |
| | | if (POLYGON.desc.equals(desc)) { |
| | | return POLYGON.val; |
| | | } |
| | | |
| | | return UNKNOWN.val; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.service; |
| | | |
| | | import com.dji.sample.map.model.dto.ElementCoordinateDTO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | public interface IElementCoordinateService { |
| | | |
| | | /** |
| | | * Query all the coordinates of the element according to its id. |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | List<ElementCoordinateDTO> getCoordinateByElementId(String elementId); |
| | | |
| | | /** |
| | | * Save all the coordinate data of this element. |
| | | * @param coordinate |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | Boolean saveCoordinate(List<ElementCoordinateDTO> coordinate, String elementId); |
| | | |
| | | /** |
| | | * Delete all the coordinates of the element according to its id. |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | Boolean deleteCoordinateByElementId(String elementId); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.service; |
| | | |
| | | import com.dji.sample.map.model.dto.ElementCreateDTO; |
| | | import com.dji.sample.map.model.dto.ElementUpdateDTO; |
| | | import com.dji.sample.map.model.dto.GroupElementDTO; |
| | | |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | public interface IGroupElementService { |
| | | |
| | | /** |
| | | * Query all the elements in this group based on the group id. |
| | | * @param groupId |
| | | * @return |
| | | */ |
| | | List<GroupElementDTO> getElementsByGroupId(String groupId); |
| | | |
| | | /** |
| | | * Save all the elements. |
| | | * @param groupId |
| | | * @param elementCreate |
| | | * @return |
| | | */ |
| | | Boolean saveElement(String groupId, ElementCreateDTO elementCreate); |
| | | |
| | | /** |
| | | * Query the element information based on the element id and update element. |
| | | * @param elementId |
| | | * @param elementUpdate |
| | | * @param username |
| | | * @return |
| | | */ |
| | | Boolean updateElement(String elementId, ElementUpdateDTO elementUpdate, String username); |
| | | |
| | | /** |
| | | * Delete the element based on the element id. |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | Boolean deleteElement(String elementId); |
| | | |
| | | /** |
| | | * Query an element based on the element id, including the coordinate information in the elements. |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | Optional<GroupElementDTO> getElementByElementId(String elementId); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.service; |
| | | |
| | | import com.dji.sample.map.model.dto.GroupDTO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | public interface IGroupService { |
| | | |
| | | /** |
| | | * Query all groups in the workspace based on the workspace's id. |
| | | * If the group id does not exist, do not add this filter condition. |
| | | * @param workspaceId |
| | | * @param groupId |
| | | * @param isDistributed Used to define if the group needs to be distributed. Default is true. |
| | | * @return |
| | | */ |
| | | List<GroupDTO> getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed); |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.service; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.map.model.dto.ElementCreateDTO; |
| | | import com.dji.sample.map.model.dto.ElementUpdateDTO; |
| | | import com.dji.sample.map.model.dto.GroupDTO; |
| | | import com.dji.sample.map.model.dto.GroupElementDTO; |
| | | |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | public interface IWorkspaceElementService { |
| | | |
| | | /** |
| | | * Query all groups in the workspace based on the workspace's id, |
| | | * including the information of the elements in the group, and the coordinate information in the elements. |
| | | * @param workspaceId |
| | | * @param groupId |
| | | * @param isDistributed |
| | | * @return |
| | | */ |
| | | List<GroupDTO> getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed); |
| | | |
| | | /** |
| | | * Save all the elements, including the information of the elements in the group, |
| | | * and the coordinate information in the elements. |
| | | * @param groupId |
| | | * @param elementCreate |
| | | * @return |
| | | */ |
| | | ResponseResult saveElement(String groupId, ElementCreateDTO elementCreate); |
| | | |
| | | /** |
| | | * Update the element information based on the element id, |
| | | * including the information of the elements in the group, and the coordinate information in the elements. |
| | | * @param elementId |
| | | * @param elementUpdate |
| | | * @param username |
| | | * @return |
| | | */ |
| | | ResponseResult updateElement(String elementId, ElementUpdateDTO elementUpdate, String username); |
| | | |
| | | /** |
| | | * Delete the element information based on the element id, |
| | | * including the information of the elements in the group, and the coordinate information in the elements. |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | ResponseResult deleteElement(String elementId); |
| | | |
| | | /** |
| | | * Query an element based on the element id, |
| | | * including the information of the elements in the group, and the coordinate information in the elements. |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | Optional<GroupElementDTO> getElementByElementId(String elementId); |
| | | |
| | | /** |
| | | * Delete all the elements information based on the group id, |
| | | * including the information of the elements in the group, and the coordinate information in the elements. |
| | | * @param groupId |
| | | * @return |
| | | */ |
| | | ResponseResult deleteAllElementByGroupId(String groupId); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.dji.sample.map.dao.IElementCoordinateMapper; |
| | | import com.dji.sample.map.model.dto.ElementCoordinateDTO; |
| | | import com.dji.sample.map.model.entity.ElementCoordinateEntity; |
| | | import com.dji.sample.map.service.IElementCoordinateService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class ElementCoordinateServiceImpl implements IElementCoordinateService { |
| | | |
| | | @Autowired |
| | | private IElementCoordinateMapper mapper; |
| | | |
| | | @Override |
| | | public List<ElementCoordinateDTO> getCoordinateByElementId(String elementId) { |
| | | return mapper.selectList( |
| | | new LambdaQueryWrapper<ElementCoordinateEntity>() |
| | | .eq(ElementCoordinateEntity::getElementId, elementId)) |
| | | .stream() |
| | | .map(this::entityConvertToDto) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean saveCoordinate(List<ElementCoordinateDTO> coordinateList, String elementId) { |
| | | for (ElementCoordinateDTO coordinate : coordinateList) { |
| | | ElementCoordinateEntity entity = this.dtoConvertToEntity(coordinate); |
| | | entity.setElementId(elementId); |
| | | |
| | | int insert = mapper.insert(entity); |
| | | if (insert <= 0) { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean deleteCoordinateByElementId(String elementId) { |
| | | return mapper.delete(new LambdaUpdateWrapper<ElementCoordinateEntity>() |
| | | .eq(ElementCoordinateEntity::getElementId, elementId)) > 0; |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into coordinate data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private ElementCoordinateDTO entityConvertToDto(ElementCoordinateEntity entity) { |
| | | ElementCoordinateDTO.ElementCoordinateDTOBuilder builder = ElementCoordinateDTO.builder(); |
| | | if (entity == null) { |
| | | return builder.build(); |
| | | } |
| | | |
| | | return builder |
| | | .longitude(entity.getLongitude()) |
| | | .latitude(entity.getLatitude()) |
| | | .altitude(entity.getAltitude()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert the received coordinate object into a database entity object. |
| | | * @param coordinate |
| | | * @return |
| | | */ |
| | | private ElementCoordinateEntity dtoConvertToEntity(ElementCoordinateDTO coordinate) { |
| | | ElementCoordinateEntity.ElementCoordinateEntityBuilder builder = ElementCoordinateEntity.builder(); |
| | | |
| | | if (coordinate == null) { |
| | | return builder.build(); |
| | | } |
| | | |
| | | return builder |
| | | .altitude(coordinate.getAltitude()) |
| | | .latitude(coordinate.getLatitude()) |
| | | .longitude(coordinate.getLongitude()) |
| | | .build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.map.dao.IGroupElementMapper; |
| | | import com.dji.sample.map.model.dto.*; |
| | | import com.dji.sample.map.model.entity.GroupElementEntity; |
| | | import com.dji.sample.map.model.enums.ElementTypeEnum; |
| | | import com.dji.sample.map.service.IElementCoordinateService; |
| | | import com.dji.sample.map.service.IGroupElementService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class GroupElementServiceImpl implements IGroupElementService { |
| | | |
| | | @Autowired |
| | | private IGroupElementMapper mapper; |
| | | |
| | | @Autowired |
| | | private IElementCoordinateService elementCoordinateService; |
| | | |
| | | @Override |
| | | public List<GroupElementDTO> getElementsByGroupId(String groupId) { |
| | | List<GroupElementEntity> elementList = mapper.selectList( |
| | | new LambdaQueryWrapper<GroupElementEntity>() |
| | | .eq(GroupElementEntity::getGroupId, groupId)); |
| | | |
| | | List<GroupElementDTO> groupElementList = new ArrayList<>(); |
| | | for (GroupElementEntity elementEntity : elementList) { |
| | | |
| | | GroupElementDTO groupElement = this.entityConvertToDto(elementEntity); |
| | | groupElementList.add(groupElement); |
| | | |
| | | this.addCoordinateToElement(groupElement, elementEntity); |
| | | } |
| | | return groupElementList; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean saveElement(String groupId, ElementCreateDTO elementCreate) { |
| | | Optional<GroupElementEntity> groupElementOpt = this.getEntityByElementId(elementCreate.getId()); |
| | | |
| | | if (groupElementOpt.isPresent()) { |
| | | return false; |
| | | } |
| | | GroupElementEntity groupElement = this.createDtoConvertToEntity(elementCreate); |
| | | groupElement.setGroupId(groupId); |
| | | |
| | | return mapper.insert(groupElement) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean updateElement(String elementId, ElementUpdateDTO elementUpdate, String username) { |
| | | Optional<GroupElementEntity> groupElementOpt = this.getEntityByElementId(elementId); |
| | | if (groupElementOpt.isEmpty()) { |
| | | return false; |
| | | } |
| | | |
| | | GroupElementEntity groupElement = groupElementOpt.get(); |
| | | groupElement.setUsername(username); |
| | | this.updateEntityWithDto(elementUpdate, groupElement); |
| | | |
| | | return mapper.updateById(groupElement) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean deleteElement(String elementId) { |
| | | Optional<GroupElementEntity> groupElementOpt = this.getEntityByElementId(elementId); |
| | | if (groupElementOpt.isEmpty()) { |
| | | return true; |
| | | } |
| | | |
| | | GroupElementEntity groupElement = groupElementOpt.get(); |
| | | return mapper.deleteById(groupElement.getId()) > 0; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public Optional<GroupElementDTO> getElementByElementId(String elementId) { |
| | | Optional<GroupElementEntity> elementEntityOpt = this.getEntityByElementId(elementId); |
| | | if (elementEntityOpt.isEmpty()) { |
| | | return Optional.empty(); |
| | | } |
| | | GroupElementEntity elementEntity = elementEntityOpt.get(); |
| | | GroupElementDTO groupElement = this.entityConvertToDto(elementEntity); |
| | | |
| | | this.addCoordinateToElement(groupElement, elementEntity); |
| | | |
| | | return Optional.ofNullable(groupElement); |
| | | } |
| | | |
| | | /** |
| | | * Adds the received coordinate data to the element object. |
| | | * @param element |
| | | * @param elementEntity |
| | | */ |
| | | private void addCoordinateToElement(GroupElementDTO element, GroupElementEntity elementEntity) { |
| | | Optional<ElementType> coordinateOpt = ElementTypeEnum.findType(elementEntity.getElementType()); |
| | | if (coordinateOpt.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | ElementType elementType = coordinateOpt.get(); |
| | | |
| | | element.getResource().setContent( |
| | | ResourceContentDTO.builder() |
| | | .properties(ContentPropertyDTO.builder() |
| | | .clampToGround(elementEntity.getClampToGround()) |
| | | .color(elementEntity.getColor()) |
| | | .build()) |
| | | .geometry(elementType) |
| | | .build()); |
| | | |
| | | elementType.adapterCoordinateType( |
| | | elementCoordinateService.getCoordinateByElementId(elementEntity.getElementId())); |
| | | } |
| | | |
| | | /** |
| | | * Query an element based on the element id。 |
| | | * @param elementId |
| | | * @return |
| | | */ |
| | | private Optional<GroupElementEntity> getEntityByElementId(String elementId) { |
| | | return Optional.ofNullable(mapper.selectOne( |
| | | new LambdaQueryWrapper<GroupElementEntity>() |
| | | .eq(GroupElementEntity::getElementId, elementId))); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into element data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private GroupElementDTO entityConvertToDto(GroupElementEntity entity) { |
| | | GroupElementDTO.GroupElementDTOBuilder builder = GroupElementDTO.builder(); |
| | | if (entity == null) { |
| | | return builder.build(); |
| | | } |
| | | |
| | | return builder |
| | | .display(entity.getDisplay()) |
| | | .groupId(entity.getGroupId()) |
| | | .elementId(entity.getElementId()) |
| | | .name(entity.getElementName()) |
| | | .createTime(entity.getCreateTime()) |
| | | .updateTime(entity.getUpdateTime()) |
| | | .resource(ElementResourceDTO.builder() |
| | | .type(entity.getElementType()) |
| | | .username(entity.getUsername()) |
| | | .build()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert the received element object into a database entity object. |
| | | * @param elementCreate |
| | | * @return |
| | | */ |
| | | private GroupElementEntity createDtoConvertToEntity(ElementCreateDTO elementCreate) { |
| | | ContentPropertyDTO properties = elementCreate.getResource().getContent().getProperties(); |
| | | return GroupElementEntity.builder() |
| | | .elementId(elementCreate.getId()) |
| | | .elementName(elementCreate.getName()) |
| | | .username(elementCreate.getResource().getUsername()) |
| | | .elementType(ElementTypeEnum.findVal(elementCreate.getResource().getContent().getGeometry().getType())) |
| | | .clampToGround(properties.getClampToGround() != null && properties.getClampToGround()) |
| | | .color(properties.getColor()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * Add the content that needs to be updated to the entity object to be updated. |
| | | * @param elementUpdate |
| | | * @param groupElement |
| | | */ |
| | | private void updateEntityWithDto(ElementUpdateDTO elementUpdate, GroupElementEntity groupElement) { |
| | | if (elementUpdate == null || groupElement == null) { |
| | | return; |
| | | } |
| | | |
| | | groupElement.setElementName(elementUpdate.getName()); |
| | | groupElement.setElementType(ElementTypeEnum.findVal(elementUpdate.getContent().getGeometry().getType())); |
| | | groupElement.setColor(elementUpdate.getContent().getProperties().getColor()); |
| | | |
| | | boolean clampToGround = elementUpdate.getContent().getProperties().getClampToGround(); |
| | | groupElement.setClampToGround(clampToGround); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.map.dao.IGroupMapper; |
| | | import com.dji.sample.map.model.dto.GroupDTO; |
| | | import com.dji.sample.map.model.entity.GroupEntity; |
| | | import com.dji.sample.map.service.IGroupElementService; |
| | | import com.dji.sample.map.service.IGroupService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/29 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class GroupServiceImpl implements IGroupService { |
| | | |
| | | @Autowired |
| | | private IGroupMapper mapper; |
| | | |
| | | @Autowired |
| | | private IGroupElementService groupElementService; |
| | | |
| | | @Override |
| | | public List<GroupDTO> getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed) { |
| | | |
| | | return mapper.selectList( |
| | | new LambdaQueryWrapper<GroupEntity>() |
| | | .eq(GroupEntity::getWorkspaceId, workspaceId) |
| | | .eq(StringUtils.hasText(groupId), GroupEntity::getGroupId, groupId) |
| | | .eq(isDistributed != null, GroupEntity::getIsDistributed, isDistributed)) |
| | | .stream() |
| | | .map(this::entityConvertToDto) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into group data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private GroupDTO entityConvertToDto(GroupEntity entity) { |
| | | GroupDTO.GroupDTOBuilder builder = GroupDTO.builder(); |
| | | |
| | | if (entity == null) { |
| | | return builder.build(); |
| | | } |
| | | |
| | | return builder |
| | | .id(entity.getGroupId()) |
| | | .name(entity.getGroupName()) |
| | | .type(entity.getGroupType()) |
| | | .isLock(entity.getIsLock()) |
| | | .isDistributed(entity.getIsDistributed()) |
| | | .createTime(entity.getCreateTime()) |
| | | .build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.map.service.impl; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.map.model.dto.ElementCreateDTO; |
| | | import com.dji.sample.map.model.dto.ElementUpdateDTO; |
| | | import com.dji.sample.map.model.dto.GroupDTO; |
| | | import com.dji.sample.map.model.dto.GroupElementDTO; |
| | | import com.dji.sample.map.service.IElementCoordinateService; |
| | | import com.dji.sample.map.service.IGroupElementService; |
| | | import com.dji.sample.map.service.IGroupService; |
| | | import com.dji.sample.map.service.IWorkspaceElementService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/11/30 |
| | | */ |
| | | @Transactional |
| | | @Service |
| | | public class WorkspaceElementServiceImpl implements IWorkspaceElementService { |
| | | |
| | | @Autowired |
| | | private IGroupService groupService; |
| | | |
| | | @Autowired |
| | | private IGroupElementService groupElementService; |
| | | |
| | | @Autowired |
| | | private IElementCoordinateService elementCoordinateService; |
| | | |
| | | @Override |
| | | public List<GroupDTO> getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed) { |
| | | List<GroupDTO> groupList = groupService.getAllGroupsByWorkspaceId(workspaceId, groupId, isDistributed); |
| | | groupList.forEach(group -> group.setElements( |
| | | groupElementService.getElementsByGroupId(group.getId()) |
| | | )); |
| | | return groupList; |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult saveElement(String groupId, ElementCreateDTO elementCreate) { |
| | | boolean saveElement = groupElementService.saveElement(groupId, elementCreate); |
| | | if (!saveElement) { |
| | | return ResponseResult.error("Failed to save the element."); |
| | | } |
| | | |
| | | // save coordinate |
| | | boolean saveCoordinate = elementCoordinateService.saveCoordinate( |
| | | elementCreate.getResource().getContent().getGeometry().convertToList(), elementCreate.getId()); |
| | | |
| | | return saveCoordinate ? |
| | | ResponseResult.success() : ResponseResult.error("Failed to save the coordinate."); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public ResponseResult updateElement(String elementId, ElementUpdateDTO elementUpdate, String username) { |
| | | boolean updElement = groupElementService.updateElement(elementId, elementUpdate, username); |
| | | if (!updElement) { |
| | | return ResponseResult.error("Failed to update the element."); |
| | | } |
| | | |
| | | // delete all coordinates according to element id. |
| | | boolean delCoordinate = elementCoordinateService.deleteCoordinateByElementId(elementId); |
| | | // save coordinate |
| | | boolean saveCoordinate = elementCoordinateService.saveCoordinate( |
| | | elementUpdate.getContent().getGeometry().convertToList(), elementId); |
| | | |
| | | return delCoordinate && saveCoordinate ? |
| | | ResponseResult.success() : ResponseResult.error("Failed to update the coordinate."); |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult deleteElement(String elementId) { |
| | | boolean delElement = groupElementService.deleteElement(elementId); |
| | | if (!delElement) { |
| | | return ResponseResult.error("Failed to delete the element."); |
| | | } |
| | | |
| | | // delete all coordinates according to element id. |
| | | boolean delCoordinate = elementCoordinateService.deleteCoordinateByElementId(elementId); |
| | | |
| | | return delCoordinate ? |
| | | ResponseResult.success() : ResponseResult.error("Failed to delete the coordinate."); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<GroupElementDTO> getElementByElementId(String elementId) { |
| | | return groupElementService.getElementByElementId(elementId); |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult deleteAllElementByGroupId(String groupId) { |
| | | List<GroupElementDTO> groupElementList = groupElementService.getElementsByGroupId(groupId); |
| | | for (GroupElementDTO groupElement : groupElementList) { |
| | | ResponseResult response = this.deleteElement(groupElement.getElementId()); |
| | | if (ResponseResult.CODE_SUCCESS != response.getCode()) { |
| | | return response; |
| | | } |
| | | } |
| | | return ResponseResult.success(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.controller; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.media.model.MediaFileDTO; |
| | | import com.dji.sample.media.service.IFileService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("${url.media.prefix}${url.media.version}/files") |
| | | public class FileController { |
| | | |
| | | @Autowired |
| | | private IFileService fileService; |
| | | |
| | | /** |
| | | * Get information about all the media files in this workspace based on the workspace id. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | @GetMapping("/{workspace_id}/files") |
| | | public ResponseResult<List<MediaFileDTO>> getFilesList(@PathVariable(name = "workspace_id") String workspaceId) { |
| | | List<MediaFileDTO> filesList = fileService.getAllFilesByWorkspaceId(workspaceId); |
| | | return ResponseResult.success(filesList); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.controller; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.media.model.FileUploadDTO; |
| | | import com.dji.sample.media.service.IMediaService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/7 |
| | | */ |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("${url.media.prefix}${url.media.version}/workspaces") |
| | | public class MediaController { |
| | | |
| | | @Autowired |
| | | private IMediaService mediaService; |
| | | |
| | | /** |
| | | * Check if the file has been uploaded by the fingerprint. |
| | | * @param workspaceId |
| | | * @param file |
| | | * @return |
| | | */ |
| | | @PostMapping("/{workspace_id}/fast-upload") |
| | | public ResponseResult fastUpload(@PathVariable(name = "workspace_id") String workspaceId, @RequestBody FileUploadDTO file) { |
| | | |
| | | boolean isExist = mediaService.fastUpload(workspaceId, file.getFingerprint()); |
| | | |
| | | return isExist ? ResponseResult.success() : ResponseResult.error(file.getFingerprint() + "already exists."); |
| | | } |
| | | |
| | | /** |
| | | * When the file is uploaded to the storage server by pilot, |
| | | * the basic information of the file is reported through this interface. |
| | | * @param workspaceId |
| | | * @param file |
| | | * @return |
| | | */ |
| | | @PostMapping("/{workspace_id}/upload-callback") |
| | | public ResponseResult<String> uploadCallback(@PathVariable(name = "workspace_id") String workspaceId, @RequestBody FileUploadDTO file) { |
| | | mediaService.saveMediaFile(workspaceId, file); |
| | | return ResponseResult.success(file.getObjectKey()); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Query the files that already exist in this workspace based on the workspace id and the collection of tiny fingerprints. |
| | | * @param workspaceId |
| | | * @param tinyFingerprints |
| | | * @return |
| | | */ |
| | | @GetMapping("/{workspace_id}/files/tiny-fingerprints") |
| | | public ResponseResult<Map<String, List<String>>> uploadCallback(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @RequestParam(value = "tiny_fingerprint") List<String> tinyFingerprints) { |
| | | List<String> tinyFingerprintList = mediaService.getAllTinyFingerprintsByWorkspaceId(workspaceId); |
| | | List<String> existingList = tinyFingerprints |
| | | .stream() |
| | | .filter(tinyFingerprintList::contains) |
| | | .collect(Collectors.toList()); |
| | | return ResponseResult.success(new ConcurrentHashMap<>(Map.of("tiny_fingerprints", existingList))); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.media.model.MediaFileEntity; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | public interface IFileMapper extends BaseMapper<MediaFileEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.model; |
| | | |
| | | import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse; |
| | | import io.minio.credentials.Credentials; |
| | | import lombok.Data; |
| | | |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/7 |
| | | */ |
| | | @Data |
| | | public class CredentialsDTO { |
| | | |
| | | private String accessKeyId; |
| | | |
| | | private String accessKeySecret; |
| | | |
| | | private Integer expire; |
| | | |
| | | private String securityToken; |
| | | |
| | | public CredentialsDTO(Credentials credentials, int expire) { |
| | | this.accessKeyId = credentials.accessKey(); |
| | | this.accessKeySecret = credentials.secretKey(); |
| | | this.securityToken = credentials.sessionToken(); |
| | | this.expire = expire; |
| | | } |
| | | |
| | | public CredentialsDTO(AssumeRoleResponse.Credentials credentials, long expire) { |
| | | this.accessKeyId = credentials.getAccessKeyId(); |
| | | this.accessKeySecret = credentials.getAccessKeySecret(); |
| | | this.securityToken = credentials.getSecurityToken(); |
| | | this.expire = Math.toIntExact(expire); |
| | | } |
| | | |
| | | public CredentialsDTO() { |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.model; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/7 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class FileExtensionDTO { |
| | | |
| | | private String droneModelKey; |
| | | |
| | | private Boolean isOriginal; |
| | | |
| | | private String payloadModelKey; |
| | | |
| | | private String tinnyFingerprint; |
| | | |
| | | private String sn; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.model; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/7 |
| | | */ |
| | | @Data |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class FileMetadataDTO { |
| | | |
| | | private Double absoluteAltitude; |
| | | |
| | | @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssX") |
| | | private Date createdTime; |
| | | |
| | | private Double gimbalYawDegree; |
| | | |
| | | private PositionDTO photoedPosition; |
| | | |
| | | private PositionDTO shootPosition; |
| | | |
| | | private Double relativeAltitude; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/7 |
| | | */ |
| | | @Data |
| | | public class FileUploadDTO { |
| | | |
| | | private FileExtensionDTO ext; |
| | | |
| | | private String fingerprint; |
| | | |
| | | private String name; |
| | | |
| | | private String path; |
| | | |
| | | private FileMetadataDTO metadata; |
| | | |
| | | private String objectKey; |
| | | |
| | | private Integer subFileType; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.model; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class MediaFileDTO { |
| | | |
| | | private String fileName; |
| | | |
| | | private String filePath; |
| | | |
| | | private String objectKey; |
| | | |
| | | private String subFileType; |
| | | |
| | | private Boolean isOriginal; |
| | | |
| | | private String drone; |
| | | |
| | | private String payload; |
| | | |
| | | private String tinnyFingerprint; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.model; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | @TableName(value = "media_file") |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class MediaFileEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField("file_name") |
| | | private String fileName; |
| | | |
| | | @TableField("file_path") |
| | | private String filePath; |
| | | |
| | | @TableField("workspace_id") |
| | | private String workspaceId; |
| | | |
| | | @TableField("fingerprint") |
| | | private String fingerprint; |
| | | |
| | | @TableField("tinny_fingerprint") |
| | | private String tinnyFingerprint; |
| | | |
| | | @TableField("object_key") |
| | | private String objectKey; |
| | | |
| | | @TableField("sub_file_type") |
| | | private Integer subFileType; |
| | | |
| | | @TableField("is_original") |
| | | private Boolean isOriginal; |
| | | |
| | | @TableField("drone") |
| | | private String drone; |
| | | |
| | | @TableField("payload") |
| | | private String payload; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | } |
| | | |
| New file |
| | |
| | | package com.dji.sample.media.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/7 |
| | | */ |
| | | @Data |
| | | public class PositionDTO { |
| | | |
| | | private Double lat; |
| | | |
| | | private Double lng; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.model; |
| | | |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/7 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| | | public class StsCredentialsDTO { |
| | | |
| | | private String bucket; |
| | | |
| | | private CredentialsDTO credentials; |
| | | |
| | | private String endpoint; |
| | | |
| | | private String objectKeyPrefix; |
| | | |
| | | private String provider; |
| | | |
| | | private String region; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.service; |
| | | |
| | | import com.dji.sample.media.model.FileUploadDTO; |
| | | import com.dji.sample.media.model.MediaFileDTO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | public interface IFileService { |
| | | |
| | | /** |
| | | * Query if the file already exists based on the workspace id and the fingerprint of the file. |
| | | * @param workspaceId |
| | | * @param fingerprint |
| | | * @return |
| | | */ |
| | | Boolean checkExist(String workspaceId, String fingerprint); |
| | | |
| | | /** |
| | | * Save the basic information of the file to the database. |
| | | * @param workspaceId |
| | | * @param file |
| | | * @return |
| | | */ |
| | | Integer saveFile(String workspaceId, FileUploadDTO file); |
| | | |
| | | /** |
| | | * Query information about all files in this workspace based on the workspace id. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | List<MediaFileDTO> getAllFilesByWorkspaceId(String workspaceId); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.service; |
| | | |
| | | import com.dji.sample.media.model.FileUploadDTO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | public interface IMediaService { |
| | | |
| | | /** |
| | | * Check if the file has been uploaded by the fingerprint. |
| | | * @param workspaceId |
| | | * @param fingerprint |
| | | * @return |
| | | */ |
| | | Boolean fastUpload(String workspaceId, String fingerprint); |
| | | |
| | | /** |
| | | * Save the basic information of the file to the database. |
| | | * @param workspaceId |
| | | * @param file |
| | | * @return |
| | | */ |
| | | Integer saveMediaFile(String workspaceId, FileUploadDTO file); |
| | | |
| | | /** |
| | | * Query tiny fingerprints about all files in this workspace based on the workspace id. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | List<String> getAllTinyFingerprintsByWorkspaceId(String workspaceId); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; |
| | | import com.dji.sample.manage.service.IDeviceDictionaryService; |
| | | import com.dji.sample.media.dao.IFileMapper; |
| | | import com.dji.sample.media.model.FileUploadDTO; |
| | | import com.dji.sample.media.model.MediaFileDTO; |
| | | import com.dji.sample.media.model.MediaFileEntity; |
| | | import com.dji.sample.media.service.IFileService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class FileServiceImpl implements IFileService { |
| | | |
| | | |
| | | @Autowired |
| | | private IFileMapper mapper; |
| | | |
| | | @Autowired |
| | | private IDeviceDictionaryService deviceDictionaryService; |
| | | |
| | | @Override |
| | | public Boolean checkExist(String workspaceId, String fingerprint) { |
| | | MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>() |
| | | .eq(MediaFileEntity::getWorkspaceId, workspaceId) |
| | | .eq(MediaFileEntity::getFingerprint, fingerprint)); |
| | | return fileEntity != null; |
| | | } |
| | | |
| | | @Override |
| | | public Integer saveFile(String workspaceId, FileUploadDTO file) { |
| | | MediaFileEntity fileEntity = this.fileUploadConvertToEntity(file); |
| | | fileEntity.setWorkspaceId(workspaceId); |
| | | return mapper.insert(fileEntity); |
| | | } |
| | | |
| | | @Override |
| | | public List<MediaFileDTO> getAllFilesByWorkspaceId(String workspaceId) { |
| | | return mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>() |
| | | .eq(MediaFileEntity::getWorkspaceId, workspaceId)) |
| | | .stream() |
| | | .map(this::entityConvertToDto) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * Convert the received file object into a database entity object. |
| | | * @param file |
| | | * @return |
| | | */ |
| | | private MediaFileEntity fileUploadConvertToEntity(FileUploadDTO file) { |
| | | MediaFileEntity.MediaFileEntityBuilder builder = MediaFileEntity.builder(); |
| | | |
| | | if (file != null) { |
| | | builder.fileName(file.getName()) |
| | | .filePath(file.getPath()) |
| | | .fingerprint(file.getFingerprint()) |
| | | .objectKey(file.getObjectKey()) |
| | | .subFileType(file.getSubFileType()) |
| | | .isOriginal(file.getExt().getIsOriginal()) |
| | | .drone(file.getExt().getSn()) |
| | | .tinnyFingerprint(file.getExt().getTinnyFingerprint()); |
| | | |
| | | // domain-type-subType |
| | | int[] payloadModel = Arrays.stream(file.getExt().getPayloadModelKey().split("-")) |
| | | .map(Integer::valueOf) |
| | | .mapToInt(Integer::intValue) |
| | | .toArray(); |
| | | Optional<DeviceDictionaryDTO> payloadDict = deviceDictionaryService |
| | | .getOneDictionaryInfoByDomainTypeSubType(payloadModel[0], payloadModel[1], payloadModel[2]); |
| | | payloadDict.ifPresent(payload -> builder.payload(payload.getDeviceName())); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into file data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private MediaFileDTO entityConvertToDto(MediaFileEntity entity) { |
| | | MediaFileDTO.MediaFileDTOBuilder builder = MediaFileDTO.builder(); |
| | | |
| | | if (entity != null) { |
| | | builder.fileName(entity.getFileName()) |
| | | .filePath(entity.getFilePath()) |
| | | .isOriginal(entity.getIsOriginal()) |
| | | .objectKey(entity.getObjectKey()) |
| | | .tinnyFingerprint(entity.getTinnyFingerprint()) |
| | | .payload(entity.getPayload()) |
| | | .drone(entity.getDrone()); |
| | | |
| | | } |
| | | |
| | | return builder.build(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.media.service.impl; |
| | | |
| | | import com.dji.sample.media.model.FileUploadDTO; |
| | | import com.dji.sample.media.model.MediaFileDTO; |
| | | import com.dji.sample.media.service.IFileService; |
| | | import com.dji.sample.media.service.IMediaService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.2 |
| | | * @date 2021/12/9 |
| | | */ |
| | | @Service |
| | | public class MediaServiceImpl implements IMediaService { |
| | | |
| | | @Autowired |
| | | private IFileService fileService; |
| | | |
| | | @Override |
| | | public Boolean fastUpload(String workspaceId, String fingerprint) { |
| | | return fileService.checkExist(workspaceId, fingerprint); |
| | | } |
| | | |
| | | @Override |
| | | public Integer saveMediaFile(String workspaceId, FileUploadDTO file) { |
| | | return fileService.saveFile(workspaceId, file); |
| | | } |
| | | |
| | | @Override |
| | | public List<String> getAllTinyFingerprintsByWorkspaceId(String workspaceId) { |
| | | return fileService.getAllFilesByWorkspaceId(workspaceId) |
| | | .stream() |
| | | .map(MediaFileDTO::getTinnyFingerprint) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.storage.controller; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.oss.model.AliyunOSSConfiguration; |
| | | import com.dji.sample.component.oss.model.MinIOConfiguration; |
| | | import com.dji.sample.media.model.StsCredentialsDTO; |
| | | import com.dji.sample.storage.service.IStorageService; |
| | | import com.dji.sample.storage.service.impl.AliyunStorageServiceImpl; |
| | | import com.dji.sample.storage.service.impl.MinIOStorageServiceImpl; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/29 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("${url.storage.prefix}${url.storage.version}/workspaces/") |
| | | public class StorageController { |
| | | |
| | | private IStorageService storageService; |
| | | |
| | | @Autowired |
| | | private void setOssService(@Autowired AliyunStorageServiceImpl aliyunStorageService, |
| | | @Autowired MinIOStorageServiceImpl minIOStorageService) { |
| | | if (AliyunOSSConfiguration.enable) { |
| | | this.storageService = aliyunStorageService; |
| | | return; |
| | | } |
| | | if (MinIOConfiguration.enable) { |
| | | this.storageService = minIOStorageService; |
| | | return; |
| | | } |
| | | throw new NullPointerException("storageService is null."); |
| | | } |
| | | /** |
| | | * Get temporary credentials for uploading the media and wayline in DJI Pilot. |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | @PostMapping("/{workspace_id}/sts") |
| | | public ResponseResult<StsCredentialsDTO> getSTSCredentials(@PathVariable(name = "workspace_id") String workspaceId) { |
| | | |
| | | StsCredentialsDTO stsCredentials = storageService.getSTSCredentials(); |
| | | return ResponseResult.success(stsCredentials); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.storage.service; |
| | | |
| | | import com.dji.sample.media.model.StsCredentialsDTO; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/29 |
| | | */ |
| | | public interface IStorageService { |
| | | |
| | | /** |
| | | * Get custom temporary credentials object for uploading the media and wayline. |
| | | * @return temporary credentials object |
| | | */ |
| | | StsCredentialsDTO getSTSCredentials(); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.storage.service.impl; |
| | | |
| | | import com.dji.sample.component.oss.model.AliyunOSSConfiguration; |
| | | import com.dji.sample.component.oss.service.impl.AliyunOssServiceImpl; |
| | | import com.dji.sample.media.model.StsCredentialsDTO; |
| | | import com.dji.sample.storage.service.IStorageService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2022/3/9 |
| | | */ |
| | | @Service |
| | | public class AliyunStorageServiceImpl implements IStorageService { |
| | | |
| | | @Autowired |
| | | private AliyunOssServiceImpl ossService; |
| | | |
| | | @Override |
| | | public StsCredentialsDTO getSTSCredentials() { |
| | | return StsCredentialsDTO.builder() |
| | | .endpoint(AliyunOSSConfiguration.endpoint) |
| | | .bucket(AliyunOSSConfiguration.bucket) |
| | | .credentials(ossService.getCredentials()) |
| | | .provider(AliyunOSSConfiguration.PROVIDER) |
| | | .objectKeyPrefix(AliyunOSSConfiguration.objectDirPrefix) |
| | | .region(AliyunOSSConfiguration.region) |
| | | .build(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.storage.service.impl; |
| | | |
| | | import com.dji.sample.component.oss.model.MinIOConfiguration; |
| | | import com.dji.sample.component.oss.service.impl.MinIOServiceImpl; |
| | | import com.dji.sample.media.model.StsCredentialsDTO; |
| | | import com.dji.sample.storage.service.IStorageService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/31 |
| | | */ |
| | | @Service |
| | | public class MinIOStorageServiceImpl implements IStorageService { |
| | | |
| | | @Autowired |
| | | private MinIOServiceImpl ossService; |
| | | |
| | | @Override |
| | | public StsCredentialsDTO getSTSCredentials() { |
| | | return StsCredentialsDTO.builder() |
| | | .endpoint(MinIOConfiguration.endpoint) |
| | | .bucket(MinIOConfiguration.bucket) |
| | | .credentials(ossService.getCredentials()) |
| | | .provider(MinIOConfiguration.PROVIDER) |
| | | .objectKeyPrefix(MinIOConfiguration.objectDirPrefix) |
| | | .region(MinIOConfiguration.region) |
| | | .build(); |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.controller; |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.oss.model.AliyunOSSConfiguration; |
| | | import com.dji.sample.wayline.model.WaylineFileDTO; |
| | | import com.dji.sample.wayline.model.WaylineFileUploadDTO; |
| | | import com.dji.sample.wayline.model.WaylineQueryParam; |
| | | import com.dji.sample.wayline.service.IWaylineFileService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.net.URL; |
| | | import java.util.List; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("${url.wayline.prefix}${url.wayline.version}/workspaces") |
| | | public class WaylineFileController { |
| | | |
| | | @Autowired |
| | | private IWaylineFileService waylineFileService; |
| | | |
| | | /** |
| | | * Query the basic data of the wayline file according to the query conditions. |
| | | * The query condition field in pilot is fixed. |
| | | * @param orderBy Sorted fields. Spliced at the end of the sql statement. |
| | | * @param favorited Whether the wayline file is favorited or not. |
| | | * @param page |
| | | * @param pageSize |
| | | * @param templateType |
| | | * @param workspaceId |
| | | * @return |
| | | */ |
| | | @GetMapping("/{workspace_id}/waylines") |
| | | public ResponseResult<PaginationData<WaylineFileDTO>> getWaylinesPagination(@RequestParam(name = "order_by") String orderBy, |
| | | @RequestParam(required = false) boolean favorited, @RequestParam Integer page, |
| | | @RequestParam(name = "page_size", defaultValue = "10") Integer pageSize, |
| | | @RequestParam(name = "template_type", required = false) Integer[] templateType, |
| | | @PathVariable(name = "workspace_id") String workspaceId) { |
| | | WaylineQueryParam param = WaylineQueryParam.builder() |
| | | .favorited(favorited) |
| | | .page(page) |
| | | .pageSize(pageSize) |
| | | .orderBy(orderBy) |
| | | .templateType(templateType) |
| | | .build(); |
| | | PaginationData<WaylineFileDTO> data = waylineFileService.getWaylinesByParam(workspaceId, param); |
| | | return ResponseResult.success(data); |
| | | } |
| | | |
| | | /** |
| | | * Query the download address of the file according to the wayline file id, |
| | | * and redirect to this address directly for download. |
| | | * @param workspaceId |
| | | * @param waylineId |
| | | * @param response |
| | | */ |
| | | @GetMapping("/{workspace_id}/waylines/{wayline_id}/url") |
| | | public void getFileUrl(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @PathVariable(name = "wayline_id") String waylineId, HttpServletResponse response) { |
| | | |
| | | WaylineFileDTO wayline = waylineFileService.getWaylineByWaylineId(workspaceId, waylineId); |
| | | URL url = waylineFileService.getObjectUrl(AliyunOSSConfiguration.bucket, wayline.getObjectKey()); |
| | | |
| | | try { |
| | | response.sendRedirect(url.toString()); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * When the wayline file is uploaded to the storage server by pilot, |
| | | * the basic information of the file is reported through this interface. |
| | | * @param request |
| | | * @param workspaceId |
| | | * @param uploadFile |
| | | * @return |
| | | */ |
| | | @PostMapping("/{workspace_id}/upload-callback") |
| | | public ResponseResult uploadCallBack(HttpServletRequest request, |
| | | @PathVariable(name = "workspace_id") String workspaceId, |
| | | @RequestBody WaylineFileUploadDTO uploadFile) { |
| | | |
| | | CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); |
| | | |
| | | WaylineFileDTO metadata = uploadFile.getMetadata(); |
| | | metadata.setUsername(customClaim.getUsername()); |
| | | metadata.setObjectKey(uploadFile.getObjectKey()); |
| | | metadata.setName(uploadFile.getName()); |
| | | |
| | | int id = waylineFileService.saveWaylineFile(workspaceId, metadata); |
| | | |
| | | return id <= 0 ? ResponseResult.error() : ResponseResult.success(); |
| | | } |
| | | |
| | | /** |
| | | * Favorite the wayline file according to the wayline file id. |
| | | * @param workspaceId |
| | | * @param ids wayline file id |
| | | * @return |
| | | */ |
| | | @PostMapping("/{workspace_id}/favorites") |
| | | public ResponseResult markFavorite(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @RequestParam(name = "id") List<String> ids) { |
| | | boolean isMark = waylineFileService.markFavorite(workspaceId, ids, true); |
| | | |
| | | return isMark ? ResponseResult.success() : ResponseResult.error(); |
| | | } |
| | | |
| | | /** |
| | | * Delete the favorites of this wayline file based on the wayline file id. |
| | | * @param workspaceId |
| | | * @param ids wayline file id |
| | | * @return |
| | | */ |
| | | @DeleteMapping("/{workspace_id}/favorites") |
| | | public ResponseResult unmarkFavorite(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @RequestParam(name = "id") List<String> ids) { |
| | | boolean isMark = waylineFileService.markFavorite(workspaceId, ids, false); |
| | | |
| | | return isMark ? ResponseResult.success() : ResponseResult.error(); |
| | | } |
| | | |
| | | /** |
| | | * Checking whether the name already exists according to the wayline name must ensure the uniqueness of the wayline name. |
| | | * This interface will be called when uploading waylines and must be available. |
| | | * @param workspaceId |
| | | * @param names |
| | | * @return |
| | | */ |
| | | @GetMapping("/{workspace_id}/waylines/duplicate-names") |
| | | public ResponseResult checkDuplicateNames(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @RequestParam(name = "name") List<String> names) { |
| | | List<String> existNamesList = waylineFileService.getDuplicateNames(workspaceId, names); |
| | | |
| | | return ResponseResult.success(existNamesList); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.wayline.model.WaylineFileEntity; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | public interface IWaylineFileMapper extends BaseMapper<WaylineFileEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class WaylineFileDTO { |
| | | |
| | | private String name; |
| | | |
| | | @JsonProperty("id") |
| | | private String waylineId; |
| | | |
| | | private String droneModelKey; |
| | | |
| | | private List<String> payloadModelKeys; |
| | | |
| | | private Boolean favorited; |
| | | |
| | | private List<Integer> templateTypes; |
| | | |
| | | private String objectKey; |
| | | |
| | | @JsonProperty("user_name") |
| | | private String username; |
| | | |
| | | private Long updateTime; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | @Data |
| | | @TableName("wayline_file") |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class WaylineFileEntity { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @TableField("name") |
| | | private String name; |
| | | |
| | | @TableField("wayline_id") |
| | | private String waylineId; |
| | | |
| | | @TableField("drone_model_key") |
| | | private String droneModelKey; |
| | | |
| | | @TableField("payload_model_keys") |
| | | private String payloadModelKeys; |
| | | |
| | | @TableField("workspace_id") |
| | | private String workspaceId; |
| | | |
| | | @TableField("favorited") |
| | | private Boolean favorited; |
| | | |
| | | @TableField("template_types") |
| | | private String templateTypes; |
| | | |
| | | @TableField("object_key") |
| | | private String objectKey; |
| | | |
| | | @TableField("user_name") |
| | | private String username; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/23 |
| | | */ |
| | | @Data |
| | | public class WaylineFileUploadDTO { |
| | | |
| | | private String objectKey; |
| | | |
| | | private String name; |
| | | |
| | | private WaylineFileDTO metadata; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class WaylineQueryParam { |
| | | |
| | | private boolean favorited; |
| | | |
| | | @Builder.Default |
| | | private int page = 1; |
| | | |
| | | @Builder.Default |
| | | private int pageSize = 10; |
| | | |
| | | private String orderBy; |
| | | |
| | | private Integer[] templateType; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.service; |
| | | |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.wayline.model.WaylineFileDTO; |
| | | import com.dji.sample.wayline.model.WaylineQueryParam; |
| | | |
| | | import java.net.URL; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | public interface IWaylineFileService { |
| | | |
| | | /** |
| | | * Perform paging queries based on query parameters. |
| | | * @param workspaceId |
| | | * @param param |
| | | * @return |
| | | */ |
| | | PaginationData<WaylineFileDTO> getWaylinesByParam(String workspaceId, WaylineQueryParam param); |
| | | |
| | | /** |
| | | * Query the information of this wayline file according to the wayline file id. |
| | | * @param workspaceId |
| | | * @param waylineId |
| | | * @return |
| | | */ |
| | | WaylineFileDTO getWaylineByWaylineId(String workspaceId, String waylineId); |
| | | |
| | | /** |
| | | * Get the download address of the file object. |
| | | * @param bucket bucket name |
| | | * @param objectKey object name |
| | | * @return |
| | | */ |
| | | URL getObjectUrl(String bucket, String objectKey); |
| | | |
| | | /** |
| | | * Save the basic information of the wayline file. |
| | | * @param workspaceId |
| | | * @param metadata |
| | | * @return |
| | | */ |
| | | Integer saveWaylineFile(String workspaceId, WaylineFileDTO metadata); |
| | | |
| | | /** |
| | | * Updates whether the file is collected or not based on the passed parameters. |
| | | * @param workspaceId |
| | | * @param ids wayline id |
| | | * @param isFavorite Whether the wayline file is favorited or not. |
| | | * @return |
| | | */ |
| | | Boolean markFavorite(String workspaceId, List<String> ids, Boolean isFavorite); |
| | | |
| | | /** |
| | | * Batch query for duplicate file names in workspace. |
| | | * @param workspaceId |
| | | * @param names |
| | | * @return |
| | | */ |
| | | List<String> getDuplicateNames(String workspaceId, List<String> names); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.dji.sample.common.model.Pagination; |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.component.oss.model.AliyunOSSConfiguration; |
| | | import com.dji.sample.component.oss.model.MinIOConfiguration; |
| | | import com.dji.sample.component.oss.service.IOssService; |
| | | import com.dji.sample.component.oss.service.impl.AliyunOssServiceImpl; |
| | | import com.dji.sample.component.oss.service.impl.MinIOServiceImpl; |
| | | import com.dji.sample.wayline.dao.IWaylineFileMapper; |
| | | import com.dji.sample.wayline.model.WaylineFileDTO; |
| | | import com.dji.sample.wayline.model.WaylineFileEntity; |
| | | import com.dji.sample.wayline.model.WaylineQueryParam; |
| | | import com.dji.sample.wayline.service.IWaylineFileService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.net.URL; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 0.3 |
| | | * @date 2021/12/22 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class WaylineFileServiceImpl implements IWaylineFileService { |
| | | |
| | | @Autowired |
| | | private IWaylineFileMapper mapper; |
| | | |
| | | private IOssService ossService; |
| | | |
| | | @Autowired |
| | | private void setOssService(@Autowired AliyunOssServiceImpl aliyunOssService, @Autowired MinIOServiceImpl minIOService) { |
| | | if (AliyunOSSConfiguration.enable) { |
| | | this.ossService = aliyunOssService; |
| | | return; |
| | | } |
| | | if (MinIOConfiguration.enable) { |
| | | this.ossService = minIOService; |
| | | return; |
| | | } |
| | | throw new NullPointerException("ossService is null."); |
| | | } |
| | | |
| | | @Override |
| | | public PaginationData<WaylineFileDTO> getWaylinesByParam(String workspaceId, WaylineQueryParam param) { |
| | | // Paging Query |
| | | Page<WaylineFileEntity> page = mapper.selectPage( |
| | | new Page<WaylineFileEntity>(param.getPage(), param.getPageSize()), |
| | | new LambdaQueryWrapper<WaylineFileEntity>() |
| | | .eq(WaylineFileEntity::getWorkspaceId, workspaceId) |
| | | .eq(param.isFavorited(), WaylineFileEntity::getFavorited, param.isFavorited()) |
| | | .and(param.getTemplateType() != null, wrapper -> { |
| | | for (Integer type : param.getTemplateType()) { |
| | | wrapper.like(WaylineFileEntity::getTemplateTypes, type).or(); |
| | | } |
| | | }) |
| | | // There is a risk of SQL injection |
| | | .last(StringUtils.hasText(param.getOrderBy()), " order by " + param.getOrderBy())); |
| | | |
| | | // Wrap the results of a paging query into a custom paging object. |
| | | List<WaylineFileDTO> records = page.getRecords() |
| | | .stream() |
| | | .map(this::entityConvertToDTO) |
| | | .collect(Collectors.toList()); |
| | | |
| | | return new PaginationData<>(records, new Pagination(page)); |
| | | } |
| | | |
| | | @Override |
| | | public WaylineFileDTO getWaylineByWaylineId(String workspaceId, String waylineId) { |
| | | return this.entityConvertToDTO( |
| | | mapper.selectOne( |
| | | new LambdaQueryWrapper<WaylineFileEntity>() |
| | | .eq(WaylineFileEntity::getWorkspaceId, workspaceId) |
| | | .eq(WaylineFileEntity::getWaylineId, waylineId))); |
| | | } |
| | | |
| | | @Override |
| | | public URL getObjectUrl(String bucket, String objectKey) { |
| | | return ossService.getObjectUrl(bucket, objectKey); |
| | | } |
| | | |
| | | @Override |
| | | public Integer saveWaylineFile(String workspaceId, WaylineFileDTO metadata) { |
| | | WaylineFileEntity file = this.dtoConvertToEntity(metadata); |
| | | file.setWaylineId(UUID.randomUUID().toString()); |
| | | file.setWorkspaceId(workspaceId); |
| | | |
| | | int insertId = mapper.insert(file); |
| | | return insertId > 0 ? file.getId() : insertId; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean markFavorite(String workspaceId, List<String> waylineIds, Boolean isFavorite) { |
| | | if (waylineIds.isEmpty()) { |
| | | return false; |
| | | } |
| | | if (isFavorite == null) { |
| | | return true; |
| | | } |
| | | return mapper.update(null, new LambdaUpdateWrapper<WaylineFileEntity>() |
| | | .set(WaylineFileEntity::getFavorited, isFavorite) |
| | | .eq(WaylineFileEntity::getWorkspaceId, workspaceId) |
| | | .in(WaylineFileEntity::getWaylineId, waylineIds)) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public List<String> getDuplicateNames(String workspaceId, List<String> names) { |
| | | return mapper.selectList(new LambdaQueryWrapper<WaylineFileEntity>() |
| | | .eq(WaylineFileEntity::getWorkspaceId, workspaceId) |
| | | .in(WaylineFileEntity::getName, names)) |
| | | .stream() |
| | | .map(WaylineFileEntity::getName) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into wayline data transfer object. |
| | | * @param entity |
| | | * @return |
| | | */ |
| | | private WaylineFileDTO entityConvertToDTO(WaylineFileEntity entity) { |
| | | WaylineFileDTO.WaylineFileDTOBuilder builder = WaylineFileDTO.builder(); |
| | | |
| | | if (entity != null) { |
| | | builder.droneModelKey(entity.getDroneModelKey()) |
| | | .favorited(entity.getFavorited()) |
| | | .name(entity.getName()) |
| | | .payloadModelKeys(entity.getPayloadModelKeys() != null ? |
| | | Arrays.asList(entity.getPayloadModelKeys().split(",")) : null) |
| | | .templateTypes(Arrays.stream(entity.getTemplateTypes().split(",")) |
| | | .map(Integer::parseInt) |
| | | .collect(Collectors.toList())) |
| | | .username(entity.getUsername()) |
| | | .objectKey(entity.getObjectKey()) |
| | | .updateTime(entity.getUpdateTime()) |
| | | .waylineId(entity.getWaylineId()); |
| | | } |
| | | |
| | | return builder.build(); |
| | | } |
| | | |
| | | /** |
| | | * Convert the received wayline object into a database entity object. |
| | | * @param file |
| | | * @return |
| | | */ |
| | | private WaylineFileEntity dtoConvertToEntity(WaylineFileDTO file) { |
| | | WaylineFileEntity.WaylineFileEntityBuilder builder = WaylineFileEntity.builder(); |
| | | |
| | | if (file != null) { |
| | | builder.droneModelKey(file.getDroneModelKey()) |
| | | .name(file.getName()) |
| | | .username(file.getUsername()) |
| | | .objectKey(file.getObjectKey()) |
| | | // Separate multiple payload data with ",". |
| | | .payloadModelKeys(String.join(",", file.getPayloadModelKeys())) |
| | | .templateTypes(file.getTemplateTypes().stream() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(","))) |
| | | .favorited(file.getFavorited()) |
| | | .build(); |
| | | } |
| | | |
| | | return builder.build(); |
| | | } |
| | | } |
| New file |
| | |
| | | server: |
| | | port: 6789 |
| | | spring: |
| | | application: |
| | | name: cloud-api-sample |
| | | datasource: |
| | | druid: |
| | | type: com.alibaba.druid.pool.DruidDataSource |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | | url: jdbc:mysql://localhost:3306/cloud_sample?useSSL=false |
| | | username: root |
| | | password: root |
| | | initial-size: 10 |
| | | min-idle: 10 |
| | | max-active: 20 |
| | | max-wait: 60000 |
| | | |
| | | jackson: |
| | | property-naming-strategy: SNAKE_CASE |
| | | date-format: yyyy-MM-dd HH:mm:ss |
| | | default-property-inclusion: NON_NULL |
| | | deserialization: |
| | | FAIL_ON_UNKNOWN_PROPERTIES: false |
| | | |
| | | jwt: |
| | | issuer: DJI |
| | | subject: CloudApiSample |
| | | secret: CloudApiSample |
| | | age: 86400 |
| | | |
| | | mqtt: |
| | | protocol: tcp |
| | | host: Please enter your IP. |
| | | port: 1883 |
| | | username: JavaServer |
| | | password: 123456 |
| | | client-id: 123456 |
| | | # Topics that need to be subscribed when initially connecting to mqtt, multiple topics are divided by ",". |
| | | inbound-topic: sys/product/+/status |
| | | |
| | | url: |
| | | manage: |
| | | prefix: /manage |
| | | version: /api/v1 |
| | | map: |
| | | prefix: /map |
| | | version: /api/v1 |
| | | media: |
| | | prefix: /media |
| | | version: /api/v1 |
| | | wayline: |
| | | prefix: /wayline |
| | | version: /api/v1 |
| | | storage: |
| | | prefix: /storage |
| | | version: /api/v1 |
| | | |
| | | aliyun: |
| | | oss: |
| | | enable: true |
| | | endpoint: Please enter your endpoint. #Example: https://oss-cn-shenzhen.aliyuncs.com |
| | | access-key: Please enter your access key. |
| | | secret-key: Please enter your secret key. |
| | | expire: 3600 |
| | | region: Please enter oss region. |
| | | role-session-name: Please enter session name. |
| | | role-arn: Please enter role arn. |
| | | bucket: Please enter bucket name. |
| | | object-dir-prefix: Please enter object prefix. |
| | | |
| | | # MinIO is temporarily unavailable. |
| | | minio: |
| | | enable: false |
| | | endpoint: Please enter your endpoint. #http://192.168.1.1:9000/ |
| | | access-key: minioadmin |
| | | secret-key: minioadmin |
| | | bucket: Please enter bucket name. |
| | | expire: 3600 |
| | | region: Please enter minio region. |
| | | object-dir-prefix: Please enter object prefix. |
| | | |
| | | logging: |
| | | level: |
| | | com.dji.sample: debug |
| | | file: |
| | | name: logs/cloud-api-sample.log |