Compare commits

...

3 Commits

Author SHA1 Message Date
sean.zhou 1c2daf7629 Modify the judgment of the result 2 years ago
sean.zhou 5f75c0d498 initial v1.3.0-beta2 2 years ago
sean.zhou 860d5d4995 v1.3.0-beta1 3 years ago
  1. 65
      api/Cloud API Demo.postman_collection.json
  2. 13
      pom.xml
  3. 33
      sql/cloud_sample.sql
  4. 4
      src/main/java/com/dji/sample/common/error/LiveErrorEnum.java
  5. 20
      src/main/java/com/dji/sample/common/util/JwtUtil.java
  6. 1
      src/main/java/com/dji/sample/component/GlobalScheduleService.java
  7. 10
      src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java
  8. 86
      src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java
  9. 40
      src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java
  10. 1
      src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java
  11. 4
      src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java
  12. 1
      src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java
  13. 46
      src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java
  14. 6
      src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java
  15. 2
      src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java
  16. 5
      src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java
  17. 2
      src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java
  18. 10
      src/main/java/com/dji/sample/component/mqtt/model/ServicesMethodEnum.java
  19. 14
      src/main/java/com/dji/sample/component/mqtt/model/SetReply.java
  20. 30
      src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java
  21. 4
      src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java
  22. 6
      src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java
  23. 13
      src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java
  24. 5
      src/main/java/com/dji/sample/component/oss/service/IOssService.java
  25. 23
      src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java
  26. 27
      src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java
  27. 44
      src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java
  28. 7
      src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java
  29. 4
      src/main/java/com/dji/sample/component/redis/RedisConst.java
  30. 54
      src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java
  31. 1
      src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java
  32. 17
      src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java
  33. 57
      src/main/java/com/dji/sample/manage/controller/DeviceController.java
  34. 5
      src/main/java/com/dji/sample/manage/controller/LiveStreamController.java
  35. 4
      src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java
  36. 2
      src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java
  37. 41
      src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java
  38. 30
      src/main/java/com/dji/sample/manage/model/enums/StateSwitchReceiver.java
  39. 2
      src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java
  40. 20
      src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java
  41. 20
      src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java
  42. 4
      src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java
  43. 16
      src/main/java/com/dji/sample/manage/model/receiver/DeviceMaintainStatusReceiver.java
  44. 57
      src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java
  45. 38
      src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java
  46. 16
      src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java
  47. 29
      src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java
  48. 20
      src/main/java/com/dji/sample/manage/model/receiver/MaintainStatusReceiver.java
  49. 2
      src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java
  50. 62
      src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java
  51. 49
      src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java
  52. 18
      src/main/java/com/dji/sample/manage/model/receiver/OsdDockTransmissionReceiver.java
  53. 9
      src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java
  54. 4
      src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java
  55. 33
      src/main/java/com/dji/sample/manage/service/IDeviceService.java
  56. 7
      src/main/java/com/dji/sample/manage/service/ILiveStreamService.java
  57. 3
      src/main/java/com/dji/sample/manage/service/impl/CameraVideoServiceImpl.java
  58. 3
      src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java
  59. 8
      src/main/java/com/dji/sample/manage/service/impl/DeviceDictionaryServiceImpl.java
  60. 8
      src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java
  61. 22
      src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java
  62. 3
      src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java
  63. 3
      src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java
  64. 148
      src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
  65. 9
      src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java
  66. 75
      src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java
  67. 2
      src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java
  68. 19
      src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java
  69. 3
      src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java
  70. 15
      src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java
  71. 19
      src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java
  72. 15
      src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java
  73. 6
      src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java
  74. 2
      src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java
  75. 39
      src/main/java/com/dji/sample/wayline/model/dto/KmzFileProperties.java
  76. 15
      src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java
  77. 22
      src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java
  78. 39
      src/main/java/com/dji/sample/wayline/model/enums/WaylineJobStatusEnum.java
  79. 22
      src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java
  80. 26
      src/main/java/com/dji/sample/wayline/model/enums/WaylineTemplateTypeEnum.java
  81. 6
      src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java
  82. 10
      src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java
  83. 37
      src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java
  84. 78
      src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java
  85. 103
      src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java
  86. 320
      src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java

65
api/Cloud API Demo.postman_collection.json

@ -703,6 +703,36 @@
} }
}, },
"response": [] "response": []
},
{
"name": "Set Property",
"request": {
"method": "PUT",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"night_lights_state\": 0\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{base_url}}{{manage_version}}/devices/{{workspace_id}}/devices/{{device_sn}}/property",
"host": [
"{{base_url}}{{manage_version}}"
],
"path": [
"devices",
"{{workspace_id}}",
"devices",
"{{device_sn}}",
"property"
]
}
},
"response": []
} }
], ],
"auth": { "auth": {
@ -710,7 +740,7 @@
"apikey": [ "apikey": [
{ {
"key": "value", "key": "value",
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NjM1NTkxMTAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NjM2NDU1MTAsImlhdCI6MTY2MzU1OTExMCwidXNlcm5hbWUiOiJhZG1pblBDIn0.LG1JXZkuTdMaqnXn5WMJvnysNkHHbc4HLe_qZPWz_nM", "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njc0NzcwNTUsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njc1NjM0NTUsImlhdCI6MTY2NzQ3NzA1NSwidXNlcm5hbWUiOiJhZG1pblBDIn0.VMJ0ZKn895uvkMDjg2fw3p4trVCUx9ltVFKeP7QmYpo",
"type": "string" "type": "string"
}, },
{ {
@ -1322,6 +1352,37 @@
} }
}, },
"response": [] "response": []
},
{
"name": "Import KMZ File",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "file",
"type": "file",
"src": []
}
]
},
"url": {
"raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/waylines/file/upload",
"host": [
"{{base_url}}{{wayline_version}}"
],
"path": [
"workspaces",
"{{workspace_id}}",
"waylines",
"file",
"upload"
]
}
},
"response": []
} }
], ],
"auth": { "auth": {
@ -1329,7 +1390,7 @@
"apikey": [ "apikey": [
{ {
"key": "value", "key": "value",
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTU0NDk2MDIsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTU1MzYwMDIsImlhdCI6MTY1NTQ0OTYwMiwidXNlcm5hbWUiOiJhZG1pblBDIn0.YZWHJ65Pl_DT2Ampxk0WC01KD_fNTm_rYVUBIHAZD-4", "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njc1MzMwNDMsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njc2MTk0NDMsImlhdCI6MTY2NzUzMzA0MywidXNlcm5hbWUiOiJhZG1pblBDIn0.es0boeLSuSD8ysMft7OP701zYuaAHlrmf68iNCvqvnw",
"type": "string" "type": "string"
}, },
{ {

13
pom.xml

@ -11,7 +11,7 @@
<groupId>com.dji</groupId> <groupId>com.dji</groupId>
<artifactId>cloud-api-sample</artifactId> <artifactId>cloud-api-sample</artifactId>
<version>1.1.0</version> <version>1.3.0-beta2</version>
<name>cloud-api-sample</name> <name>cloud-api-sample</name>
<properties> <properties>
@ -156,6 +156,17 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId> <artifactId>spring-boot-starter-aop</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

33
sql/cloud_sample.sql

@ -127,7 +127,12 @@ VALUES
(15,1,90742,0,'L1',NULL), (15,1,90742,0,'L1',NULL),
(16,2,56,0,'DJI Smart Controller','Remote control for M300'), (16,2,56,0,'DJI Smart Controller','Remote control for M300'),
(17,2,119,0,'DJI RC Plus','Remote control for M30'), (17,2,119,0,'DJI RC Plus','Remote control for M30'),
(18,3,1,0,'DJI Dock',''); (18,3,1,0,'DJI Dock',''),
(19,0,77,0,'Mavic 3E',NULL),
(20,0,77,1,'Mavic 3T',NULL),
(21,1,66,0,'Mavic 3E Camera',NULL),
(22,1,67,0,'Mavic 3T Camera',NULL),
(23,2,144,0,'DJI RC Pro','Remote control for Mavic 3E/T');
/*!40000 ALTER TABLE `manage_device_dictionary` ENABLE KEYS */; /*!40000 ALTER TABLE `manage_device_dictionary` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
@ -441,6 +446,32 @@ CREATE TABLE `wayline_job` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Wayline mission information of the dock.'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Wayline mission information of the dock.';
# wayline_job_new
# ------------------------------------------------------------
DROP TABLE IF EXISTS `wayline_job_new`;
CREATE TABLE `wayline_job_new` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`job_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'uuid',
`name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The name of the job.',
`file_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The wayline file used for this job.',
`dock_sn` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Which dock executes the job.',
`workspace_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Which workspace the current job belongs to.',
`task_type` int NOT NULL,
`wayline_type` int NOT NULL COMMENT 'The template type of the wayline.',
`execute_time` bigint NOT NULL,
`username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The name of the creator.',
`end_time` bigint DEFAULT NULL COMMENT 'end time of the job.',
`error_code` int DEFAULT NULL,
`status` int NOT NULL COMMENT '1: pending; 2: in progress; 3: success; 4: cancel; 5: failed',
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `job_id_UNIQUE` (`job_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Wayline mission information of the dock.';
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;

4
src/main/java/com/dji/sample/common/error/LiveErrorEnum.java

@ -67,9 +67,9 @@ public enum LiveErrorEnum implements IErrorInfo {
* @return enumeration object * @return enumeration object
*/ */
public static LiveErrorEnum find(int code) { public static LiveErrorEnum find(int code) {
final int MOD = 100_000;
for (LiveErrorEnum errorEnum : LiveErrorEnum.class.getEnumConstants()) { for (LiveErrorEnum errorEnum : LiveErrorEnum.class.getEnumConstants()) {
if (errorEnum.code == code) { if (errorEnum.code % MOD == code % MOD) {
return errorEnum; return errorEnum;
} }
} }

20
src/main/java/com/dji/sample/common/util/JwtUtil.java

@ -2,7 +2,6 @@ package com.dji.sample.common.util;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
@ -85,14 +84,7 @@ public class JwtUtil {
* @throws TokenExpiredException * @throws TokenExpiredException
*/ */
public static DecodedJWT verifyToken(String token) { public static DecodedJWT verifyToken(String token) {
try { return JWT.require(algorithm).build().verify(token);
JWTVerifier verifier = JWT.require(algorithm).build();
return verifier.verify(token);
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
return null;
}
} }
/** /**
@ -101,7 +93,13 @@ public class JwtUtil {
* @return custom claim * @return custom claim
*/ */
public static Optional<CustomClaim> parseToken(String token) { public static Optional<CustomClaim> parseToken(String token) {
DecodedJWT jwt = verifyToken(token); DecodedJWT jwt;
return jwt == null ? Optional.empty() : Optional.of(new CustomClaim(jwt.getClaims())); try {
jwt = verifyToken(token);
} catch (Exception e) {
e.printStackTrace();
return Optional.empty();
}
return Optional.of(new CustomClaim(jwt.getClaims()));
} }
} }

1
src/main/java/com/dji/sample/component/GlobalScheduleService.java

@ -6,6 +6,7 @@ import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.service.IDeviceService; import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.wayline.service.IWaylineJobService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;

10
src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java

@ -152,4 +152,14 @@ public class MqttMessageChannel {
return new DirectChannel(); return new DirectChannel();
} }
@Bean(name = ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET)
public MessageChannel requestsFlightTaskResourceGet() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_PROPERTY_SET_REPLY)
public MessageChannel propertySetReply() {
return new DirectChannel();
}
} }

86
src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java

@ -1,7 +1,10 @@
package com.dji.sample.component.mqtt.handler; package com.dji.sample.component.mqtt.handler;
import com.dji.sample.component.mqtt.model.ChannelName; import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.DeviceTopicEnum;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.integration.annotation.Router; import org.springframework.integration.annotation.Router;
import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.integration.router.AbstractMessageRouter; import org.springframework.integration.router.AbstractMessageRouter;
@ -10,12 +13,9 @@ import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.regex.Pattern; import java.util.concurrent.ConcurrentHashMap;
import static com.dji.sample.component.mqtt.model.TopicConst.*;
/** /**
* *
@ -27,47 +27,10 @@ import static com.dji.sample.component.mqtt.model.TopicConst.*;
@Slf4j @Slf4j
public class InboundMessageRouter extends AbstractMessageRouter { public class InboundMessageRouter extends AbstractMessageRouter {
@Resource(name = ChannelName.INBOUND) @Autowired
private MessageChannel inboundChannel; private ApplicationContext applicationContext;
@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;
@Resource(name = ChannelName.INBOUND_REQUESTS)
private MessageChannel requestsChannel;
@Resource(name = ChannelName.INBOUND_EVENTS)
private MessageChannel eventsChannel;
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 + "$");
private static final Pattern PATTERN_TOPIC_REQUESTS = private static final ConcurrentHashMap<String, MessageChannel> channels = new ConcurrentHashMap<>(16);
Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + REQUESTS_SUF + "$");
private static final Pattern PATTERN_TOPIC_EVENTS =
Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + EVENTS_SUF + "$");
/** /**
* All mqtt broker messages will arrive here before distributing them to different channels. * All mqtt broker messages will arrive here before distributing them to different channels.
@ -81,38 +44,15 @@ public class InboundMessageRouter extends AbstractMessageRouter {
String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString();
byte[] payload = (byte[])message.getPayload(); 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)); log.debug("received topic :{} \t payload :{}", topic, new String(payload));
// status DeviceTopicEnum topicEnum = DeviceTopicEnum.find(topic);
if (PATTERN_TOPIC_STATUS.matcher(topic).matches()) { if (channels.containsKey(topicEnum.getBeanName())) {
return Collections.singleton(statusChannel); return Collections.singleton(channels.get(topicEnum.getBeanName()));
}
// 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);
}
// requests
if (PATTERN_TOPIC_REQUESTS.matcher(topic).matches()) {
return Collections.singleton(requestsChannel);
}
// events
if (PATTERN_TOPIC_EVENTS.matcher(topic).matches()) {
return Collections.singleton(eventsChannel);
} }
return Collections.singleton(defaultChannel); MessageChannel bean = (MessageChannel) applicationContext.getBean(topicEnum.getBeanName());
channels.put(topicEnum.getBeanName(), bean);
return Collections.singleton(bean);
} }
} }

40
src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java

@ -0,0 +1,40 @@
package com.dji.sample.component.mqtt.handler;
import com.dji.sample.component.mqtt.model.Chan;
import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @author sean
* @version 1.2
* @date 2022/9/9
*/
@Component
public class PropertySetReplyHandler {
@Autowired
private ObjectMapper mapper;
/**
* Handle the reply message from the pilot side to the on-demand video.
* @param message reply message
* @throws IOException
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_PROPERTY_SET_REPLY)
public void serviceReply(Message<?> message) throws IOException {
byte[] payload = (byte[])message.getPayload();
CommonTopicReceiver receiver = mapper.readValue(payload, new TypeReference<CommonTopicReceiver>() {});
Chan<CommonTopicReceiver<?>> chan = Chan.getInstance();
// Put the message to the chan object.
chan.put(receiver);
}
}

1
src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java

@ -42,6 +42,7 @@ public class RequestsRouter {
mapping.channelMapping(RequestsMethodEnum.AIRPORT_BIND_STATUS, ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS); mapping.channelMapping(RequestsMethodEnum.AIRPORT_BIND_STATUS, ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS);
mapping.channelMapping(RequestsMethodEnum.AIRPORT_ORGANIZATION_GET, ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET); mapping.channelMapping(RequestsMethodEnum.AIRPORT_ORGANIZATION_GET, ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET);
mapping.channelMapping(RequestsMethodEnum.AIRPORT_ORGANIZATION_BIND, ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND); mapping.channelMapping(RequestsMethodEnum.AIRPORT_ORGANIZATION_BIND, ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND);
mapping.channelMapping(RequestsMethodEnum.FLIGHT_TASK_RESOURCE_GET, ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET);
mapping.channelMapping(RequestsMethodEnum.UNKNOWN, ChannelName.DEFAULT); mapping.channelMapping(RequestsMethodEnum.UNKNOWN, ChannelName.DEFAULT);
}) })
.get(); .get();

4
src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java

@ -68,4 +68,8 @@ public class ChannelName {
public static final String INBOUND_EVENTS_OTA_PROGRESS = "inboundEventsOtaProgress"; public static final String INBOUND_EVENTS_OTA_PROGRESS = "inboundEventsOtaProgress";
public static final String INBOUND_EVENTS_FILE_UPLOAD_PROGRESS = "inboundEventsFileUploadProgress"; public static final String INBOUND_EVENTS_FILE_UPLOAD_PROGRESS = "inboundEventsFileUploadProgress";
public static final String INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET = "inboundEventsFlightTaskResourceGet";
public static final String INBOUND_PROPERTY_SET_REPLY = "inboundPropertySetReply";
} }

1
src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java

@ -31,5 +31,4 @@ public class CommonTopicReceiver<T> {
private Integer needReply; private Integer needReply;
private String from;
} }

46
src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java

@ -0,0 +1,46 @@
package com.dji.sample.component.mqtt.model;
import lombok.Getter;
import java.util.Arrays;
import java.util.regex.Pattern;
import static com.dji.sample.component.mqtt.model.TopicConst.*;
/**
* @author sean
* @version 1.3
* @date 2022/10/28
*/
@Getter
public enum DeviceTopicEnum {
STATUS(Pattern.compile("^" + BASIC_PRE + PRODUCT + REGEX_SN + STATUS_SUF + "$"), ChannelName.INBOUND_STATUS),
STATE(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + STATE_SUF + "$"), ChannelName.INBOUND_STATE),
SERVICE_REPLY(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + SERVICES_SUF + _REPLY_SUF + "$"), ChannelName.INBOUND_SERVICE_REPLY),
OSD(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + OSD_SUF + "$"), ChannelName.INBOUND_OSD),
REQUESTS(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + REQUESTS_SUF + "$"), ChannelName.INBOUND_REQUESTS),
EVENTS(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + EVENTS_SUF + "$"), ChannelName.INBOUND_EVENTS),
PROPERTY_SET_REPLY(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + PROPERTY_SUF + SET_SUF + _REPLY_SUF + "$"), ChannelName.INBOUND_PROPERTY_SET_REPLY),
UNKNOWN(null, ChannelName.DEFAULT);
Pattern pattern;
String beanName;
DeviceTopicEnum(Pattern pattern, String beanName) {
this.pattern = pattern;
this.beanName = beanName;
}
public static DeviceTopicEnum find(String topic) {
return Arrays.stream(DeviceTopicEnum.values()).filter(topicEnum -> topicEnum.pattern.matcher(topic).matches()).findAny().orElse(UNKNOWN);
}
}

6
src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java

@ -1,7 +1,10 @@
package com.dji.sample.component.mqtt.model; package com.dji.sample.component.mqtt.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
/** /**
* @author sean * @author sean
@ -10,6 +13,9 @@ import lombok.Data;
*/ */
@Data @Data
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EventsReceiver<T> { public class EventsReceiver<T> {
private Integer result; private Integer result;

2
src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java

@ -28,6 +28,8 @@ public enum EventsResultStatusEnum {
TIMEOUT("timeout", true), TIMEOUT("timeout", true),
PARTIALLY_DONE("partially_done", true),
UNKNOWN("unknown", false); UNKNOWN("unknown", false);
String desc; String desc;

5
src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java

@ -28,4 +28,9 @@ public final class MapKeyConst {
public static final String LIST = "list"; public static final String LIST = "list";
public static final String MODULE_LIST = "module_list"; public static final String MODULE_LIST = "module_list";
public static final String FLIGHT_ID = "flight_id";
public static final String FLIGHT_IDS = "flight_ids";
} }

2
src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java

@ -17,6 +17,8 @@ public enum RequestsMethodEnum {
AIRPORT_ORGANIZATION_GET("airport_organization_get"), AIRPORT_ORGANIZATION_GET("airport_organization_get"),
FLIGHT_TASK_RESOURCE_GET("flighttask_resource_get"),
UNKNOWN("Unknown"); UNKNOWN("Unknown");
private String method; private String method;

10
src/main/java/com/dji/sample/component/mqtt/model/ServicesMethodEnum.java

@ -15,7 +15,13 @@ public enum ServicesMethodEnum {
LIVE_SET_QUALITY("live_set_quality", false), LIVE_SET_QUALITY("live_set_quality", false),
FLIGHTTASK_CREATE("flighttask_create", false), FLIGHT_TASK_CREATE("flighttask_create", false),
FLIGHT_TASK_PREPARE("flighttask_prepare", false),
FLIGHT_TASK_EXECUTE("flighttask_execute", false),
FLIGHT_TASK_CANCEL("flighttask_undo", false),
DEBUG_MODE_OPEN("debug_mode_open", false), DEBUG_MODE_OPEN("debug_mode_open", false),
@ -61,6 +67,8 @@ public enum ServicesMethodEnum {
FILE_UPLOAD_UPDATE("fileupload_update", false), FILE_UPLOAD_UPDATE("fileupload_update", false),
LIVE_LENS_CHANGE("live_lens_change", false),
UNKNOWN("unknown", false); UNKNOWN("unknown", false);
private String method; private String method;

14
src/main/java/com/dji/sample/component/mqtt/model/SetReply.java

@ -0,0 +1,14 @@
package com.dji.sample.component.mqtt.model;
import lombok.Data;
/**
* @author sean
* @version 1.3
* @date 2022/10/28
*/
@Data
public class SetReply {
private Integer result;
}

30
src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java

@ -0,0 +1,30 @@
package com.dji.sample.component.mqtt.model;
import lombok.Getter;
/**
* @author sean
* @version 1.3
* @date 2022/10/28
*/
@Getter
public enum SetReplyStatusResultEnum {
SUCCESS(0, "success"),
FAILED(1, "failed"),
TIMEOUT(2, "timeout"),
UNKNOWN(-1, "unknown");
int val;
String desc;
SetReplyStatusResultEnum(int val, String desc) {
this.val = val;
this.desc = desc;
}
}

4
src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java

@ -28,6 +28,10 @@ public class TopicConst {
public static final String EVENTS_SUF = "/events"; public static final String EVENTS_SUF = "/events";
public static final String PROPERTY_SUF = "/property";
public static final String SET_SUF = "/set";
public static final String REGEX_SN = "[A-Za-z0-9]+"; public static final String REGEX_SN = "[A-Za-z0-9]+";
} }

6
src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java

@ -3,8 +3,6 @@ package com.dji.sample.component.mqtt.service;
import com.dji.sample.component.mqtt.model.CommonTopicResponse; import com.dji.sample.component.mqtt.model.CommonTopicResponse;
import com.dji.sample.component.mqtt.model.ServiceReply; import com.dji.sample.component.mqtt.model.ServiceReply;
import java.util.Optional;
/** /**
* @author sean.zhou * @author sean.zhou
* @version 0.1 * @version 0.1
@ -33,7 +31,7 @@ public interface IMessageSenderService {
* @param response notification of whether the start is successful. * @param response notification of whether the start is successful.
* @return * @return
*/ */
Optional<ServiceReply> publishWithReply(String topic, CommonTopicResponse response); ServiceReply publishWithReply(String topic, CommonTopicResponse response);
/** /**
* Send live streaming start message and receive a response at the same time. * Send live streaming start message and receive a response at the same time.
@ -44,5 +42,5 @@ public interface IMessageSenderService {
* @param <T> * @param <T>
* @return * @return
*/ */
<T> Optional<T> publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response, int retryTime); <T> T publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response, int retryTime);
} }

13
src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java

@ -12,7 +12,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
@ -49,28 +48,28 @@ public class MessageSenderServiceImpl implements IMessageSenderService {
} }
} }
public Optional<ServiceReply> publishWithReply(String topic, CommonTopicResponse response) { public ServiceReply publishWithReply(String topic, CommonTopicResponse response) {
return this.publishWithReply(ServiceReply.class, topic, response, 2); return this.publishWithReply(ServiceReply.class, topic, response, 2);
} }
public <T> Optional<T> publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response, int retryTime) { public <T> T publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response, int retryTime) {
AtomicInteger time = new AtomicInteger(0); AtomicInteger time = new AtomicInteger(0);
// Retry three times // Retry three times
while (time.getAndIncrement() < retryTime) { while (time.getAndIncrement() <= retryTime) {
this.publish(topic, response); this.publish(topic, response);
Chan<CommonTopicReceiver<T>> chan = Chan.getInstance(); Chan<CommonTopicReceiver<T>> chan = Chan.getInstance();
// If the message is not received in 0.5 seconds then resend it again. // If the message is not received in 0.5 seconds then resend it again.
CommonTopicReceiver<T> receiver = chan.get(response.getMethod()); CommonTopicReceiver<T> receiver = chan.get(response.getTid());
if (receiver == null) { if (receiver == null) {
continue; continue;
} }
// Need to match tid and bid. // Need to match tid and bid.
if (receiver.getTid().equals(response.getTid()) && if (receiver.getTid().equals(response.getTid()) &&
receiver.getBid().equals(response.getBid())) { receiver.getBid().equals(response.getBid())) {
return Optional.ofNullable(receiver.getData()); return receiver.getData();
} }
} }
return Optional.empty(); throw new RuntimeException("No message reply received.");
} }
} }

5
src/main/java/com/dji/sample/component/oss/service/IOssService.java

@ -2,6 +2,7 @@ package com.dji.sample.component.oss.service;
import com.dji.sample.media.model.CredentialsDTO; import com.dji.sample.media.model.CredentialsDTO;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
/** /**
@ -41,5 +42,7 @@ public interface IOssService {
* @param objectKey * @param objectKey
* @return * @return
*/ */
byte[] getObject(String bucket, String objectKey); InputStream getObject(String bucket, String objectKey);
void putObject(String bucket, String objectKey, InputStream input);
} }

23
src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java

@ -4,6 +4,9 @@ import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException; import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.OSSObject; import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient; import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ClientException;
@ -93,18 +96,30 @@ public class AliyunOssServiceImpl implements IOssService {
} }
@Override @Override
public byte[] getObject(String bucket, String objectKey) { public InputStream getObject(String bucket, String objectKey) {
OSS ossClient = this.createClient(); OSS ossClient = this.createClient();
OSSObject object = ossClient.getObject(bucket, objectKey); OSSObject object = ossClient.getObject(bucket, objectKey);
try (InputStream stream = object.getObjectContent()) { try (InputStream input = object.getObjectContent()) {
return stream.readAllBytes(); return input;
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
ossClient.shutdown(); ossClient.shutdown();
} }
return new byte[0]; return InputStream.nullInputStream();
}
@Override
public void putObject(String bucket, String objectKey, InputStream input) {
OSS ossClient = this.createClient();
if (ossClient.doesObjectExist(bucket, objectKey)) {
ossClient.shutdown();
throw new RuntimeException("The filename already exists.");
}
PutObjectResult objectResult = ossClient.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata()));
ossClient.shutdown();
log.info("Upload File: {}", objectResult.getETag());
} }
private OSS createClient() { private OSS createClient() {

27
src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java

@ -5,9 +5,7 @@ import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration; import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.model.CORSRule;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService; import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest; import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
@ -18,6 +16,7 @@ import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.oss.model.enums.OssTypeEnum; import com.dji.sample.component.oss.model.enums.OssTypeEnum;
import com.dji.sample.component.oss.service.IOssService; import com.dji.sample.component.oss.service.IOssService;
import com.dji.sample.media.model.CredentialsDTO; import com.dji.sample.media.model.CredentialsDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -34,6 +33,7 @@ import java.util.List;
* @version 1.0 * @version 1.0
* @date 2022/4/27 * @date 2022/4/27
*/ */
@Slf4j
@Service @Service
public class AmazonS3ServiceImpl implements IOssService { public class AmazonS3ServiceImpl implements IOssService {
@ -83,18 +83,29 @@ public class AmazonS3ServiceImpl implements IOssService {
return true; return true;
} }
public byte[] getObject(String bucket, String objectKey) { public InputStream getObject(String bucket, String objectKey) {
AmazonS3 client = this.createClient(); AmazonS3 client = this.createClient();
S3Object object = client.getObject(bucket, objectKey); S3Object object = client.getObject(bucket, objectKey);
try (InputStream input = object.getObjectContent().getDelegateStream()) {
try (InputStream stream = object.getObjectContent().getDelegateStream()) { return input;
return stream.readAllBytes();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
client.shutdown(); client.shutdown();
} }
return new byte[0]; return InputStream.nullInputStream();
}
@Override
public void putObject(String bucket, String objectKey, InputStream input) {
AmazonS3 client = this.createClient();
if (client.doesObjectExist(bucket, objectKey)) {
client.shutdown();
throw new RuntimeException("The filename already exists.");
}
PutObjectResult objectResult = client.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata()));
client.shutdown();
log.info("Upload File: {}", objectResult.toString());
} }
private AmazonS3 createClient() { private AmazonS3 createClient() {

44
src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java

@ -4,10 +4,7 @@ import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.oss.model.enums.OssTypeEnum; import com.dji.sample.component.oss.model.enums.OssTypeEnum;
import com.dji.sample.component.oss.service.IOssService; import com.dji.sample.component.oss.service.IOssService;
import com.dji.sample.media.model.CredentialsDTO; import com.dji.sample.media.model.CredentialsDTO;
import io.minio.GetObjectArgs; import io.minio.*;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.RemoveObjectArgs;
import io.minio.credentials.AssumeRoleProvider; import io.minio.credentials.AssumeRoleProvider;
import io.minio.errors.*; import io.minio.errors.*;
import io.minio.http.Method; import io.minio.http.Method;
@ -15,11 +12,13 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Objects;
/** /**
* @author sean * @author sean
@ -30,6 +29,8 @@ import java.security.NoSuchAlgorithmException;
@Slf4j @Slf4j
public class MinIOServiceImpl implements IOssService { public class MinIOServiceImpl implements IOssService {
private MinioClient client;
@Autowired @Autowired
private OssConfiguration configuration; private OssConfiguration configuration;
@ -87,21 +88,44 @@ public class MinIOServiceImpl implements IOssService {
} }
@Override @Override
public byte[] getObject(String bucket, String objectKey) { public InputStream getObject(String bucket, String objectKey) {
try {
GetObjectResponse object = this.createClient().getObject(GetObjectArgs.builder().bucket(bucket).object(objectKey).build());
return new ByteArrayInputStream(object.readAllBytes());
} catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) {
e.printStackTrace();
}
return InputStream.nullInputStream();
}
@Override
public void putObject(String bucket, String objectKey, InputStream input) {
try {
MinioClient client = this.createClient(); MinioClient client = this.createClient();
try (InputStream objectResponse = client.getObject(GetObjectArgs.builder().bucket(bucket).object(objectKey).build())) { client.statObject(StatObjectArgs.builder().bucket(bucket).object(objectKey).build());
return objectResponse.readAllBytes(); throw new RuntimeException("The filename already exists.");
} catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) { } catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) {
e.printStackTrace(); log.info("The file does not exist, start uploading.");
try {
ObjectWriteResponse response = client.putObject(
PutObjectArgs.builder().bucket(bucket).object(objectKey).stream(input, input.available(), 0).build());
log.info("Upload File: {}", response.etag());
} catch (MinioException | IOException | InvalidKeyException | NoSuchAlgorithmException ex) {
log.error("Failed to upload File {}.", objectKey);
ex.printStackTrace();
}
} }
return new byte[0];
} }
private MinioClient createClient() { private MinioClient createClient() {
return MinioClient.builder() if (Objects.nonNull(this.client)) {
return this.client;
}
this.client = MinioClient.builder()
.endpoint(configuration.getEndpoint()) .endpoint(configuration.getEndpoint())
.credentials(configuration.getAccessKey(), configuration.getSecretKey()) .credentials(configuration.getAccessKey(), configuration.getSecretKey())
.region(configuration.getRegion()) .region(configuration.getRegion())
.build(); .build();
return this.client;
} }
} }

7
src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java

@ -7,6 +7,7 @@ import com.dji.sample.media.model.CredentialsDTO;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -52,7 +53,11 @@ public class OssServiceContext {
return this.ossService.deleteObject(bucket, objectKey); return this.ossService.deleteObject(bucket, objectKey);
} }
public byte[] getObject(String bucket, String objectKey) { public InputStream getObject(String bucket, String objectKey) {
return this.ossService.getObject(bucket, objectKey); return this.ossService.getObject(bucket, objectKey);
} }
public void putObject(String bucket, String objectKey, InputStream stream) {
this.ossService.putObject(bucket, objectKey, stream);
}
} }

4
src/main/java/com/dji/sample/component/redis/RedisConst.java

@ -34,4 +34,8 @@ public final class RedisConst {
public static final String STATE_PAYLOAD_PREFIX = "payload" + DELIMITER; public static final String STATE_PAYLOAD_PREFIX = "payload" + DELIMITER;
public static final String LOGS_FILE_PREFIX = "logs_file" + DELIMITER; public static final String LOGS_FILE_PREFIX = "logs_file" + DELIMITER;
public static final String WAYLINE_JOB = "wayline_job";
public static final String OSD_PREFIX = "osd" + DELIMITER;
} }

54
src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java

@ -3,6 +3,7 @@ package com.dji.sample.component.redis;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -184,4 +185,57 @@ public class RedisOpsUtils {
public Long listLen(String key) { public Long listLen(String key) {
return redisTemplate.opsForList().size(key); return redisTemplate.opsForList().size(key);
} }
/**
* ZADD
* @param key
* @param value
* @param score
*/
public Boolean zAdd(String key, Object value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
/**
* ZREM
* @param key
* @param value
*/
public Boolean zRemove(String key, Object... value) {
return redisTemplate.opsForZSet().remove(key, value) > 0;
}
/**
* ZRANGE
* @param key
* @param start
* @param end
* @return
*/
public Set<Object> zRange(String key, long start, long end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
/**
* ZRANGE
* @param key
* @return
*/
public Object zGetMin(String key) {
Set<Object> objects = zRange(key, 0, 0);
if (CollectionUtils.isEmpty(objects)) {
return null;
}
return objects.iterator().next();
}
/**
* ZSCORE
* @param key
* @param value
* @return
*/
public Double zScore(String key, Object value) {
return redisTemplate.opsForZSet().score(key, value);
}
} }

1
src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java

@ -35,6 +35,7 @@ public class SpringBeanConfiguration {
objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS); objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
objectMapper.registerModules(timeModule); objectMapper.registerModules(timeModule);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);

17
src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java

@ -11,6 +11,7 @@ import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.control.service.IControlService; import com.dji.sample.control.service.IControlService;
import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.service.IDeviceService;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -20,7 +21,6 @@ import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
/** /**
@ -44,6 +44,9 @@ public class ControlServiceImpl implements IControlService {
@Autowired @Autowired
private IWebSocketManageService webSocketManageService; private IWebSocketManageService webSocketManageService;
@Autowired
private IDeviceService deviceService;
@Autowired @Autowired
private ObjectMapper mapper; private ObjectMapper mapper;
@ -53,13 +56,13 @@ public class ControlServiceImpl implements IControlService {
if (servicesMethodEnum == ServicesMethodEnum.UNKNOWN) { if (servicesMethodEnum == ServicesMethodEnum.UNKNOWN) {
return ResponseResult.error("The " + serviceIdentifier + " method does not exist."); return ResponseResult.error("The " + serviceIdentifier + " method does not exist.");
} }
boolean isExist = redisOps.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + sn) > 0; boolean isExist = deviceService.checkDeviceOnline(sn);
if (!isExist) { if (!isExist) {
return ResponseResult.error("The dock is offline."); return ResponseResult.error("The dock is offline.");
} }
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + sn + TopicConst.SERVICES_SUF; String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + sn + TopicConst.SERVICES_SUF;
String bid = UUID.randomUUID().toString(); String bid = UUID.randomUUID().toString();
Optional<ServiceReply> serviceReplyOpt = messageSenderService.publishWithReply( ServiceReply serviceReplyOpt = messageSenderService.publishWithReply(
topic, CommonTopicResponse.builder() topic, CommonTopicResponse.builder()
.tid(UUID.randomUUID().toString()) .tid(UUID.randomUUID().toString())
.bid(bid) .bid(bid)
@ -67,11 +70,9 @@ public class ControlServiceImpl implements IControlService {
.timestamp(System.currentTimeMillis()) .timestamp(System.currentTimeMillis())
.data("") .data("")
.build()); .build());
if (serviceReplyOpt.isEmpty()) {
return ResponseResult.error("No message reply received.");
}
ServiceReply<EventsOutputReceiver> serviceReply = mapper.convertValue( ServiceReply<EventsOutputReceiver> serviceReply = mapper.convertValue(
serviceReplyOpt.get(), new TypeReference<ServiceReply<EventsOutputReceiver>>() {}); serviceReplyOpt, new TypeReference<ServiceReply<EventsOutputReceiver>>() {});
if (serviceReply.getResult() != ResponseResult.CODE_SUCCESS) { if (serviceReply.getResult() != ResponseResult.CODE_SUCCESS) {
return ResponseResult.error(serviceReply.getResult(), serviceReply.getOutput().getStatus()); return ResponseResult.error(serviceReply.getResult(), serviceReply.getOutput().getStatus());
} }
@ -126,7 +127,7 @@ public class ControlServiceImpl implements IControlService {
.bid(receiver.getBid()) .bid(receiver.getBid())
.method(receiver.getMethod()) .method(receiver.getMethod())
.timestamp(System.currentTimeMillis()) .timestamp(System.currentTimeMillis())
.data(ResponseResult.success()) .data(RequestsReply.success())
.build()); .build());
} }
} }

57
src/main/java/com/dji/sample/manage/controller/DeviceController.java

@ -1,21 +1,20 @@
package com.dji.sample.manage.controller; package com.dji.sample.manage.controller;
import com.dji.sample.common.error.CommonErrorEnum;
import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.PaginationData;
import com.dji.sample.common.model.ResponseResult; import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.mqtt.model.ChannelName; import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.component.mqtt.model.CommonTopicResponse; import com.dji.sample.component.mqtt.model.CommonTopicResponse;
import com.dji.sample.component.websocket.service.ISendMessageService;
import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO; import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; import com.dji.sample.manage.model.enums.DeviceSetPropertyEnum;
import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; import com.dji.sample.manage.model.receiver.StatusGatewayReceiver;
import com.dji.sample.manage.service.IDeviceService; import com.dji.sample.manage.service.IDeviceService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@ -34,9 +33,6 @@ public class DeviceController {
@Autowired @Autowired
private IDeviceService deviceService; private IDeviceService deviceService;
@Autowired
private ISendMessageService sendMessageService;
/** /**
* Handles the message that the drone goes online. * Handles the message that the drone goes online.
* @param receiver The drone information is not empty. * @param receiver The drone information is not empty.
@ -50,6 +46,8 @@ public class DeviceController {
CommonTopicResponse.builder() CommonTopicResponse.builder()
.tid(receiver.getTid()) .tid(receiver.getTid())
.bid(receiver.getBid()) .bid(receiver.getBid())
.timestamp(System.currentTimeMillis())
.method(receiver.getMethod())
.build()); .build());
} }
} }
@ -68,6 +66,8 @@ public class DeviceController {
CommonTopicResponse.builder() CommonTopicResponse.builder()
.tid(receiver.getTid()) .tid(receiver.getTid())
.bid(receiver.getBid()) .bid(receiver.getBid())
.timestamp(System.currentTimeMillis())
.method(receiver.getMethod())
.build()); .build());
} }
@ -85,26 +85,6 @@ public class DeviceController {
return ResponseResult.success(devicesList); return ResponseResult.success(devicesList);
} }
/**
* Handle osd topic messages.
* @param message
*/
@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);
}
/**
* Receive the reported firmware version data.
* @param receiver
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_FIRMWARE_VERSION)
public void updateFirmwareVersion(FirmwareVersionReceiver receiver) {
deviceService.updateFirmwareVersion(receiver);
}
/** /**
* After binding the device to the workspace, the device data can only be seen on the web. * After binding the device to the workspace, the device data can only be seen on the web.
* @param device * @param device
@ -186,4 +166,27 @@ public class DeviceController {
@RequestBody List<DeviceFirmwareUpgradeDTO> upgradeDTOS) { @RequestBody List<DeviceFirmwareUpgradeDTO> upgradeDTOS) {
return deviceService.createDeviceOtaJob(workspaceId, upgradeDTOS); return deviceService.createDeviceOtaJob(workspaceId, upgradeDTOS);
} }
/**
* Set the property parameters of the drone.
* @param workspaceId
* @param dockSn
* @param param
* @return
*/
@PutMapping("/{workspace_id}/devices/{device_sn}/property")
public ResponseResult devicePropertySet(@PathVariable("workspace_id") String workspaceId,
@PathVariable("device_sn") String dockSn,
@RequestBody JsonNode param) {
if (param.size() != 1) {
return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT);
}
String property = param.fieldNames().next();
Optional<DeviceSetPropertyEnum> propertyEnumOpt = DeviceSetPropertyEnum.find(property);
if (propertyEnumOpt.isEmpty()) {
return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT);
}
deviceService.devicePropertySet(workspaceId, dockSn, propertyEnumOpt.get(), param.get(property));
return ResponseResult.success();
}
} }

5
src/main/java/com/dji/sample/manage/controller/LiveStreamController.java

@ -91,4 +91,9 @@ public class LiveStreamController {
return liveStreamService.liveSetQuality(liveParam); return liveStreamService.liveSetQuality(liveParam);
} }
@PostMapping("/streams/switch")
public ResponseResult liveLensChange(@RequestBody LiveTypeDTO liveParam) {
return liveStreamService.liveLensChange(liveParam);
}
} }

4
src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java

@ -5,6 +5,8 @@ import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.util.List;
/** /**
* @author sean.zhou * @author sean.zhou
* @date 2021/11/22 * @date 2021/11/22
@ -21,4 +23,6 @@ public class CapacityVideoDTO {
private String index; private String index;
private String type; private String type;
private List<String> switchVideoTypes;
} }

2
src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java

@ -23,4 +23,6 @@ public class LiveTypeDTO {
@JsonProperty("video_quality") @JsonProperty("video_quality")
private Integer videoQuality; private Integer videoQuality;
private String videoType;
} }

41
src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java

@ -0,0 +1,41 @@
package com.dji.sample.manage.model.enums;
import com.dji.sample.manage.model.receiver.BasicDeviceProperty;
import com.dji.sample.manage.model.receiver.DistanceLimitStatusReceiver;
import com.dji.sample.manage.model.receiver.HeightLimitReceiver;
import com.dji.sample.manage.model.receiver.ObstacleAvoidanceReceiver;
import lombok.Getter;
import java.util.Arrays;
import java.util.Optional;
/**
* @author sean
* @version 1.3
* @date 2022/10/27
*/
@Getter
public enum DeviceSetPropertyEnum {
NIGHT_LIGHTS_STATE("night_lights_state", StateSwitchReceiver.class),
HEIGHT_LIMIT("height_limit", HeightLimitReceiver.class),
DISTANCE_LIMIT_STATUS("distance_limit_status", DistanceLimitStatusReceiver.class),
OBSTACLE_AVOIDANCE("obstacle_avoidance", ObstacleAvoidanceReceiver.class);
String property;
Class<? extends BasicDeviceProperty> clazz;
DeviceSetPropertyEnum(String property, Class<? extends BasicDeviceProperty> clazz) {
this.property = property;
this.clazz = clazz;
}
public static Optional<DeviceSetPropertyEnum> find(String property) {
return Arrays.stream(DeviceSetPropertyEnum.values()).filter(propertyEnum -> propertyEnum.property.equals(property)).findAny();
}
}

30
src/main/java/com/dji/sample/manage/model/enums/StateSwitchReceiver.java

@ -0,0 +1,30 @@
package com.dji.sample.manage.model.enums;
import com.dji.sample.manage.model.receiver.BasicDeviceProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Objects;
/**
* @author sean
* @version 1.3
* @date 2022/10/28
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StateSwitchReceiver extends BasicDeviceProperty {
public static final int DISABLE = 0;
public static final int ENABLE = 1;
private Integer value;
@Override
public boolean valid() {
return Objects.nonNull(this.value) && (this.value == DISABLE || this.value == ENABLE);
}
}

2
src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java

@ -15,4 +15,6 @@ public class AlternateLandPointReceiver {
private Double longitude; private Double longitude;
private Double safeLandHeight; private Double safeLandHeight;
private Integer isConfigured;
} }

20
src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java

@ -0,0 +1,20 @@
package com.dji.sample.manage.model.receiver;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* @author sean
* @version 1.3
* @date 2022/11/3
*/
@Data
public class BackupBatteryReceiver {
private Integer voltage;
private Float temperature;
@JsonProperty("switch")
private Integer batterySwitch;
}

20
src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java

@ -0,0 +1,20 @@
package com.dji.sample.manage.model.receiver;
import lombok.Data;
/**
* @author sean
* @version 1.3
* @date 2022/10/27
*/
@Data
public class BasicDeviceProperty {
public boolean valid() {
return false;
}
public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) {
return true;
}
}

4
src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java

@ -4,6 +4,8 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Data; import lombok.Data;
import java.util.List;
/** /**
* @author sean.zhou * @author sean.zhou
* @date 2021/11/18 * @date 2021/11/18
@ -16,4 +18,6 @@ public class CapacityVideoReceiver {
private String videoIndex; private String videoIndex;
private String videoType; private String videoType;
private List<String> switchableVideoTypes;
} }

16
src/main/java/com/dji/sample/manage/model/receiver/DeviceMaintainStatusReceiver.java

@ -0,0 +1,16 @@
package com.dji.sample.manage.model.receiver;
import lombok.Data;
import java.util.List;
/**
* @author sean
* @version 1.3
* @date 2022/11/3
*/
@Data
public class DeviceMaintainStatusReceiver {
private List<MaintainStatusReceiver> maintainStatusArray;
}

57
src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java

@ -0,0 +1,57 @@
package com.dji.sample.manage.model.receiver;
import com.dji.sample.manage.model.enums.StateSwitchReceiver;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Objects;
/**
* The state of the drone's limited distance
* @author sean
* @version 1.3
* @date 2022/10/27
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DistanceLimitStatusReceiver extends BasicDeviceProperty {
private Integer state;
private Integer distanceLimit;
private static final int DISTANCE_MAX = 8000;
private static final int DISTANCE_MIN = 15;
@Override
public boolean valid() {
boolean valid = Objects.nonNull(state) || Objects.nonNull(distanceLimit);
if (Objects.nonNull(state)) {
valid = new StateSwitchReceiver(this.state).valid();
}
if (Objects.nonNull(distanceLimit)) {
valid &= distanceLimit >= DISTANCE_MIN && distanceLimit <= DISTANCE_MAX;
}
return valid;
}
@Override
public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) {
DistanceLimitStatusReceiver distanceLimitStatus = osd.getDistanceLimitStatus();
switch (fieldName) {
case "state":
return Objects.isNull(distanceLimitStatus.getState()) ||
Objects.nonNull(distanceLimitStatus.getState()) &&
distanceLimitStatus.getState().intValue() != this.state;
case "distance_limit":
return Objects.isNull(distanceLimitStatus.getDistanceLimit()) ||
Objects.nonNull(distanceLimitStatus.getDistanceLimit()) &&
distanceLimitStatus.getDistanceLimit().intValue() != this.distanceLimit;
default:
throw new RuntimeException("Property " + fieldName + " does not exist.");
}
}
}

38
src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java

@ -0,0 +1,38 @@
package com.dji.sample.manage.model.receiver;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* @author sean
* @version 1.3
* @date 2022/11/3
*/
@Data
public class DockWirelessLinkReceiver {
@JsonProperty("4g_freq_band")
private Float fourGFreqBand;
@JsonProperty("4g_gnd_quality")
private Integer fourGGndQuality;
@JsonProperty("4g_link_state")
private Integer fourGLinkState;
@JsonProperty("4g_quality")
private Integer fourGQuality;
@JsonProperty("4g_uav_quality")
private Integer fourGUavQuality;
private Integer dongleNumber;
private Integer linkWorkmode;
private Float sdrFreqBand;
private Integer sdrLinkState;
private Integer sdrQuality;
}

16
src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java

@ -0,0 +1,16 @@
package com.dji.sample.manage.model.receiver;
import lombok.Data;
/**
* @author sean
* @version 1.4
* @date 2022/11/3
*/
@Data
public class DroneBatteryMaintenanceInfoReceiver {
private Integer maintenanceState;
private Long maintenanceTimeLeft;
}

29
src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java

@ -0,0 +1,29 @@
package com.dji.sample.manage.model.receiver;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Objects;
/**
* @author sean
* @version 1.3
* @date 2022/10/28
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HeightLimitReceiver extends BasicDeviceProperty {
private static final int HEIGHT_LIMIT_MAX = 1500;
private static final int HEIGHT_LIMIT_MIN = 20;
private Integer value;
@Override
public boolean valid() {
return Objects.nonNull(this.value) && this.value >= HEIGHT_LIMIT_MIN && this.value <= HEIGHT_LIMIT_MAX;
}
}

20
src/main/java/com/dji/sample/manage/model/receiver/MaintainStatusReceiver.java

@ -0,0 +1,20 @@
package com.dji.sample.manage.model.receiver;
import lombok.Data;
/**
* @author sean
* @version 1.3
* @date 2022/11/3
*/
@Data
public class MaintainStatusReceiver {
private Integer state;
private Integer lastMaintainType;
private Long lastMaintainTime;
private Long lastMaintainWorkSorties;
}

2
src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java

@ -14,5 +14,5 @@ public class NetworkStateReceiver {
private Integer quality; private Integer quality;
private float rate; private Float rate;
} }

62
src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java

@ -0,0 +1,62 @@
package com.dji.sample.manage.model.receiver;
import com.dji.sample.manage.model.enums.StateSwitchReceiver;
import lombok.Data;
import java.util.Objects;
/**
* @author sean
* @version 1.3
* @date 2022/10/27
*/
@Data
public class ObstacleAvoidanceReceiver extends BasicDeviceProperty {
private Integer horizon;
private Integer upside;
private Integer downside;
@Override
public boolean valid() {
boolean valid = Objects.nonNull(this.horizon) || Objects.nonNull(this.upside) || Objects.nonNull(this.downside);
StateSwitchReceiver stateSwitch = new StateSwitchReceiver();
if (Objects.nonNull(this.horizon)) {
stateSwitch.setValue(this.horizon);
valid = stateSwitch.valid();
}
if (Objects.nonNull(this.upside)) {
stateSwitch.setValue(this.upside);
valid &= stateSwitch.valid();
}
if (Objects.nonNull(this.downside)) {
stateSwitch.setValue(this.downside);
valid &= stateSwitch.valid();
}
return valid;
}
@Override
public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) {
ObstacleAvoidanceReceiver obstacleAvoidance = osd.getObstacleAvoidance();
switch (fieldName) {
case "horizon":
return Objects.isNull(obstacleAvoidance.getHorizon()) ||
Objects.nonNull(obstacleAvoidance.getHorizon()) &&
obstacleAvoidance.getHorizon().intValue() != this.horizon;
case "upside":
return Objects.isNull(obstacleAvoidance.getUpside()) ||
Objects.nonNull(obstacleAvoidance.getUpside()) &&
obstacleAvoidance.getUpside().intValue() != this.upside;
case "downside":
return Objects.isNull(obstacleAvoidance.getDownside()) ||
Objects.nonNull(obstacleAvoidance.getDownside()) &&
obstacleAvoidance.getDownside().intValue() != this.downside;
default:
throw new RuntimeException("Property " + fieldName + " does not exist.");
}
}
}

49
src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java

@ -4,8 +4,8 @@ import lombok.Data;
/** /**
* @author sean * @author sean
* @version 1.0 * @version 1.3
* @date 2022/5/11 * @date 2022/11/3
*/ */
@Data @Data
public class OsdDockReceiver { public class OsdDockReceiver {
@ -22,8 +22,6 @@ public class OsdDockReceiver {
private Float environmentTemperature; private Float environmentTemperature;
private Integer environmentHumidity;
private Float temperature; private Float temperature;
private Integer humidity; private Integer humidity;
@ -36,32 +34,55 @@ public class OsdDockReceiver {
private AlternateLandPointReceiver alternateLandPoint; private AlternateLandPointReceiver alternateLandPoint;
private Integer jobNumber;
private Integer accTime;
private Long firstPowerOn; private Long firstPowerOn;
private PositionStateReceiver positionState; private PositionStateReceiver positionState;
private StorageReceiver storage; private StorageReceiver storage;
private Integer modeCode;
private Integer coverState;
private Integer supplementLightState;
private Integer emergencyStopState;
private Integer airConditionerMode;
private Integer batteryStoreMode;
private Integer alarmState;
private Integer putterState;
private DockSubDeviceReceiver subDevice;
private Integer jobNumber;
private Long accTime;
private Long activationTime;
private DeviceMaintainStatusReceiver maintainStatus;
private Integer electricSupplyVoltage; private Integer electricSupplyVoltage;
private Integer workingVoltage; private Integer workingVoltage;
private Integer workingCurrent; private Integer workingCurrent;
private Integer backupBatteryVoltage; private BackupBatteryReceiver backupBattery;
private Integer modeCode; private DroneBatteryMaintenanceInfoReceiver droneBatteryMaintenanceInfo;
private Integer coverState; private Integer flighttaskStepCode;
private Integer supplementLightState; private Integer flighttaskPrepareCapacity;
private Integer putterState; private DockMediaFileDetailReceiver mediaFileDetail;
private DockSubDeviceReceiver subDevice; private DockSdrReceiver sdr;
private DockWirelessLinkReceiver wirelessLink;
} }

18
src/main/java/com/dji/sample/manage/model/receiver/OsdDockTransmissionReceiver.java

@ -1,18 +0,0 @@
package com.dji.sample.manage.model.receiver;
import lombok.Data;
/**
* @author sean
* @version 1.1
* @date 2022/6/17
*/
@Data
public class OsdDockTransmissionReceiver {
private Integer flighttaskStepCode;
private DockMediaFileDetailReceiver mediaFileDetail;
private DockSdrReceiver sdr;
}

9
src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java

@ -58,4 +58,13 @@ public class OsdSubDeviceReceiver {
private List<OsdPayloadReceiver> payloads; private List<OsdPayloadReceiver> payloads;
private StorageReceiver storage; private StorageReceiver storage;
private Integer nightLightsState;
private Integer heightLimit;
private DistanceLimitStatusReceiver distanceLimitStatus;
private ObstacleAvoidanceReceiver obstacleAvoidance;
} }

4
src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java

@ -13,10 +13,12 @@ public interface IDeviceDictionaryService {
/** /**
* Query the type data of the device based on domain, device type and sub type. * Query the type data of the device based on domain, device type and sub type.
*
* @param domain
* @param deviceType * @param deviceType
* @param subType * @param subType
* @return * @return
*/ */
Optional<DeviceDictionaryDTO> getOneDictionaryInfoByTypeSubType(Integer deviceType, Integer subType); Optional<DeviceDictionaryDTO> getOneDictionaryInfoByTypeSubType(Integer domain, Integer deviceType, Integer subType);
} }

33
src/main/java/com/dji/sample/manage/service/IDeviceService.java

@ -8,13 +8,17 @@ import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession;
import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO; import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
import com.dji.sample.manage.model.dto.TopologyDeviceDTO; import com.dji.sample.manage.model.dto.TopologyDeviceDTO;
import com.dji.sample.manage.model.enums.DeviceSetPropertyEnum;
import com.dji.sample.manage.model.param.DeviceQueryParam; import com.dji.sample.manage.model.param.DeviceQueryParam;
import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver;
import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; import com.dji.sample.manage.model.receiver.StatusGatewayReceiver;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
/** /**
@ -133,10 +137,9 @@ public interface IDeviceService {
/** /**
* Handle messages from the osd topic. * Handle messages from the osd topic.
* @param topic osd * @param message osd
* @param payload
*/ */
void handleOSD(String topic, byte[] payload); void handleOSD(Message<?> message);
/** /**
* Update the device information. * Update the device information.
@ -205,4 +208,28 @@ public interface IDeviceService {
* @return * @return
*/ */
ResponseResult createDeviceOtaJob(String workspaceId, List<DeviceFirmwareUpgradeDTO> upgradeDTOS); ResponseResult createDeviceOtaJob(String workspaceId, List<DeviceFirmwareUpgradeDTO> upgradeDTOS);
/**
* Set the property parameters of the drone.
* @param workspaceId
* @param dockSn
* @param propertyEnum
* @param param
*/
void devicePropertySet(String workspaceId, String dockSn, DeviceSetPropertyEnum propertyEnum, JsonNode param);
/**
* Set one property parameters of the drone.
* @param topic
* @param propertyEnum
* @param value
*/
void deviceOnePropertySet(String topic, DeviceSetPropertyEnum propertyEnum, Map.Entry<String, Object> value);
/**
* Determine if the device is online.
* @param sn
* @return
*/
Boolean checkDeviceOnline(String sn);
} }

7
src/main/java/com/dji/sample/manage/service/ILiveStreamService.java

@ -48,4 +48,11 @@ public interface ILiveStreamService {
* @return * @return
*/ */
ResponseResult liveSetQuality(LiveTypeDTO liveParam); ResponseResult liveSetQuality(LiveTypeDTO liveParam);
/**
* Switches the lens of the device during the live streaming.
* @param liveParam
* @return
*/
ResponseResult liveLensChange(LiveTypeDTO liveParam);
} }

3
src/main/java/com/dji/sample/manage/service/impl/CameraVideoServiceImpl.java

@ -23,7 +23,8 @@ public class CameraVideoServiceImpl implements ICameraVideoService {
if (receiver != null) { if (receiver != null) {
builder.id(UUID.randomUUID().toString()) builder.id(UUID.randomUUID().toString())
.index(receiver.getVideoIndex()) .index(receiver.getVideoIndex())
.type(receiver.getVideoType()); .type(receiver.getVideoType())
.switchVideoTypes(receiver.getSwitchableVideoTypes());
} }
return builder.build(); return builder.build();
} }

3
src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java

@ -5,6 +5,7 @@ import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.manage.model.dto.CapacityCameraDTO; import com.dji.sample.manage.model.dto.CapacityCameraDTO;
import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; import com.dji.sample.manage.model.dto.DeviceDictionaryDTO;
import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.model.receiver.CapacityCameraReceiver; import com.dji.sample.manage.model.receiver.CapacityCameraReceiver;
import com.dji.sample.manage.service.ICameraVideoService; import com.dji.sample.manage.service.ICameraVideoService;
import com.dji.sample.manage.service.ICapacityCameraService; import com.dji.sample.manage.service.ICapacityCameraService;
@ -68,7 +69,7 @@ public class CapacityCameraServiceImpl implements ICapacityCameraService {
// type-subType-index // type-subType-index
if (indexArr.length == 3) { if (indexArr.length == 3) {
Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService
.getOneDictionaryInfoByTypeSubType(indexArr[0], indexArr[1]); .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), indexArr[0], indexArr[1]);
dictionaryOpt.ifPresent(dictionary -> dictionaryOpt.ifPresent(dictionary ->
builder.name(dictionary.getDeviceName())); builder.name(dictionary.getDeviceName()));
} }

8
src/main/java/com/dji/sample/manage/service/impl/DeviceDictionaryServiceImpl.java

@ -25,16 +25,18 @@ public class DeviceDictionaryServiceImpl implements IDeviceDictionaryService {
private IDeviceDictionaryMapper mapper; private IDeviceDictionaryMapper mapper;
@Override @Override
public Optional<DeviceDictionaryDTO> getOneDictionaryInfoByTypeSubType(Integer deviceType, Integer subType) { public Optional<DeviceDictionaryDTO> getOneDictionaryInfoByTypeSubType(Integer domain, Integer deviceType, Integer subType) {
if (deviceType == null || subType == null) { if (domain == null || deviceType == null || subType == null) {
return Optional.empty(); return Optional.empty();
} }
return Optional.ofNullable( return Optional.ofNullable(
entityConvertToDTO( entityConvertToDTO(
mapper.selectOne( mapper.selectOne(
new LambdaQueryWrapper<DeviceDictionaryEntity>() new LambdaQueryWrapper<DeviceDictionaryEntity>()
.eq(DeviceDictionaryEntity::getDomain, domain)
.eq(DeviceDictionaryEntity::getDeviceType, deviceType) .eq(DeviceDictionaryEntity::getDeviceType, deviceType)
.eq(DeviceDictionaryEntity::getSubType, subType)))); .eq(DeviceDictionaryEntity::getSubType, subType)
.last(" limit 1 "))));
} }
/** /**

8
src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java

@ -18,6 +18,7 @@ import com.dji.sample.manage.model.entity.DeviceFirmwareEntity;
import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.model.param.DeviceOtaCreateParam; import com.dji.sample.manage.model.param.DeviceOtaCreateParam;
import com.dji.sample.manage.service.IDeviceFirmwareService; import com.dji.sample.manage.service.IDeviceFirmwareService;
import com.dji.sample.manage.service.IDeviceService;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -61,6 +62,9 @@ public class DeviceFirmwareServiceImpl implements IDeviceFirmwareService {
@Autowired @Autowired
private IWebSocketManageService webSocketManageService; private IWebSocketManageService webSocketManageService;
@Autowired
private IDeviceService deviceService;
@Override @Override
public Optional<DeviceFirmwareDTO> getFirmware(String deviceName, String version) { public Optional<DeviceFirmwareDTO> getFirmware(String deviceName, String version) {
return Optional.ofNullable(entity2Dto(mapper.selectOne( return Optional.ofNullable(entity2Dto(mapper.selectOne(
@ -83,7 +87,7 @@ public class DeviceFirmwareServiceImpl implements IDeviceFirmwareService {
public List<DeviceOtaCreateParam> getDeviceOtaFirmware(List<DeviceFirmwareUpgradeDTO> upgradeDTOS) { public List<DeviceOtaCreateParam> getDeviceOtaFirmware(List<DeviceFirmwareUpgradeDTO> upgradeDTOS) {
List<DeviceOtaCreateParam> deviceOtaList = new ArrayList<>(); List<DeviceOtaCreateParam> deviceOtaList = new ArrayList<>();
upgradeDTOS.forEach(upgradeDevice -> { upgradeDTOS.forEach(upgradeDevice -> {
boolean exist = redisOps.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + upgradeDevice.getSn()) > 0; boolean exist = deviceService.checkDeviceOnline(upgradeDevice.getSn());
if (!exist) { if (!exist) {
throw new IllegalArgumentException("Device is offline."); throw new IllegalArgumentException("Device is offline.");
} }
@ -169,7 +173,7 @@ public class DeviceFirmwareServiceImpl implements IDeviceFirmwareService {
.bid(receiver.getBid()) .bid(receiver.getBid())
.method(receiver.getMethod()) .method(receiver.getMethod())
.timestamp(System.currentTimeMillis()) .timestamp(System.currentTimeMillis())
.data(ResponseResult.success()) .data(RequestsReply.success())
.build()); .build());
} }
} }

22
src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java

@ -113,7 +113,7 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService {
} }
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF; String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF;
Optional<LogsFileUploadList> serviceReplyOpt = messageSenderService.publishWithReply( LogsFileUploadList data = messageSenderService.publishWithReply(
LogsFileUploadList.class, LogsFileUploadList.class,
topic, topic,
CommonTopicResponse.builder() CommonTopicResponse.builder()
@ -123,10 +123,7 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService {
.timestamp(System.currentTimeMillis()) .timestamp(System.currentTimeMillis())
.data(Map.of(MapKeyConst.MODULE_LIST, domainList)) .data(Map.of(MapKeyConst.MODULE_LIST, domainList))
.build(), 1); .build(), 1);
if (serviceReplyOpt.isEmpty()) {
return ResponseResult.error("No message reply received.");
}
LogsFileUploadList data = serviceReplyOpt.get();
for (LogsFileUpload file : data.getFiles()) { for (LogsFileUpload file : data.getFiles()) {
if (file.getDeviceSn().isBlank()) { if (file.getDeviceSn().isBlank()) {
file.setDeviceSn(deviceSn); file.setDeviceSn(deviceSn);
@ -170,7 +167,7 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService {
credentialsDTO.setParams(LogsFileUploadList.builder().files(files).build()); credentialsDTO.setParams(LogsFileUploadList.builder().files(files).build());
String bid = UUID.randomUUID().toString(); String bid = UUID.randomUUID().toString();
Optional<ServiceReply> serviceReply = messageSenderService.publishWithReply( ServiceReply reply = messageSenderService.publishWithReply(
TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF, TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF,
CommonTopicResponse.<LogsUploadCredentialsDTO>builder() CommonTopicResponse.<LogsUploadCredentialsDTO>builder()
.tid(UUID.randomUUID().toString()) .tid(UUID.randomUUID().toString())
@ -180,10 +177,6 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService {
.data(credentialsDTO) .data(credentialsDTO)
.build()); .build());
if (serviceReply.isEmpty()) {
return ResponseResult.error("No message reply received.");
}
ServiceReply reply = serviceReply.get();
if (ResponseResult.CODE_SUCCESS != reply.getResult()) { if (ResponseResult.CODE_SUCCESS != reply.getResult()) {
return ResponseResult.error(String.valueOf(reply.getResult())); return ResponseResult.error(String.valueOf(reply.getResult()));
} }
@ -207,7 +200,7 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService {
} }
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF; String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF;
String bid = UUID.randomUUID().toString(); String bid = UUID.randomUUID().toString();
Optional<ServiceReply> serviceReply = messageSenderService.publishWithReply(topic, ServiceReply reply = messageSenderService.publishWithReply(topic,
CommonTopicResponse.<LogsFileUpdateParam>builder() CommonTopicResponse.<LogsFileUpdateParam>builder()
.tid(UUID.randomUUID().toString()) .tid(UUID.randomUUID().toString())
.bid(bid) .bid(bid)
@ -216,10 +209,6 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService {
.data(param) .data(param)
.build()); .build());
if (serviceReply.isEmpty()) {
return ResponseResult.error("No message reply received.");
}
ServiceReply reply = serviceReply.get();
if (ResponseResult.CODE_SUCCESS != reply.getResult()) { if (ResponseResult.CODE_SUCCESS != reply.getResult()) {
return ResponseResult.error("Error Code : " + reply.getResult()); return ResponseResult.error("Error Code : " + reply.getResult());
} }
@ -249,7 +238,7 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService {
.bid(receiver.getBid()) .bid(receiver.getBid())
.method(receiver.getMethod()) .method(receiver.getMethod())
.timestamp(System.currentTimeMillis()) .timestamp(System.currentTimeMillis())
.data(ResponseResult.success()) .data(RequestsReply.success())
.build()); .build());
} }
@ -284,7 +273,6 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService {
List<LogsExtFileReceiver> fileReceivers = output.getExt().getFiles(); List<LogsExtFileReceiver> fileReceivers = output.getExt().getFiles();
if (CollectionUtils.isEmpty(fileReceivers)) { if (CollectionUtils.isEmpty(fileReceivers)) {
redisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); redisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn);
return;
} }
// refresh cache. // refresh cache.

3
src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java

@ -1,6 +1,7 @@
package com.dji.sample.manage.service.impl; package com.dji.sample.manage.service.impl;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession;
import com.dji.sample.component.websocket.model.BizCodeEnum; import com.dji.sample.component.websocket.model.BizCodeEnum;
import com.dji.sample.component.websocket.model.CustomWebSocketMessage; import com.dji.sample.component.websocket.model.CustomWebSocketMessage;
@ -74,7 +75,7 @@ public class DeviceOSDServiceImpl extends AbstractTSAService {
log.warn("Please remount the payload, or restart the drone. Otherwise the data of the payload will not be received."); log.warn("Please remount the payload, or restart the drone. Otherwise the data of the payload will not be received.");
} }
redisOps.setWithExpire(RedisConst.OSD_PREFIX + device.getDeviceSn(), data, RedisConst.DEVICE_ALIVE_SECOND);
wsMessage.getData().setHost(data); wsMessage.getData().setHost(data);
sendMessageService.sendBatch(webSessions, wsMessage); sendMessageService.sendBatch(webSessions, wsMessage);

3
src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java

@ -9,6 +9,7 @@ import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; import com.dji.sample.manage.model.dto.DeviceDictionaryDTO;
import com.dji.sample.manage.model.dto.DevicePayloadDTO; import com.dji.sample.manage.model.dto.DevicePayloadDTO;
import com.dji.sample.manage.model.entity.DevicePayloadEntity; 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.model.receiver.DevicePayloadReceiver;
import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver;
import com.dji.sample.manage.service.ICapacityCameraService; import com.dji.sample.manage.service.ICapacityCameraService;
@ -204,7 +205,7 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService {
if (arr.length == 3) { if (arr.length == 3) {
Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService
.getOneDictionaryInfoByTypeSubType(arr[0], arr[1]); .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), arr[0], arr[1]);
dictionaryOpt.ifPresent(dictionary -> dictionaryOpt.ifPresent(dictionary ->
builder.payloadName(dictionary.getDeviceName()) builder.payloadName(dictionary.getDeviceName())
.payloadDesc(dictionary.getDeviceDesc())); .payloadDesc(dictionary.getDeviceDesc()));

148
src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java

@ -20,21 +20,20 @@ import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.manage.dao.IDeviceMapper; import com.dji.sample.manage.dao.IDeviceMapper;
import com.dji.sample.manage.model.dto.*; import com.dji.sample.manage.model.dto.*;
import com.dji.sample.manage.model.entity.DeviceEntity; import com.dji.sample.manage.model.entity.DeviceEntity;
import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.enums.*;
import com.dji.sample.manage.model.enums.DeviceFirmwareStatusEnum;
import com.dji.sample.manage.model.enums.IconUrlEnum;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.model.param.DeviceOtaCreateParam; import com.dji.sample.manage.model.param.DeviceOtaCreateParam;
import com.dji.sample.manage.model.param.DeviceQueryParam; import com.dji.sample.manage.model.param.DeviceQueryParam;
import com.dji.sample.manage.model.receiver.*; import com.dji.sample.manage.model.receiver.*;
import com.dji.sample.manage.service.*; import com.dji.sample.manage.service.*;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -99,6 +98,9 @@ public class DeviceServiceImpl implements IDeviceService {
@Qualifier("gatewayOSDServiceImpl") @Qualifier("gatewayOSDServiceImpl")
private ITSAService tsaService; private ITSAService tsaService;
private static final List<String> INIT_TOPICS_SUFFIX = List.of(
OSD_SUF, STATE_SUF, SERVICES_SUF + _REPLY_SUF, REQUESTS_SUF, EVENTS_SUF, PROPERTY_SUF + SET_SUF + _REPLY_SUF);
@Override @Override
public Boolean deviceOffline(String gatewaySn) { public Boolean deviceOffline(String gatewaySn) {
this.subscribeTopicOnline(gatewaySn); this.subscribeTopicOnline(gatewaySn);
@ -111,26 +113,24 @@ public class DeviceServiceImpl implements IDeviceService {
Optional<DeviceDTO> gatewayOpt = this.getDeviceBySn(gatewaySn); Optional<DeviceDTO> gatewayOpt = this.getDeviceBySn(gatewaySn);
if (gatewayOpt.isPresent()) { if (gatewayOpt.isPresent()) {
DeviceDTO value = gatewayOpt.get(); DeviceDTO value = gatewayOpt.get();
value.setChildDeviceSn(value.getDeviceSn());
value.setBoundTime(null); value.setBoundTime(null);
value.setLoginTime(null); value.setLoginTime(null);
redisOps.setWithExpire(key, value, RedisConst.DEVICE_ALIVE_SECOND); redisOps.setWithExpire(key, value, RedisConst.DEVICE_ALIVE_SECOND);
this.pushDeviceOnlineTopo(value.getWorkspaceId(), gatewaySn, gatewaySn); this.pushDeviceOnlineTopo(value.getWorkspaceId(), gatewaySn, gatewaySn);
return true; return true;
} }
// When connecting for the first time
DeviceDTO gateway = DeviceDTO.builder() DeviceDTO gateway = DeviceDTO.builder()
.deviceSn(gatewaySn) .deviceSn(gatewaySn)
.childDeviceSn(gatewaySn)
.domain(DeviceDomainEnum.GATEWAY.getDesc()) .domain(DeviceDomainEnum.GATEWAY.getDesc())
.build(); .build();
gatewayOpt.map(DeviceDTO::getWorkspaceId).ifPresent(gateway::setWorkspaceId);
redisOps.setWithExpire(key, gateway, RedisConst.DEVICE_ALIVE_SECOND); redisOps.setWithExpire(key, gateway, RedisConst.DEVICE_ALIVE_SECOND);
this.pushDeviceOnlineTopo(gateway.getWorkspaceId(), gatewaySn, gatewaySn);
return true; return true;
} }
String deviceSn = ((DeviceDTO)(redisOps.get(key))).getChildDeviceSn(); DeviceDTO deviceDTO = (DeviceDTO) (redisOps.get(key));
if (deviceSn.equals(gatewaySn)) { String deviceSn = deviceDTO.getChildDeviceSn();
if (!StringUtils.hasText(deviceSn)) {
return true; return true;
} }
@ -139,21 +139,23 @@ public class DeviceServiceImpl implements IDeviceService {
@Override @Override
public Boolean subDeviceOffline(String deviceSn) { public Boolean subDeviceOffline(String deviceSn) {
// Cancel drone-related subscriptions.
this.unsubscribeTopicOffline(deviceSn);
payloadService.deletePayloadsByDeviceSn(new ArrayList<>(List.of(deviceSn))); // If no information about this device exists in the cache, the drone is considered to be offline.
// If no information about this gateway device exists in the database, the drone is considered to be offline.
String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn;
if (!redisOps.checkExist(key) || redisOps.getExpire(key) <= 0) { if (!redisOps.checkExist(key) || redisOps.getExpire(key) <= 0) {
log.debug("The drone is already offline."); log.debug("The drone is already offline.");
return true; return true;
} }
DeviceDTO device = (DeviceDTO) redisOps.get(key); DeviceDTO device = (DeviceDTO) redisOps.get(key);
// Cancel drone-related subscriptions.
this.unsubscribeTopicOffline(deviceSn);
payloadService.deletePayloadsByDeviceSn(new ArrayList<>(List.of(deviceSn)));
// Publish the latest device topology information in the current workspace. // Publish the latest device topology information in the current workspace.
this.pushDeviceOfflineTopo(device.getWorkspaceId(), deviceSn); this.pushDeviceOfflineTopo(device.getWorkspaceId(), deviceSn);
redisOps.del(key); redisOps.del(key);
redisOps.del(RedisConst.OSD_PREFIX + device.getDeviceSn());
log.debug("{} offline.", deviceSn); log.debug("{} offline.", deviceSn);
return true; return true;
} }
@ -264,20 +266,14 @@ public class DeviceServiceImpl implements IDeviceService {
return; return;
} }
} }
topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + OSD_SUF); String prefix = THING_MODEL_PRE + PRODUCT + sn;
topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + STATE_SUF); INIT_TOPICS_SUFFIX.forEach(suffix -> topicService.subscribe(prefix + suffix));
topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + SERVICES_SUF + _REPLY_SUF);
topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + REQUESTS_SUF);
topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + EVENTS_SUF);
} }
@Override @Override
public void unsubscribeTopicOffline(String sn) { public void unsubscribeTopicOffline(String sn) {
topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + OSD_SUF); String prefix = THING_MODEL_PRE + PRODUCT + sn;
topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + STATE_SUF); INIT_TOPICS_SUFFIX.forEach(suffix -> topicService.unsubscribe(prefix + suffix));
topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + SERVICES_SUF + _REPLY_SUF);
topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + REQUESTS_SUF);
topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + EVENTS_SUF);
} }
@Override @Override
@ -459,7 +455,10 @@ public class DeviceServiceImpl implements IDeviceService {
} }
@Override @Override
public void handleOSD(String topic, byte[] payload) { @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD)
public void handleOSD(Message<?> message) {
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString();
byte[] payload = (byte[])message.getPayload();
CommonTopicReceiver receiver; CommonTopicReceiver receiver;
try { try {
String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(),
@ -568,7 +567,7 @@ public class DeviceServiceImpl implements IDeviceService {
// Query the model information of this gateway device. // Query the model information of this gateway device.
Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService
.getOneDictionaryInfoByTypeSubType(gateway.getType(), gateway.getSubType()); .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.GATEWAY.getVal(), gateway.getType(), gateway.getSubType());
dictionaryOpt.ifPresent(entity -> dictionaryOpt.ifPresent(entity ->
builder.deviceName(entity.getDeviceName()) builder.deviceName(entity.getDeviceName())
@ -598,7 +597,7 @@ public class DeviceServiceImpl implements IDeviceService {
// Query the model information of this drone device. // Query the model information of this drone device.
Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService
.getOneDictionaryInfoByTypeSubType(device.getType(), device.getSubType()); .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.SUB_DEVICE.getVal(), device.getType(), device.getSubType());
dictionaryOpt.ifPresent(dictionary -> dictionaryOpt.ifPresent(dictionary ->
builder.deviceName(dictionary.getDeviceName()) builder.deviceName(dictionary.getDeviceName())
@ -767,8 +766,8 @@ public class DeviceServiceImpl implements IDeviceService {
assert dock != null; assert dock != null;
Optional<DeviceEntity> dockEntityOpt = this.bindDevice2Entity(dock); Optional<DeviceEntity> dockEntityOpt = this.bindDevice2Entity(DeviceDomainEnum.DOCK.getVal(), dock);
Optional<DeviceEntity> droneEntityOpt = this.bindDevice2Entity(drone); Optional<DeviceEntity> droneEntityOpt = this.bindDevice2Entity(DeviceDomainEnum.SUB_DEVICE.getVal(), drone);
List<ErrorInfoReply> bindResult = new ArrayList<>(); List<ErrorInfoReply> bindResult = new ArrayList<>();
@ -855,7 +854,13 @@ public class DeviceServiceImpl implements IDeviceService {
} }
@Override @Override
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_FIRMWARE_VERSION)
public void updateFirmwareVersion(FirmwareVersionReceiver receiver) { public void updateFirmwareVersion(FirmwareVersionReceiver receiver) {
// If the reported version is empty, it will not be processed to prevent misleading page.
if (!StringUtils.hasText(receiver.getFirmwareVersion())) {
return;
}
if (receiver.getDomain() == DeviceDomainEnum.SUB_DEVICE) { if (receiver.getDomain() == DeviceDomainEnum.SUB_DEVICE) {
final DeviceDTO device = DeviceDTO.builder() final DeviceDTO device = DeviceDTO.builder()
.deviceSn(receiver.getSn()) .deviceSn(receiver.getSn())
@ -885,7 +890,7 @@ public class DeviceServiceImpl implements IDeviceService {
// The bids in the progress messages reported subsequently are the same. // The bids in the progress messages reported subsequently are the same.
String bid = UUID.randomUUID().toString(); String bid = UUID.randomUUID().toString();
Optional<ServiceReply> serviceReplyOpt = messageSender.publishWithReply( ServiceReply serviceReply = messageSender.publishWithReply(
topic, CommonTopicResponse.<Map<String, List<DeviceOtaCreateParam>>>builder() topic, CommonTopicResponse.<Map<String, List<DeviceOtaCreateParam>>>builder()
.tid(UUID.randomUUID().toString()) .tid(UUID.randomUUID().toString())
.bid(bid) .bid(bid)
@ -893,10 +898,6 @@ public class DeviceServiceImpl implements IDeviceService {
.method(ServicesMethodEnum.OTA_CREATE.getMethod()) .method(ServicesMethodEnum.OTA_CREATE.getMethod())
.data(Map.of(MapKeyConst.DEVICES, deviceOtaFirmwares)) .data(Map.of(MapKeyConst.DEVICES, deviceOtaFirmwares))
.build()); .build());
if (serviceReplyOpt.isEmpty()) {
return ResponseResult.error("No message reply received.");
}
ServiceReply serviceReply = serviceReplyOpt.get();
if (serviceReply.getResult() != ResponseResult.CODE_SUCCESS) { if (serviceReply.getResult() != ResponseResult.CODE_SUCCESS) {
return ResponseResult.error(serviceReply.getResult(), "Firmware Error Code: " + serviceReply.getResult()); return ResponseResult.error(serviceReply.getResult(), "Firmware Error Code: " + serviceReply.getResult());
} }
@ -910,6 +911,79 @@ public class DeviceServiceImpl implements IDeviceService {
return ResponseResult.success(); return ResponseResult.success();
} }
@Override
public void devicePropertySet(String workspaceId, String dockSn, DeviceSetPropertyEnum propertyEnum, JsonNode param) {
boolean dockOnline = this.checkDeviceOnline(dockSn);
if (!dockOnline) {
throw new RuntimeException("Dock is offline.");
}
DeviceDTO deviceDTO = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn);
boolean deviceOnline = this.checkDeviceOnline(deviceDTO.getChildDeviceSn());
if (!deviceOnline) {
throw new RuntimeException("Device is offline.");
}
// Make sure the data is valid.
BasicDeviceProperty basicDeviceProperty = objectMapper.convertValue(param, propertyEnum.getClazz());
boolean valid = basicDeviceProperty.valid();
if (!valid) {
throw new IllegalArgumentException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg());
}
String topic = THING_MODEL_PRE + PRODUCT + dockSn + PROPERTY_SUF + SET_SUF;
OsdSubDeviceReceiver osd = (OsdSubDeviceReceiver) redisOps.get(RedisConst.OSD_PREFIX + deviceDTO.getChildDeviceSn());
if (!param.isObject()) {
this.deviceOnePropertySet(topic, propertyEnum, Map.entry(propertyEnum.getProperty(), param));
return;
}
// If there are multiple parameters, set them separately.
for (Iterator<Map.Entry<String, JsonNode>> filed = param.fields(); filed.hasNext(); ) {
Map.Entry<String, JsonNode> node = filed.next();
boolean isPublish = basicDeviceProperty.canPublish(node.getKey(), osd);
if (!isPublish) {
continue;
}
this.deviceOnePropertySet(topic, propertyEnum, Map.entry(propertyEnum.getProperty(), node));
}
}
@Override
public void deviceOnePropertySet(String topic, DeviceSetPropertyEnum propertyEnum, Map.Entry<String, Object> value) {
if (Objects.isNull(value) || Objects.isNull(value.getValue())) {
throw new IllegalArgumentException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg());
}
Map reply = messageSender.publishWithReply(
Map.class, topic,
CommonTopicResponse.builder()
.bid(UUID.randomUUID().toString())
.tid(UUID.randomUUID().toString())
.timestamp(System.currentTimeMillis())
.data(value)
.build(),
2);
while (true) {
reply = (Map<String, Object>) reply.get(value.getKey());
if (value.getValue() instanceof JsonNode) {
break;
}
value = (Map.Entry) value.getValue();
}
SetReply setReply = objectMapper.convertValue(reply, SetReply.class);
if (SetReplyStatusResultEnum.SUCCESS.getVal() != setReply.getResult()) {
throw new RuntimeException("Failed to set " + value.getKey() + "; Error Code: " + setReply.getResult());
}
}
public Boolean checkDeviceOnline(String sn) {
String key = RedisConst.DEVICE_ONLINE_PREFIX + sn;
return redisOps.checkExist(key) && redisOps.getExpire(key) > 0;
}
/** /**
* Convert device data transfer object into database entity object. * Convert device data transfer object into database entity object.
* @param dto * @param dto
@ -940,15 +1014,17 @@ public class DeviceServiceImpl implements IDeviceService {
/** /**
* Convert device binding data object into database entity object. * Convert device binding data object into database entity object.
*
* @param domain
* @param receiver * @param receiver
* @return * @return
*/ */
private Optional<DeviceEntity> bindDevice2Entity(BindDeviceReceiver receiver) { private Optional<DeviceEntity> bindDevice2Entity(Integer domain, BindDeviceReceiver receiver) {
if (receiver == null) { if (receiver == null) {
return Optional.empty(); return Optional.empty();
} }
int[] droneKey = Arrays.stream(receiver.getDeviceModelKey().split("-")).mapToInt(Integer::parseInt).toArray(); int[] droneKey = Arrays.stream(receiver.getDeviceModelKey().split("-")).mapToInt(Integer::parseInt).toArray();
Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService.getOneDictionaryInfoByTypeSubType(droneKey[1], droneKey[2]); Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService.getOneDictionaryInfoByTypeSubType(domain, droneKey[1], droneKey[2]);
DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder(); DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder();
dictionaryOpt.ifPresent(entity -> dictionaryOpt.ifPresent(entity ->

9
src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java

@ -8,10 +8,10 @@ import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.dto.TelemetryDTO; import com.dji.sample.manage.model.dto.TelemetryDTO;
import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.model.receiver.OsdDockReceiver; import com.dji.sample.manage.model.receiver.OsdDockReceiver;
import com.dji.sample.manage.model.receiver.OsdDockTransmissionReceiver;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Collection; import java.util.Collection;
import java.util.Objects;
/** /**
* @author sean * @author sean
@ -39,11 +39,10 @@ public class DockOSDServiceImpl extends AbstractTSAService {
if (DeviceDomainEnum.DOCK.getDesc().equals(device.getDomain())) { if (DeviceDomainEnum.DOCK.getDesc().equals(device.getDomain())) {
wsMessage.setBizCode(BizCodeEnum.DOCK_OSD.getCode()); wsMessage.setBizCode(BizCodeEnum.DOCK_OSD.getCode());
OsdDockReceiver data = mapper.convertValue(receiver.getData(), OsdDockReceiver.class); OsdDockReceiver data = mapper.convertValue(receiver.getData(), OsdDockReceiver.class);
wsMessage.getData().setHost(data); if (Objects.nonNull(data.getMaintainStatus())) {
if (data.getSubDevice() == null) { return;
OsdDockTransmissionReceiver transmission = mapper.convertValue(receiver.getData(), OsdDockTransmissionReceiver.class);
wsMessage.getData().setHost(transmission);
} }
wsMessage.getData().setHost(data);
sendMessageService.sendBatch(webSessions, wsMessage); sendMessageService.sendBatch(webSessions, wsMessage);
} }
} }

75
src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java

@ -70,7 +70,7 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
return devicesList.stream() return devicesList.stream()
.filter(device -> redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())) .filter(device -> redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()))
.map(device -> CapacityDeviceDTO.builder() .map(device -> CapacityDeviceDTO.builder()
.name(device.getDeviceName()) .name(Objects.requireNonNullElse(device.getNickname(), device.getDeviceName()))
.sn(device.getDeviceSn()) .sn(device.getDeviceSn())
.camerasList(capacityCameraService.getCapacityCameraByDeviceSn(device.getDeviceSn())) .camerasList(capacityCameraService.getCapacityCameraByDeviceSn(device.getDeviceSn()))
.build()) .build())
@ -96,7 +96,7 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
public ResponseResult liveStart(LiveTypeDTO liveParam) { public ResponseResult liveStart(LiveTypeDTO liveParam) {
// Check if this lens is available live. // Check if this lens is available live.
ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId()); ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId());
if (responseResult.getCode() != 0) { if (ResponseResult.CODE_SUCCESS != responseResult.getCode()) {
return responseResult; return responseResult;
} }
@ -104,13 +104,10 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
// target topic // target topic
String respTopic = THING_MODEL_PRE + PRODUCT + String respTopic = THING_MODEL_PRE + PRODUCT +
data.getDeviceSn() + SERVICES_SUF; data.getDeviceSn() + SERVICES_SUF;
Optional<ServiceReply> receiveReplyOpt = this.publishLiveStart(respTopic, liveParam); ServiceReply receiveReply = this.publishLiveStart(respTopic, liveParam);
if (receiveReplyOpt.isEmpty()) { if (ResponseResult.CODE_SUCCESS != receiveReply.getResult()) {
return ResponseResult.error(LiveErrorEnum.NO_REPLY); return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult()));
}
if (receiveReplyOpt.get().getResult() != 0) {
return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult()));
} }
LiveUrlTypeEnum urlType = LiveUrlTypeEnum.find(liveParam.getUrlType()); LiveUrlTypeEnum urlType = LiveUrlTypeEnum.find(liveParam.getUrlType());
@ -132,7 +129,7 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
.toString()); .toString());
break; break;
case RTSP: case RTSP:
String url = receiveReplyOpt.get().getInfo().toString(); String url = receiveReply.getInfo().toString();
this.resolveUrlUser(url, live); this.resolveUrlUser(url, live);
break; break;
case UNKNOWN: case UNKNOWN:
@ -151,12 +148,9 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF;
Optional<ServiceReply> receiveReplyOpt = this.publishLiveStop(respTopic, videoId); ServiceReply receiveReply = this.publishLiveStop(respTopic, videoId);
if (receiveReplyOpt.isEmpty()) { if (receiveReply.getResult() != 0) {
return ResponseResult.error(LiveErrorEnum.NO_REPLY); return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult()));
}
if (receiveReplyOpt.get().getResult() != 0) {
return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult()));
} }
return ResponseResult.success(); return ResponseResult.success();
@ -177,23 +171,58 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF;
Optional<ServiceReply> receiveReplyOpt = this.publishLiveSetQuality(respTopic, liveParam); ServiceReply receiveReply = this.publishLiveSetQuality(respTopic, liveParam);
if (receiveReplyOpt.isEmpty()) { if (ResponseResult.CODE_SUCCESS == receiveReply.getResult()) {
return ResponseResult.error(LiveErrorEnum.NO_REPLY); return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult()));
} }
if (receiveReplyOpt.get().getResult() != 0) {
return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult())); return ResponseResult.success();
}
@Override
public ResponseResult liveLensChange(LiveTypeDTO liveParam) {
if (!StringUtils.hasText(liveParam.getVideoType())) {
return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS);
}
ResponseResult<DeviceDTO> responseResult = this.checkBeforeLive(liveParam.getVideoId());
if (ResponseResult.CODE_SUCCESS != responseResult.getCode()) {
return responseResult;
}
if (DeviceDomainEnum.GATEWAY.getDesc().equals(responseResult.getData().getDomain())) {
return ResponseResult.error(LiveErrorEnum.FUNCTION_NOT_SUPPORT);
}
String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF;
ServiceReply receiveReply = this.publishLiveLensChange(respTopic, liveParam);
if (ResponseResult.CODE_SUCCESS != receiveReply.getResult()) {
return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult()));
} }
return ResponseResult.success(); return ResponseResult.success();
} }
private ServiceReply publishLiveLensChange(String respTopic, LiveTypeDTO liveParam) {
CommonTopicResponse<LiveTypeDTO> response = new CommonTopicResponse<>();
response.setTid(UUID.randomUUID().toString());
response.setBid(UUID.randomUUID().toString());
response.setMethod(ServicesMethodEnum.LIVE_LENS_CHANGE.getMethod());
response.setData(liveParam);
return messageSender.publishWithReply(respTopic, response);
}
/** /**
* Check if this lens is available live. * Check if this lens is available live.
* @param videoId * @param videoId
* @return * @return
*/ */
private ResponseResult<DeviceDTO> checkBeforeLive(String videoId) { private ResponseResult<DeviceDTO> checkBeforeLive(String videoId) {
if (!StringUtils.hasText(videoId)) {
return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS);
}
String[] videoIdArr = videoId.split("/"); String[] videoIdArr = videoId.split("/");
// drone sn / enumeration value of the location where the payload is mounted / payload lens // drone sn / enumeration value of the location where the payload is mounted / payload lens
if (videoIdArr.length != 3) { if (videoIdArr.length != 3) {
@ -272,7 +301,7 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
* @param liveParam * @param liveParam
* @return * @return
*/ */
private Optional<ServiceReply> publishLiveStart(String topic, LiveTypeDTO liveParam) { private ServiceReply publishLiveStart(String topic, LiveTypeDTO liveParam) {
CommonTopicResponse<LiveTypeDTO> response = new CommonTopicResponse<>(); CommonTopicResponse<LiveTypeDTO> response = new CommonTopicResponse<>();
response.setTid(UUID.randomUUID().toString()); response.setTid(UUID.randomUUID().toString());
response.setBid(UUID.randomUUID().toString()); response.setBid(UUID.randomUUID().toString());
@ -288,7 +317,7 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
* @param liveParam * @param liveParam
* @return * @return
*/ */
private Optional<ServiceReply> publishLiveSetQuality(String respTopic, LiveTypeDTO liveParam) { private ServiceReply publishLiveSetQuality(String respTopic, LiveTypeDTO liveParam) {
Map<String, Object> data = new ConcurrentHashMap<>(Map.of( Map<String, Object> data = new ConcurrentHashMap<>(Map.of(
"video_id", liveParam.getVideoId(), "video_id", liveParam.getVideoId(),
"video_quality", liveParam.getVideoQuality())); "video_quality", liveParam.getVideoQuality()));
@ -307,7 +336,7 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
* @param videoId * @param videoId
* @return * @return
*/ */
private Optional<ServiceReply> publishLiveStop(String topic, String videoId) { private ServiceReply publishLiveStop(String topic, String videoId) {
Map<String, String> data = new ConcurrentHashMap<>(Map.of("video_id", videoId)); Map<String, String> data = new ConcurrentHashMap<>(Map.of("video_id", videoId));
CommonTopicResponse<Map<String, String>> response = new CommonTopicResponse<>(); CommonTopicResponse<Map<String, String>> response = new CommonTopicResponse<>();
response.setTid(UUID.randomUUID().toString()); response.setTid(UUID.randomUUID().toString());

2
src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java

@ -50,7 +50,7 @@ public class LogsFileIndexServiceImpl implements ILogsFileIndexService {
files.forEach(file -> { files.forEach(file -> {
Optional<LogsFileUpload> fileOpt = this.getFileIndexByFileId(file.getFileId()); Optional<LogsFileUpload> fileOpt = this.getFileIndexByFileId(file.getFileId());
fileOpt.ifPresent(fileUpload -> { fileOpt.ifPresent(fileUpload -> {
fileUpload.setObjectKey(file.getStatus() ? file.getObjectKey() : ""); fileUpload.setObjectKey(file.getStatus() ? file.getObjectKey() : null);
list.add(fileUpload); list.add(fileUpload);
}); });
}); });

19
src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java

@ -1,6 +1,8 @@
package com.dji.sample.manage.service.impl; package com.dji.sample.manage.service.impl;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -29,6 +31,7 @@ import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -115,10 +118,22 @@ public class UserServiceImpl implements IUserService {
if (!StringUtils.hasText(token)) { if (!StringUtils.hasText(token)) {
return Optional.empty(); return Optional.empty();
} }
CustomClaim customClaim = new CustomClaim(JWT.decode(token).getClaims()); CustomClaim customClaim;
try {
DecodedJWT jwt = JwtUtil.verifyToken(token);
customClaim = new CustomClaim(jwt.getClaims());
} catch (TokenExpiredException e) {
customClaim = new CustomClaim(JWT.decode(token).getClaims());
} catch (Exception e) {
e.printStackTrace();
return Optional.empty();
}
String refreshToken = JwtUtil.createToken(customClaim.convertToMap()); String refreshToken = JwtUtil.createToken(customClaim.convertToMap());
UserDTO user = entityConvertToDTO(this.getUserByUsername(customClaim.getUsername())); UserDTO user = entityConvertToDTO(this.getUserByUsername(customClaim.getUsername()));
if (Objects.isNull(user)) {
return Optional.empty();
}
user.setWorkspaceId(customClaim.getWorkspaceId()); user.setWorkspaceId(customClaim.getWorkspaceId());
user.setAccessToken(refreshToken); user.setAccessToken(refreshToken);
return Optional.of(user); return Optional.of(user);
@ -195,7 +210,7 @@ public class UserServiceImpl implements IUserService {
*/ */
private UserDTO entityConvertToDTO(UserEntity entity) { private UserDTO entityConvertToDTO(UserEntity entity) {
if (entity == null) { if (entity == null) {
return new UserDTO(); return null;
} }
return UserDTO.builder() return UserDTO.builder()
.userId(entity.getUserId()) .userId(entity.getUserId())

3
src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java

@ -7,6 +7,7 @@ import com.dji.sample.common.model.PaginationData;
import com.dji.sample.component.oss.model.OssConfiguration; import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.oss.service.impl.OssServiceContext; import com.dji.sample.component.oss.service.impl.OssServiceContext;
import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; import com.dji.sample.manage.model.dto.DeviceDictionaryDTO;
import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.service.IDeviceDictionaryService; import com.dji.sample.manage.service.IDeviceDictionaryService;
import com.dji.sample.media.dao.IFileMapper; import com.dji.sample.media.dao.IFileMapper;
import com.dji.sample.media.model.FileUploadDTO; import com.dji.sample.media.model.FileUploadDTO;
@ -133,7 +134,7 @@ public class FileServiceImpl implements IFileService {
.mapToInt(Integer::intValue) .mapToInt(Integer::intValue)
.toArray(); .toArray();
Optional<DeviceDictionaryDTO> payloadDict = deviceDictionaryService Optional<DeviceDictionaryDTO> payloadDict = deviceDictionaryService
.getOneDictionaryInfoByTypeSubType(payloadModel[1], payloadModel[2]); .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), payloadModel[1], payloadModel[2]);
payloadDict.ifPresent(payload -> builder.payload(payload.getDeviceName())); payloadDict.ifPresent(payload -> builder.payload(payload.getDeviceName()));
} }
return builder.build(); return builder.build();

15
src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java

@ -3,6 +3,8 @@ package com.dji.sample.media.service.impl;
import com.dji.sample.common.model.ResponseResult; import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.mqtt.model.*; import com.dji.sample.component.mqtt.model.*;
import com.dji.sample.component.mqtt.service.IMessageSenderService; import com.dji.sample.component.mqtt.service.IMessageSenderService;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.media.model.FileUploadCallback; import com.dji.sample.media.model.FileUploadCallback;
import com.dji.sample.media.model.FileUploadDTO; import com.dji.sample.media.model.FileUploadDTO;
import com.dji.sample.media.model.MediaFileDTO; import com.dji.sample.media.model.MediaFileDTO;
@ -39,6 +41,9 @@ public class MediaServiceImpl implements IMediaService {
@Autowired @Autowired
private IMessageSenderService messageSenderService; private IMessageSenderService messageSenderService;
@Autowired
private IDeviceService deviceService;
@Override @Override
public Boolean fastUpload(String workspaceId, String fingerprint) { public Boolean fastUpload(String workspaceId, String fingerprint) {
return fileService.checkExist(workspaceId, fingerprint); return fileService.checkExist(workspaceId, fingerprint);
@ -77,7 +82,7 @@ public class MediaServiceImpl implements IMediaService {
CommonTopicResponse<Object> data = CommonTopicResponse.builder() CommonTopicResponse<Object> data = CommonTopicResponse.builder()
.timestamp(System.currentTimeMillis()) .timestamp(System.currentTimeMillis())
.method(EventsMethodEnum.FILE_UPLOAD_CALLBACK.getMethod()) .method(EventsMethodEnum.FILE_UPLOAD_CALLBACK.getMethod())
.data(ResponseResult.success()) .data(RequestsReply.success())
.tid(receiver.getTid()) .tid(receiver.getTid())
.bid(receiver.getBid()) .bid(receiver.getBid())
.build(); .build();
@ -85,6 +90,14 @@ public class MediaServiceImpl implements IMediaService {
String jobId = callback.getFile().getExt().getFlightId(); String jobId = callback.getFile().getExt().getFlightId();
Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(jobId); Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(jobId);
if (jobOpt.isPresent()) { if (jobOpt.isPresent()) {
// Set the drone sn that shoots the media
Optional<DeviceDTO> dockDTO = deviceService.getDeviceBySn(jobOpt.get().getDockSn());
dockDTO.ifPresent(dock -> callback.getFile().getExt().setSn(dock.getChildDeviceSn()));
// set path
String objectKey = callback.getFile().getObjectKey();
callback.getFile().setPath(objectKey.substring(objectKey.indexOf("/") + 1, objectKey.lastIndexOf("/")));
int id = fileService.saveFile(jobOpt.get().getWorkspaceId(), callback.getFile()); int id = fileService.saveFile(jobOpt.get().getWorkspaceId(), callback.getFile());
if (id <= 0) { if (id <= 0) {
data.setData(ResponseResult.error()); data.setData(ResponseResult.error());

19
src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java

@ -9,6 +9,7 @@ import com.dji.sample.wayline.model.param.WaylineQueryParam;
import com.dji.sample.wayline.service.IWaylineFileService; import com.dji.sample.wayline.service.IWaylineFileService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -16,6 +17,7 @@ import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Objects;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
@ -158,4 +160,21 @@ public class WaylineFileController {
boolean isDel = waylineFileService.deleteByWaylineId(workspaceId, waylineId); boolean isDel = waylineFileService.deleteByWaylineId(workspaceId, waylineId);
return isDel ? ResponseResult.success() : ResponseResult.error("Failed to delete wayline."); return isDel ? ResponseResult.success() : ResponseResult.error("Failed to delete wayline.");
} }
/**
* Import kmz wayline files.
* @param file
* @return
*/
@PostMapping("/{workspace_id}/waylines/file/upload")
public ResponseResult importKmzFile(HttpServletRequest request, MultipartFile file) {
if (Objects.isNull(file)) {
return ResponseResult.error("No file received.");
}
CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
String workspaceId = customClaim.getWorkspaceId();
String creator = customClaim.getUsername();
waylineFileService.importKmzFile(file, workspaceId, creator);
return ResponseResult.success();
}
} }

15
src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java

@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
@ -39,8 +40,8 @@ public class WaylineJobController {
@PathVariable(name = "workspace_id") String workspaceId) throws SQLException { @PathVariable(name = "workspace_id") String workspaceId) throws SQLException {
CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
customClaim.setWorkspaceId(workspaceId); customClaim.setWorkspaceId(workspaceId);
boolean isCreate = waylineJobService.createJob(param, customClaim);
return isCreate ? ResponseResult.success() : ResponseResult.error(); return waylineJobService.publishFlightTask(param, customClaim);
} }
/** /**
@ -59,16 +60,16 @@ public class WaylineJobController {
} }
/** /**
* Issue wayline mission to the dock for execution. * Send the command to cancel the jobs.
* @param jobId * @param jobIds
* @param workspaceId * @param workspaceId
* @return * @return
* @throws SQLException * @throws SQLException
*/ */
@PostMapping("/{workspace_id}/jobs/{job_id}") @DeleteMapping("/{workspace_id}/jobs")
public ResponseResult publishJob(@PathVariable(name = "job_id") String jobId, public ResponseResult publishCancelJob(@RequestParam(name = "job_id") List<String> jobIds,
@PathVariable(name = "workspace_id") String workspaceId) throws SQLException { @PathVariable(name = "workspace_id") String workspaceId) throws SQLException {
waylineJobService.publishFlightTask(workspaceId, jobId); waylineJobService.cancelFlightTask(workspaceId, jobIds);
return ResponseResult.success(); return ResponseResult.success();
} }
} }

6
src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java

@ -18,7 +18,11 @@ public class FlightTaskCreateDTO {
private String flightId; private String flightId;
private String type; private Integer taskType;
private Integer waylineType;
private Long executeTime;
private FlightTaskFileDTO file; private FlightTaskFileDTO file;
} }

2
src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java

@ -18,5 +18,5 @@ public class FlightTaskFileDTO {
private String url; private String url;
private String sign; private String fingerprint;
} }

39
src/main/java/com/dji/sample/wayline/model/dto/KmzFileProperties.java

@ -0,0 +1,39 @@
package com.dji.sample.wayline.model.dto;
/**
* @author sean
* @version 1.3
* @date 2022/10/27
*/
public class KmzFileProperties {
private KmzFileProperties() {
}
public static final String WAYLINE_FILE_SUFFIX = ".kmz";
public static final String FILE_DIR_FIRST = "wpmz";
public static final String FILE_DIR_SECOND_RES = "res";
public static final String FILE_DIR_SECOND_TEMPLATE = "template.kml";
public static final String FILE_DIR_SECOND_WAYLINES = "waylines.wpml";
public static final String TAG_WPML_PREFIX = "wpml:";
public static final String TAG_DRONE_INFO = "droneInfo";
public static final String TAG_DRONE_ENUM_VALUE = "droneEnumValue";
public static final String TAG_DRONE_SUB_ENUM_VALUE = "droneSubEnumValue";
public static final String TAG_PAYLOAD_INFO = "payloadInfo";
public static final String TAG_PAYLOAD_ENUM_VALUE = "payloadEnumValue";
public static final String TAG_PAYLOAD_SUB_ENUM_VALUE = "payloadSubEnumValue";
public static final String TAG_TEMPLATE_ID = "templateId";
}

15
src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java

@ -32,12 +32,19 @@ public class WaylineJobDTO {
private String workspaceId; private String workspaceId;
private String bid; private Integer waylineType;
private String type; private Integer taskType;
private String username; private LocalDateTime executeTime;
private LocalDateTime endTime;
private Integer status;
private LocalDateTime updateTime; private Integer progress;
private String username;
private Integer code;
} }

22
src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java

@ -17,7 +17,7 @@ import java.io.Serializable;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@TableName("wayline_job") @TableName("wayline_job_new")
public class WaylineJobEntity implements Serializable { public class WaylineJobEntity implements Serializable {
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
@ -38,15 +38,27 @@ public class WaylineJobEntity implements Serializable {
@TableField("workspace_id") @TableField("workspace_id")
private String workspaceId; private String workspaceId;
@TableField("bid") @TableField("task_type")
private String bid; private Integer taskType;
@TableField("type") @TableField("wayline_type")
private String type; private Integer waylineType;
@TableField("username") @TableField("username")
private String username; private String username;
@TableField("execute_time")
private Long executeTime;
@TableField("end_time")
private Long endTime;
@TableField("error_code")
private Integer errorCode;
@TableField("status")
private Integer status;
@TableField(value = "create_time", fill = FieldFill.INSERT) @TableField(value = "create_time", fill = FieldFill.INSERT)
private Long createTime; private Long createTime;

39
src/main/java/com/dji/sample/wayline/model/enums/WaylineJobStatusEnum.java

@ -0,0 +1,39 @@
package com.dji.sample.wayline.model.enums;
import lombok.Getter;
import java.util.Arrays;
/**
* @author sean
* @version 1.3
* @date 2022/9/26
*/
@Getter
public enum WaylineJobStatusEnum {
PENDING(1, false),
IN_PROGRESS(2, false),
SUCCESS(3, true),
CANCEL(4, true),
FAILED(5, true),
UNKNOWN(6, true);
int val;
Boolean end;
WaylineJobStatusEnum(int val, boolean end) {
this.end = end;
this.val = val;
}
public static WaylineJobStatusEnum find(int val) {
return Arrays.stream(WaylineJobStatusEnum.values()).filter(statue -> statue.val == val).findAny().orElse(UNKNOWN);
}
}

22
src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java

@ -0,0 +1,22 @@
package com.dji.sample.wayline.model.enums;
import lombok.Getter;
/**
* @author sean
* @version 1.3
* @date 2022/9/26
*/
@Getter
public enum WaylineTaskTypeEnum {
IMMEDIATE(0),
TIMED(1);
int val;
WaylineTaskTypeEnum(int val) {
this.val = val;
}
}

26
src/main/java/com/dji/sample/wayline/model/enums/WaylineTemplateTypeEnum.java

@ -0,0 +1,26 @@
package com.dji.sample.wayline.model.enums;
import lombok.Getter;
/**
* @author sean
* @version 1.3
* @date 2022/9/26
*/
@Getter
public enum WaylineTemplateTypeEnum {
WAYPOINT(0),
MAPPING_2D(1),
MAPPING_3D(2),
MAPPING_STRIP(4);
int val;
WaylineTemplateTypeEnum(int val) {
this.val = val;
}
}

6
src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java

@ -16,7 +16,9 @@ public class CreateJobParam {
private String dockSn; private String dockSn;
private String type; private Integer waylineType;
private boolean immediate; private Integer taskType;
private Long executeTime;
} }

10
src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java

@ -3,6 +3,7 @@ package com.dji.sample.wayline.service;
import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.PaginationData;
import com.dji.sample.wayline.model.dto.WaylineFileDTO; import com.dji.sample.wayline.model.dto.WaylineFileDTO;
import com.dji.sample.wayline.model.param.WaylineQueryParam; import com.dji.sample.wayline.model.param.WaylineQueryParam;
import org.springframework.web.multipart.MultipartFile;
import java.net.URL; import java.net.URL;
import java.sql.SQLException; import java.sql.SQLException;
@ -71,4 +72,13 @@ public interface IWaylineFileService {
* @param waylineId * @param waylineId
*/ */
Boolean deleteByWaylineId(String workspaceId, String waylineId); Boolean deleteByWaylineId(String workspaceId, String waylineId);
/**
* Import kmz wayline file.
* @param file
* @param workspaceId
* @param creator
* @return
*/
void importKmzFile(MultipartFile file, String workspaceId, String creator);
} }

37
src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java

@ -2,10 +2,14 @@ package com.dji.sample.wayline.service;
import com.dji.sample.common.model.CustomClaim; import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.PaginationData;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.wayline.model.dto.WaylineJobDTO; import com.dji.sample.wayline.model.dto.WaylineJobDTO;
import com.dji.sample.wayline.model.param.CreateJobParam; import com.dji.sample.wayline.model.param.CreateJobParam;
import org.springframework.messaging.MessageHeaders;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection;
import java.util.Optional; import java.util.Optional;
/** /**
@ -16,20 +20,36 @@ import java.util.Optional;
public interface IWaylineJobService { public interface IWaylineJobService {
/** /**
* Create a wayline mission for the dock. * Create wayline job in the database.
* @param param * @param param
* @param customClaim user info * @param customClaim user info
* @return * @return
*/ */
Boolean createJob(CreateJobParam param, CustomClaim customClaim) throws SQLException; Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, CustomClaim customClaim);
/** /**
* Issue wayline mission to the dock for execution. * Issue wayline mission to the dock.
* @param workspaceId * @param param
* @param customClaim user info
* @return
*/
ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException;
/**
* Execute the task immediately.
* @param jobId * @param jobId
* @throws SQLException
* @return * @return
*/ */
void publishFlightTask(String workspaceId, String jobId) throws SQLException; Boolean executeFlightTask(String jobId);
/**
* Cancel the task Base on job Ids.
* @param workspaceId
* @param jobIds
* @throws SQLException
*/
void cancelFlightTask(String workspaceId, Collection<String> jobIds);
/** /**
* Query job information based on job id. * Query job information based on job id.
@ -53,4 +73,11 @@ public interface IWaylineJobService {
* @return * @return
*/ */
PaginationData<WaylineJobDTO> getJobsByWorkspaceId(String workspaceId, long page, long pageSize); PaginationData<WaylineJobDTO> getJobsByWorkspaceId(String workspaceId, long page, long pageSize);
/**
* Process to get interface data of flight mission resources.
* @param receiver
* @param headers
*/
void flightTaskResourceGet(CommonTopicReceiver receiver, MessageHeaders headers);
} }

78
src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java

@ -12,16 +12,25 @@ import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.wayline.model.dto.FlightTaskProgressReceiver; import com.dji.sample.wayline.model.dto.FlightTaskProgressReceiver;
import com.dji.sample.wayline.model.dto.WaylineJobDTO;
import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum;
import com.dji.sample.wayline.service.IFlightTaskService; import com.dji.sample.wayline.service.IFlightTaskService;
import com.dji.sample.wayline.service.IWaylineJobService;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/** /**
* @author sean * @author sean
* @version 1.1 * @version 1.1
@ -46,18 +55,42 @@ public class FlightTaskServiceImpl implements IFlightTaskService {
@Autowired @Autowired
private RedisOpsUtils redisOps; private RedisOpsUtils redisOps;
@Autowired
private IWaylineJobService waylineJobService;
@Override @Override
@ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS, outputChannel = ChannelName.OUTBOUND) @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS, outputChannel = ChannelName.OUTBOUND)
public void handleProgress(CommonTopicReceiver receiver, MessageHeaders headers) { public void handleProgress(CommonTopicReceiver receiver, MessageHeaders headers) {
EventsReceiver<FlightTaskProgressReceiver> eventsReceiver = mapper.convertValue(receiver.getData(), EventsReceiver<FlightTaskProgressReceiver> eventsReceiver = mapper.convertValue(receiver.getData(),
new TypeReference<EventsReceiver<FlightTaskProgressReceiver>>(){}); new TypeReference<EventsReceiver<FlightTaskProgressReceiver>>(){});
eventsReceiver.setBid(receiver.getBid()); eventsReceiver.setBid(receiver.getBid());
eventsReceiver.setSn(receiver.getGateway());
log.info("Task progress: " + eventsReceiver.getOutput().getProgress().toString()); FlightTaskProgressReceiver output = eventsReceiver.getOutput();
log.info("Task progress: {}", output.getProgress().toString());
if (eventsReceiver.getResult() != ResponseResult.CODE_SUCCESS) { if (eventsReceiver.getResult() != ResponseResult.CODE_SUCCESS) {
log.error("Error code: " + eventsReceiver.getResult()); log.error("Task progress ===> Error code: " + eventsReceiver.getResult());
}
EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus());
if (statusEnum.getEnd()) {
WaylineJobDTO job = WaylineJobDTO.builder()
.jobId(receiver.getBid())
.status(WaylineJobStatusEnum.SUCCESS.getVal())
.endTime(LocalDateTime.now())
.build();
if (EventsResultStatusEnum.OK != statusEnum) {
job.setCode(eventsReceiver.getResult());
job.setStatus(WaylineJobStatusEnum.FAILED.getVal());
}
waylineJobService.updateJob(job);
redisOps.del(receiver.getBid());
} }
redisOps.setWithExpire(receiver.getBid(), eventsReceiver, RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND);
DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway());
websocketMessageService.sendBatch( websocketMessageService.sendBatch(
@ -77,8 +110,47 @@ public class FlightTaskServiceImpl implements IFlightTaskService {
.bid(receiver.getBid()) .bid(receiver.getBid())
.method(EventsMethodEnum.FLIGHT_TASK_PROGRESS.getMethod()) .method(EventsMethodEnum.FLIGHT_TASK_PROGRESS.getMethod())
.timestamp(System.currentTimeMillis()) .timestamp(System.currentTimeMillis())
.data(ResponseResult.success()) .data(RequestsReply.success())
.build()); .build());
} }
} }
@Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS)
private void checkScheduledJob() {
Object jobIdValue = redisOps.zGetMin(RedisConst.WAYLINE_JOB);
log.info("Check the timed jobs of the wayline. {}", jobIdValue);
if (Objects.isNull(jobIdValue)) {
return;
}
String jobId = String.valueOf(jobIdValue);
double time = redisOps.zScore(RedisConst.WAYLINE_JOB, jobIdValue);
long now = System.currentTimeMillis();
int offset = 30_000;
// Expired tasks are deleted directly.
if (time < now - offset) {
redisOps.zRemove(RedisConst.WAYLINE_JOB, jobId);
waylineJobService.updateJob(WaylineJobDTO.builder()
.jobId(jobId)
.status(WaylineJobStatusEnum.FAILED.getVal())
.endTime(LocalDateTime.now())
.code(HttpStatus.SC_REQUEST_TIMEOUT).build());
return;
}
if (now <= time && time <= now + offset) {
try {
waylineJobService.executeFlightTask(jobId);
} catch (Exception e) {
log.info("The scheduled task delivery failed.");
waylineJobService.updateJob(WaylineJobDTO.builder()
.jobId(jobId)
.status(WaylineJobStatusEnum.FAILED.getVal())
.endTime(LocalDateTime.now())
.code(HttpStatus.SC_INTERNAL_SERVER_ERROR).build());
} finally {
redisOps.zRemove(RedisConst.WAYLINE_JOB, jobId);
}
}
}
} }

103
src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java

@ -7,24 +7,36 @@ import com.dji.sample.common.model.Pagination;
import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.PaginationData;
import com.dji.sample.component.oss.model.OssConfiguration; import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.oss.service.impl.OssServiceContext; import com.dji.sample.component.oss.service.impl.OssServiceContext;
import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.wayline.dao.IWaylineFileMapper; import com.dji.sample.wayline.dao.IWaylineFileMapper;
import com.dji.sample.wayline.model.dto.KmzFileProperties;
import com.dji.sample.wayline.model.dto.WaylineFileDTO; import com.dji.sample.wayline.model.dto.WaylineFileDTO;
import com.dji.sample.wayline.model.entity.WaylineFileEntity; import com.dji.sample.wayline.model.entity.WaylineFileEntity;
import com.dji.sample.wayline.model.param.WaylineQueryParam; import com.dji.sample.wayline.model.param.WaylineQueryParam;
import com.dji.sample.wayline.service.IWaylineFileService; import com.dji.sample.wayline.service.IWaylineFileService;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils; import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.*;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.dji.sample.wayline.model.dto.KmzFileProperties.WAYLINE_FILE_SUFFIX;
/** /**
* @author sean * @author sean
@ -94,13 +106,17 @@ public class WaylineFileServiceImpl implements IWaylineFileService {
file.setWaylineId(UUID.randomUUID().toString()); file.setWaylineId(UUID.randomUUID().toString());
file.setWorkspaceId(workspaceId); file.setWorkspaceId(workspaceId);
byte[] object = ossService.getObject(configuration.getBucket(), metadata.getObjectKey()); if (!StringUtils.hasText(file.getSign())) {
if (object.length == 0) { try (InputStream object = ossService.getObject(configuration.getBucket(), metadata.getObjectKey())) {
if (object.available() == 0) {
throw new RuntimeException("The file " + metadata.getObjectKey() + throw new RuntimeException("The file " + metadata.getObjectKey() +
" does not exist in the bucket[" + configuration.getBucket() + "]."); " does not exist in the bucket[" + configuration.getBucket() + "].");
} }
file.setSign(DigestUtils.md5DigestAsHex(object)); file.setSign(DigestUtils.md5DigestAsHex(object));
} catch (IOException e) {
e.printStackTrace();
}
}
int insertId = mapper.insert(file); int insertId = mapper.insert(file);
return insertId > 0 ? file.getId() : insertId; return insertId > 0 ? file.getId() : insertId;
} }
@ -146,6 +162,78 @@ public class WaylineFileServiceImpl implements IWaylineFileService {
return ossService.deleteObject(configuration.getBucket(), wayline.getObjectKey()); return ossService.deleteObject(configuration.getBucket(), wayline.getObjectKey());
} }
@Override
public void importKmzFile(MultipartFile file, String workspaceId, String creator) {
Optional<WaylineFileDTO> waylineFileOpt = validKmzFile(file);
if (waylineFileOpt.isEmpty()) {
throw new RuntimeException("The file format is incorrect.");
}
try {
WaylineFileDTO waylineFile = waylineFileOpt.get();
waylineFile.setWaylineId(workspaceId);
waylineFile.setUsername(creator);
ossService.putObject(configuration.getBucket(), waylineFile.getObjectKey(), file.getInputStream());
this.saveWaylineFile(workspaceId, waylineFile);
} catch (IOException e) {
e.printStackTrace();
}
}
private Optional<WaylineFileDTO> validKmzFile(MultipartFile file) {
String filename = file.getOriginalFilename();
if (Objects.nonNull(filename) && !filename.endsWith(WAYLINE_FILE_SUFFIX)) {
throw new RuntimeException("The file format is incorrect.");
}
try (ZipInputStream unzipFile = new ZipInputStream(file.getInputStream(), StandardCharsets.UTF_8)) {
ZipEntry nextEntry = unzipFile.getNextEntry();
while (Objects.nonNull(nextEntry)) {
boolean isWaylines = (KmzFileProperties.FILE_DIR_FIRST + File.separator + KmzFileProperties.FILE_DIR_SECOND_WAYLINES).equals(nextEntry.getName());
if (!isWaylines) {
nextEntry = unzipFile.getNextEntry();
continue;
}
SAXReader reader = new SAXReader();
Document document = reader.read(unzipFile);
if (!StandardCharsets.UTF_8.name().equals(document.getXMLEncoding())) {
throw new RuntimeException("The file encoding format is incorrect.");
}
Node droneNode = document.selectSingleNode("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_INFO);
Node payloadNode = document.selectSingleNode("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_INFO);
if (Objects.isNull(droneNode) || Objects.isNull(payloadNode)) {
throw new RuntimeException("The file format is incorrect.");
}
String type = droneNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_ENUM_VALUE);
String subType = droneNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_SUB_ENUM_VALUE);
String payloadType = payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_ENUM_VALUE);
String payloadSubType = payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_SUB_ENUM_VALUE);
String templateId = document.valueOf("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_TEMPLATE_ID);
if (!StringUtils.hasText(type) || !StringUtils.hasText(subType) ||
!StringUtils.hasText(payloadSubType) || !StringUtils.hasText(payloadType) ||
!StringUtils.hasText(templateId)) {
throw new RuntimeException("The file format is incorrect.");
}
return Optional.of(WaylineFileDTO.builder()
.droneModelKey(String.format("%s-%s-%s", DeviceDomainEnum.SUB_DEVICE.getVal(), type, subType))
.payloadModelKeys(List.of(String.format("%s-%s-%s",DeviceDomainEnum.PAYLOAD.getVal(), payloadType, payloadSubType)))
.objectKey(configuration.getObjectDirPrefix() + File.separator + filename)
.name(filename.substring(0, filename.lastIndexOf(WAYLINE_FILE_SUFFIX)))
.sign(DigestUtils.md5DigestAsHex(file.getInputStream()))
.templateTypes(List.of(Integer.parseInt(templateId)))
.build());
}
} catch (IOException | DocumentException e) {
e.printStackTrace();
}
return Optional.empty();
}
/** /**
* Convert database entity objects into wayline data transfer object. * Convert database entity objects into wayline data transfer object.
* @param entity * @param entity
@ -192,6 +280,7 @@ public class WaylineFileServiceImpl implements IWaylineFileService {
.map(String::valueOf) .map(String::valueOf)
.collect(Collectors.joining(","))) .collect(Collectors.joining(",")))
.favorited(file.getFavorited()) .favorited(file.getFavorited())
.sign(file.getSign())
.build(); .build();
} }

320
src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java

@ -3,40 +3,43 @@ package com.dji.sample.wayline.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dji.sample.common.error.CommonErrorEnum;
import com.dji.sample.common.model.CustomClaim; import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.Pagination; import com.dji.sample.common.model.Pagination;
import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.PaginationData;
import com.dji.sample.component.mqtt.model.CommonTopicResponse; import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.mqtt.model.ServiceReply; import com.dji.sample.component.mqtt.model.*;
import com.dji.sample.component.mqtt.model.ServicesMethodEnum;
import com.dji.sample.component.mqtt.model.TopicConst;
import com.dji.sample.component.mqtt.service.IMessageSenderService; import com.dji.sample.component.mqtt.service.IMessageSenderService;
import com.dji.sample.component.redis.RedisConst; import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.service.IDeviceService; import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.wayline.dao.IWaylineJobMapper; import com.dji.sample.wayline.dao.IWaylineJobMapper;
import com.dji.sample.wayline.model.dto.FlightTaskCreateDTO; import com.dji.sample.wayline.model.dto.*;
import com.dji.sample.wayline.model.dto.FlightTaskFileDTO;
import com.dji.sample.wayline.model.dto.WaylineFileDTO;
import com.dji.sample.wayline.model.dto.WaylineJobDTO;
import com.dji.sample.wayline.model.entity.WaylineJobEntity; import com.dji.sample.wayline.model.entity.WaylineJobEntity;
import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum;
import com.dji.sample.wayline.model.enums.WaylineTaskTypeEnum;
import com.dji.sample.wayline.model.param.CreateJobParam; import com.dji.sample.wayline.model.param.CreateJobParam;
import com.dji.sample.wayline.service.IWaylineFileService; import com.dji.sample.wayline.service.IWaylineFileService;
import com.dji.sample.wayline.service.IWaylineJobService; import com.dji.sample.wayline.service.IWaylineJobService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.net.URL; import java.net.URL;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.List; import java.util.*;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -64,10 +67,18 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
@Autowired @Autowired
private RedisOpsUtils redisOps; private RedisOpsUtils redisOps;
@Autowired
private ObjectMapper objectMapper;
@Override @Override
public Boolean createJob(CreateJobParam param, CustomClaim customClaim) throws SQLException { public Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, CustomClaim customClaim) {
if (param == null) { if (Objects.isNull(param)) {
return false; return Optional.empty();
}
// Immediate tasks, allocating time on the backend.
if (Objects.isNull(param.getExecuteTime())) {
param.setExecuteTime(System.currentTimeMillis());
} }
WaylineJobEntity jobEntity = WaylineJobEntity.builder() WaylineJobEntity jobEntity = WaylineJobEntity.builder()
.name(param.getName()) .name(param.getName())
@ -76,75 +87,199 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
.username(customClaim.getUsername()) .username(customClaim.getUsername())
.workspaceId(customClaim.getWorkspaceId()) .workspaceId(customClaim.getWorkspaceId())
.jobId(UUID.randomUUID().toString()) .jobId(UUID.randomUUID().toString())
.type(param.getType()) .executeTime(param.getExecuteTime())
.status(WaylineJobStatusEnum.PENDING.getVal())
.taskType(param.getTaskType())
.waylineType(param.getWaylineType())
.build(); .build();
int id = mapper.insert(jobEntity); int id = mapper.insert(jobEntity);
if (id <= 0) { if (id <= 0) {
return false; return Optional.empty();
} }
if (param.isImmediate()) { return Optional.ofNullable(this.entity2Dto(jobEntity));
publishFlightTask(jobEntity.getWorkspaceId(), jobEntity.getJobId());
}
return true;
} }
@Override @Override
public void publishFlightTask(String workspaceId, String jobId) throws SQLException { public ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException {
// get job Optional<WaylineJobDTO> waylineJobOpt = this.createWaylineJob(param, customClaim);
Optional<WaylineJobDTO> waylineJob = this.getJobByJobId(jobId); if (waylineJobOpt.isEmpty()) {
if (waylineJob.isEmpty()) { throw new SQLException("Failed to create wayline job.");
throw new IllegalArgumentException("Job doesn't exist.");
} }
WaylineJobDTO waylineJob = waylineJobOpt.get();
long expire = redisOps.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + waylineJob.get().getDockSn()); boolean isOnline = deviceService.checkDeviceOnline(waylineJob.getDockSn());
if (expire < 0) { if (!isOnline) {
throw new RuntimeException("Dock is offline."); throw new RuntimeException("Dock is offline.");
} }
// get wayline file // get wayline file
Optional<WaylineFileDTO> waylineFile = waylineFileService.getWaylineByWaylineId(workspaceId, waylineJob.get().getFileId()); Optional<WaylineFileDTO> waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId());
if (waylineFile.isEmpty()) { if (waylineFile.isEmpty()) {
throw new IllegalArgumentException("Wayline file doesn't exist."); throw new SQLException("Wayline file doesn't exist.");
} }
// get file url // get file url
URL url = waylineFileService.getObjectUrl(workspaceId, waylineFile.get().getWaylineId()); URL url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getWaylineId());
WaylineJobDTO job = waylineJob.get();
FlightTaskCreateDTO flightTask = FlightTaskCreateDTO.builder() FlightTaskCreateDTO flightTask = FlightTaskCreateDTO.builder()
.flightId(jobId) .flightId(waylineJob.getJobId())
.type(job.getType()) .executeTime(waylineJob.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())
.taskType(waylineJob.getTaskType())
.waylineType(waylineJob.getWaylineType())
.file(FlightTaskFileDTO.builder() .file(FlightTaskFileDTO.builder()
.url(url.toString()) .url(url.toString())
.sign(waylineFile.get().getSign()) .fingerprint(waylineFile.get().getSign())
.build()) .build())
.build(); .build();
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT +
waylineJob.getDockSn() + TopicConst.SERVICES_SUF;
CommonTopicResponse<Object> response = CommonTopicResponse.builder()
.tid(UUID.randomUUID().toString())
.bid(waylineJob.getJobId())
.timestamp(System.currentTimeMillis())
.data(flightTask)
.method(ServicesMethodEnum.FLIGHT_TASK_PREPARE.getMethod())
.build();
ServiceReply serviceReply = messageSender.publishWithReply(topic, response);
if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) {
log.info("Prepare task ====> Error code: {}", serviceReply.getResult());
this.updateJob(WaylineJobDTO.builder()
.workspaceId(waylineJob.getWorkspaceId())
.jobId(waylineJob.getJobId())
.status(WaylineJobStatusEnum.FAILED.getVal())
.endTime(LocalDateTime.now())
.code(serviceReply.getResult()).build());
return ResponseResult.error("Prepare task ====> Error code: " + serviceReply.getResult());
}
// Issue an immediate task execution command.
if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == waylineJob.getTaskType()) {
if (!executeFlightTask(waylineJob.getJobId())) {
return ResponseResult.error("Failed to execute job.");
}
}
if (WaylineTaskTypeEnum.TIMED.getVal() == waylineJob.getTaskType()) {
boolean isAdd = redisOps.zAdd(RedisConst.WAYLINE_JOB, waylineJob.getJobId(),
waylineJob.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
if (!isAdd) {
return ResponseResult.error("Failed to create scheduled job.");
}
}
return ResponseResult.success();
}
@Override
public Boolean executeFlightTask(String jobId) {
// get job
Optional<WaylineJobDTO> waylineJob = this.getJobByJobId(jobId);
if (waylineJob.isEmpty()) {
throw new IllegalArgumentException("Job doesn't exist.");
}
boolean isOnline = deviceService.checkDeviceOnline(waylineJob.get().getDockSn());
if (!isOnline) {
throw new RuntimeException("Dock is offline.");
}
WaylineJobDTO job = waylineJob.get();
FlightTaskCreateDTO flightTask = FlightTaskCreateDTO.builder().flightId(jobId).build();
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT +
job.getDockSn() + TopicConst.SERVICES_SUF; job.getDockSn() + TopicConst.SERVICES_SUF;
CommonTopicResponse<Object> response = CommonTopicResponse.builder() CommonTopicResponse<Object> response = CommonTopicResponse.builder()
.tid(UUID.randomUUID().toString()) .tid(UUID.randomUUID().toString())
.bid(UUID.randomUUID().toString()) .bid(jobId)
.timestamp(System.currentTimeMillis()) .timestamp(System.currentTimeMillis())
.data(flightTask) .data(flightTask)
.method(ServicesMethodEnum.FLIGHTTASK_CREATE.getMethod()) .method(ServicesMethodEnum.FLIGHT_TASK_EXECUTE.getMethod())
.build(); .build();
Optional<ServiceReply> serviceReplyOpt = messageSender.publishWithReply(topic, response); ServiceReply serviceReply = messageSender.publishWithReply(topic, response);
if (serviceReplyOpt.isEmpty()) { if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) {
log.info("Timeout to receive reply."); log.info("Execute job ====> Error code: {}", serviceReply.getResult());
throw new RuntimeException("Timeout to receive reply."); this.updateJob(WaylineJobDTO.builder()
.jobId(jobId)
.status(WaylineJobStatusEnum.FAILED.getVal())
.endTime(LocalDateTime.now())
.code(serviceReply.getResult()).build());
return false;
}
this.updateJob(WaylineJobDTO.builder()
.jobId(jobId)
.status(WaylineJobStatusEnum.IN_PROGRESS.getVal())
.build());
redisOps.setWithExpire(jobId,
EventsReceiver.<FlightTaskProgressReceiver>builder().bid(jobId).sn(job.getDockSn()).build(),
RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND);
return true;
}
@Override
public void cancelFlightTask(String workspaceId, Collection<String> jobIds) {
List<WaylineJobEntity> waylineJobs = mapper.selectList(
new LambdaQueryWrapper<WaylineJobEntity>()
.or(wrapper -> jobIds.forEach(id -> wrapper.eq(WaylineJobEntity::getJobId, id))));
// Check if the job have ended.
List<String> endJobs = waylineJobs.stream()
.filter(job -> WaylineJobStatusEnum.find(job.getStatus()).getEnd())
.map(WaylineJobEntity::getName)
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(endJobs)) {
throw new IllegalArgumentException("There are jobs that have ended." + Arrays.toString(endJobs.toArray()));
} }
if (serviceReplyOpt.get().getResult() != 0) {
log.info("Error code: {}", serviceReplyOpt.get().getResult()); Set<String> ids = waylineJobs.stream().map(WaylineJobEntity::getJobId).collect(Collectors.toSet());
throw new RuntimeException("Error code: " + serviceReplyOpt.get().getResult()); for (String id : jobIds) {
if (!ids.contains(id)) {
throw new IllegalArgumentException("Job id " + id + " doesn't exist.");
} }
}
// Group job id by dock sn.
Map<String, List<String>> dockJobs = waylineJobs.stream()
.collect(Collectors.groupingBy(WaylineJobEntity::getDockSn,
Collectors.mapping(WaylineJobEntity::getJobId, Collectors.toList())));
dockJobs.forEach((dockSn, idList) -> this.publishCancelTask(workspaceId, dockSn, idList));
job.setBid(response.getBid());
boolean isUpd = this.updateJob(job);
if (!isUpd) {
throw new SQLException("Failed to update data.");
} }
private void publishCancelTask(String workspaceId, String dockSn, List<String> jobIds) {
boolean isOnline = deviceService.checkDeviceOnline(dockSn);
if (isOnline) {
throw new RuntimeException("Dock is offline.");
}
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + dockSn + TopicConst.SERVICES_SUF;
CommonTopicResponse<Object> response = CommonTopicResponse.builder()
.tid(UUID.randomUUID().toString())
.bid(UUID.randomUUID().toString())
.timestamp(System.currentTimeMillis())
.data(Map.of(MapKeyConst.FLIGHT_IDS, jobIds))
.method(ServicesMethodEnum.FLIGHT_TASK_CANCEL.getMethod())
.build();
ServiceReply serviceReply = messageSender.publishWithReply(topic, response);
if (serviceReply.getResult() != 0) {
log.info("Cancel job ====> Error code: {}", serviceReply.getResult());
throw new RuntimeException("Failed to cancel the wayline job of " + dockSn);
}
for (String jobId : jobIds) {
this.updateJob(WaylineJobDTO.builder()
.workspaceId(workspaceId)
.jobId(jobId)
.status(WaylineJobStatusEnum.CANCEL.getVal())
.endTime(LocalDateTime.now())
.build());
redisOps.zRemove(RedisConst.WAYLINE_JOB, jobId);
}
} }
@Override @Override
@ -159,9 +294,7 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
public Boolean updateJob(WaylineJobDTO dto) { public Boolean updateJob(WaylineJobDTO dto) {
return mapper.update(this.dto2Entity(dto), return mapper.update(this.dto2Entity(dto),
new LambdaUpdateWrapper<WaylineJobEntity>() new LambdaUpdateWrapper<WaylineJobEntity>()
.eq(WaylineJobEntity::getWorkspaceId, dto.getWorkspaceId()) .eq(WaylineJobEntity::getJobId, dto.getJobId())) > 0;
.eq(WaylineJobEntity::getJobId, dto.getJobId()))
> 0;
} }
@Override @Override
@ -178,14 +311,75 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
return new PaginationData<WaylineJobDTO>(records, new Pagination(pageData)); return new PaginationData<WaylineJobDTO>(records, new Pagination(pageData));
} }
@Override
@ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET, outputChannel = ChannelName.OUTBOUND)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void flightTaskResourceGet(CommonTopicReceiver receiver, MessageHeaders headers) {
Map<String, String> jobIdMap = objectMapper.convertValue(receiver.getData(), new TypeReference<Map<String, String>>() {});
String jobId = jobIdMap.get(MapKeyConst.FLIGHT_ID);
CommonTopicResponse.CommonTopicResponseBuilder<RequestsReply> builder = CommonTopicResponse.<RequestsReply>builder()
.tid(receiver.getTid())
.bid(receiver.getBid())
.method(RequestsMethodEnum.FLIGHT_TASK_RESOURCE_GET.getMethod())
.timestamp(System.currentTimeMillis());
String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString() + TopicConst._REPLY_SUF;
Optional<WaylineJobDTO> waylineJobOpt = this.getJobByJobId(jobId);
if (waylineJobOpt.isEmpty()) {
builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT));
messageSender.publish(topic, builder.build());
return;
}
WaylineJobDTO waylineJob = waylineJobOpt.get();
// get wayline file
Optional<WaylineFileDTO> waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId());
if (waylineFile.isEmpty()) {
builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT));
messageSender.publish(topic, builder.build());
return;
}
// get file url
URL url = null;
try {
url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getWaylineId());
builder.data(RequestsReply.success(FlightTaskCreateDTO.builder()
.file(FlightTaskFileDTO.builder()
.url(url.toString())
.fingerprint(waylineFile.get().getSign())
.build())
.build()));
} catch (SQLException | NullPointerException e) {
e.printStackTrace();
builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT));
messageSender.publish(topic, builder.build());
return;
}
messageSender.publish(topic, builder.build());
}
private WaylineJobEntity dto2Entity(WaylineJobDTO dto) { private WaylineJobEntity dto2Entity(WaylineJobDTO dto) {
WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder(); WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder();
if (dto == null) { if (dto == null) {
return builder.build(); return builder.build();
} }
return builder.type(dto.getType()) if (Objects.nonNull(dto.getEndTime())) {
.bid(dto.getBid()) builder.endTime(dto.getEndTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
}
if (Objects.nonNull(dto.getExecuteTime())) {
builder.executeTime(dto.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
}
return builder.status(dto.getStatus())
.name(dto.getJobName()) .name(dto.getJobName())
.errorCode(dto.getCode())
.build(); .build();
} }
@ -193,10 +387,9 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
if (entity == null) { if (entity == null) {
return null; return null;
} }
return WaylineJobDTO.builder()
WaylineJobDTO.WaylineJobDTOBuilder builder = WaylineJobDTO.builder()
.jobId(entity.getJobId()) .jobId(entity.getJobId())
.bid(entity.getBid())
.updateTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getUpdateTime()), ZoneId.systemDefault()))
.jobName(entity.getName()) .jobName(entity.getName())
.fileId(entity.getFileId()) .fileId(entity.getFileId())
.fileName(waylineFileService.getWaylineByWaylineId(entity.getWorkspaceId(), entity.getFileId()) .fileName(waylineFileService.getWaylineByWaylineId(entity.getWorkspaceId(), entity.getFileId())
@ -206,7 +399,20 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
.orElse(DeviceDTO.builder().build()).getNickname()) .orElse(DeviceDTO.builder().build()).getNickname())
.username(entity.getUsername()) .username(entity.getUsername())
.workspaceId(entity.getWorkspaceId()) .workspaceId(entity.getWorkspaceId())
.type(entity.getType()) .status(entity.getStatus())
.build(); .code(entity.getErrorCode())
.executeTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getExecuteTime()), ZoneId.systemDefault()))
.taskType(entity.getTaskType())
.waylineType(entity.getWaylineType());
if (Objects.nonNull(entity.getEndTime())) {
builder.endTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getEndTime()), ZoneId.systemDefault()));
}
if (WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && redisOps.getExpire(entity.getJobId()) > 0) {
EventsReceiver<FlightTaskProgressReceiver> taskProgress = (EventsReceiver<FlightTaskProgressReceiver>) redisOps.get(entity.getJobId());
if (Objects.nonNull(taskProgress.getOutput()) && Objects.nonNull(taskProgress.getOutput().getProgress())) {
builder.progress(taskProgress.getOutput().getProgress().getPercent());
}
}
return builder.build();
} }
} }

Loading…
Cancel
Save