70 files modified
35 files added
2 files deleted
| | |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Switch Lens", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"video_id\": \"1581F5BMD228Q00A82XX/39-0-7/zoom-0\",\r\n \"video_type\": \"zoom\"\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{manage_version}}/live/streams/switch", |
| | | "host": [ |
| | | "{{base_url}}{{manage_version}}" |
| | | ], |
| | | "path": [ |
| | | "live", |
| | | "streams", |
| | | "switch" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Get All Users Info", |
| | | "request": { |
| | | "method": "GET", |
| | |
| | | } |
| | | }, |
| | | "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": { |
| | |
| | | "apikey": [ |
| | | { |
| | | "key": "value", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NjM1NTkxMTAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NjM2NDU1MTAsImlhdCI6MTY2MzU1OTExMCwidXNlcm5hbWUiOiJhZG1pblBDIn0.LG1JXZkuTdMaqnXn5WMJvnysNkHHbc4HLe_qZPWz_nM", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njc0NzcwNTUsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njc1NjM0NTUsImlhdCI6MTY2NzQ3NzA1NSwidXNlcm5hbWUiOiJhZG1pblBDIn0.VMJ0ZKn895uvkMDjg2fw3p4trVCUx9ltVFKeP7QmYpo", |
| | | "type": "string" |
| | | }, |
| | | { |
| | |
| | | } |
| | | }, |
| | | "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": { |
| | |
| | | "apikey": [ |
| | | { |
| | | "key": "value", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTU0NDk2MDIsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTU1MzYwMDIsImlhdCI6MTY1NTQ0OTYwMiwidXNlcm5hbWUiOiJhZG1pblBDIn0.YZWHJ65Pl_DT2Ampxk0WC01KD_fNTm_rYVUBIHAZD-4", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg2NTk4MjYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg3NDYyMjYsImlhdCI6MTY2ODY1OTgyNiwidXNlcm5hbWUiOiJhZG1pblBDIn0.ykCpfJcReeb3etUzmNMQk1n0vaoDT6dl47J_aHRoTbU", |
| | | "type": "string" |
| | | }, |
| | | { |
| | |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"name\": \"\",\r\n \"file_id\": \"\",\r\n \"dock_sn\": \"\",\r\n \"type\": \"\",\r\n \"immediate\": false\r\n}", |
| | | "raw": "{\r\n \"name\": \"\",\r\n \"file_id\": \"\",\r\n \"dock_sn\": \"\",\r\n \"wayline_type\": 0,\r\n \"task_type\": 0,\r\n \"execute_time\": 123456789123,\r\n \"rth_altitude\": 20,\r\n \"out_of_control_action\": 1\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Execute Job", |
| | | "name": "Cancel the jobs", |
| | | "request": { |
| | | "method": "POST", |
| | | "method": "DELETE", |
| | | "header": [], |
| | | "url": { |
| | | "raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/jobs/{{plan_id}}", |
| | | "raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/jobs", |
| | | "host": [ |
| | | "{{base_url}}{{wayline_version}}" |
| | | ], |
| | | "path": [ |
| | | "workspaces", |
| | | "{{workspace_id}}", |
| | | "jobs", |
| | | "{{plan_id}}" |
| | | "jobs" |
| | | ], |
| | | "query": [ |
| | | { |
| | | "key": "job_id", |
| | | "value": null, |
| | | "disabled": true |
| | | }, |
| | | { |
| | | "key": "job_id", |
| | | "value": null, |
| | | "disabled": true |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | |
| | | "apikey": [ |
| | | { |
| | | "key": "value", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTU4OTA5NTQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTU5NzczNTQsImlhdCI6MTY1NTg5MDk1NCwidXNlcm5hbWUiOiJhZG1pblBDIn0.fd0iIzCd71LDUE6ixexUJvo-YqtnSCqRx-790snCyBI", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg2NTk4MjYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg3NDYyMjYsImlhdCI6MTY2ODY1OTgyNiwidXNlcm5hbWUiOiJhZG1pblBDIn0.ykCpfJcReeb3etUzmNMQk1n0vaoDT6dl47J_aHRoTbU", |
| | | "type": "string" |
| | | }, |
| | | { |
| | |
| | | "apikey": [ |
| | | { |
| | | "key": "value", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NjE5NTQwMTQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NjIwNDA0MTQsImlhdCI6MTY2MTk1NDAxNCwidXNlcm5hbWUiOiJhZG1pblBDIn0.GgCh575h2-HvYvdGZIKBW50r0F6CPACQn4ceAVzJfCU", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg0MzE5MzQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg1MTgzMzQsImlhdCI6MTY2ODQzMTkzNCwidXNlcm5hbWUiOiJhZG1pblBDIn0.QU9xHBeQPHJ2V1vXQcGGWRQ-gYEOWDpaTTXIQga85BU", |
| | | "type": "string" |
| | | }, |
| | | { |
| | |
| | | |
| | | <groupId>com.dji</groupId> |
| | | <artifactId>cloud-api-sample</artifactId> |
| | | <version>1.1.0</version> |
| | | <version>1.3.0</version> |
| | | <name>cloud-api-sample</name> |
| | | |
| | | <properties> |
| | |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-aop</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>org.dom4j</groupId> |
| | | <artifactId>dom4j</artifactId> |
| | | <version>2.1.3</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>jaxen</groupId> |
| | | <artifactId>jaxen</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>org.bouncycastle</groupId> |
| | | <artifactId>bcpkix-jdk15on</artifactId> |
| | | <version>1.69</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-validation</artifactId> |
| | | </dependency> |
| | | |
| | | |
| | | </dependencies> |
| | | |
| | | <build> |
| | |
| | | (15,1,90742,0,'L1',NULL), |
| | | (16,2,56,0,'DJI Smart Controller','Remote control for M300'), |
| | | (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 */; |
| | | UNLOCK TABLES; |
| | |
| | | `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.', |
| | | `bid` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The bid used to execute the job, and the subsequent progress of the job is reported using this bid.', |
| | | `type` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The type of the job. Available: wayline.', |
| | | `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', |
| | | `rth_altitude` int NOT NULL COMMENT 'return to home altitude. min: 20m; max: 500m', |
| | | `out_of_control` int NOT NULL COMMENT 'out of control action. 0: go home; 1: hover; 2: landing;', |
| | | `media_count` int NOT NULL DEFAULT '0', |
| | | `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 */; |
| | |
| | | * @return enumeration object |
| | | */ |
| | | public static LiveErrorEnum find(int code) { |
| | | |
| | | final int MOD = 100_000; |
| | | for (LiveErrorEnum errorEnum : LiveErrorEnum.class.getEnumConstants()) { |
| | | if (errorEnum.code == code) { |
| | | if (errorEnum.code % MOD == code % MOD) { |
| | | return errorEnum; |
| | | } |
| | | } |
| | |
| | | |
| | | import com.auth0.jwt.JWT; |
| | | import com.auth0.jwt.JWTCreator; |
| | | import com.auth0.jwt.JWTVerifier; |
| | | import com.auth0.jwt.algorithms.Algorithm; |
| | | import com.auth0.jwt.exceptions.TokenExpiredException; |
| | | import com.auth0.jwt.interfaces.DecodedJWT; |
| | |
| | | * @throws TokenExpiredException |
| | | */ |
| | | public static DecodedJWT verifyToken(String token) { |
| | | try { |
| | | JWTVerifier verifier = JWT.require(algorithm).build(); |
| | | return verifier.verify(token); |
| | | } catch (Exception e) { |
| | | log.error(e.getMessage()); |
| | | e.printStackTrace(); |
| | | return null; |
| | | } |
| | | return JWT.require(algorithm).build().verify(token); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return custom claim |
| | | */ |
| | | public static Optional<CustomClaim> parseToken(String token) { |
| | | DecodedJWT jwt = verifyToken(token); |
| | | return jwt == null ? Optional.empty() : Optional.of(new CustomClaim(jwt.getClaims())); |
| | | DecodedJWT jwt; |
| | | try { |
| | | jwt = verifyToken(token); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return Optional.empty(); |
| | | } |
| | | return Optional.of(new CustomClaim(jwt.getClaims())); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.common.util; |
| | | |
| | | import org.springframework.beans.BeansException; |
| | | import org.springframework.context.ApplicationContext; |
| | | import org.springframework.context.ApplicationContextAware; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/10 |
| | | */ |
| | | @Component |
| | | public class SpringBeanUtils implements ApplicationContextAware { |
| | | |
| | | private static ApplicationContext applicationContext; |
| | | |
| | | @Override |
| | | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
| | | SpringBeanUtils.applicationContext = applicationContext; |
| | | } |
| | | |
| | | public static <T> T getBean(Class<T> clazz) { |
| | | return applicationContext.getBean(clazz); |
| | | } |
| | | |
| | | public static Object getBean(String beanName) { |
| | | return applicationContext.getBean(beanName); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.component; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import org.springframework.web.bind.MethodArgumentNotValidException; |
| | | import org.springframework.web.bind.annotation.ControllerAdvice; |
| | | import org.springframework.web.bind.annotation.ExceptionHandler; |
| | | import org.springframework.web.bind.annotation.ResponseBody; |
| | |
| | | e.printStackTrace(); |
| | | return ResponseResult.error("A null object appeared."); |
| | | } |
| | | |
| | | @ExceptionHandler(MethodArgumentNotValidException.class) |
| | | public ResponseResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { |
| | | return ResponseResult.error(e.getBindingResult().getAllErrors().get(0).getDefaultMessage()); |
| | | } |
| | | |
| | | } |
| | |
| | | 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(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_REQUESTS_CONFIG) |
| | | public MessageChannel requestsConfig() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | } |
| | |
| | | package com.dji.sample.component.mqtt.handler; |
| | | |
| | | import com.dji.sample.common.util.SpringBeanUtils; |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.DeviceTopicEnum; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.integration.annotation.Router; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.regex.Pattern; |
| | | |
| | | import static com.dji.sample.component.mqtt.model.TopicConst.*; |
| | | |
| | | /** |
| | | * |
| | |
| | | @Component |
| | | @Slf4j |
| | | public class InboundMessageRouter extends AbstractMessageRouter { |
| | | |
| | | @Resource(name = ChannelName.INBOUND) |
| | | private MessageChannel inboundChannel; |
| | | |
| | | @Resource(name = ChannelName.INBOUND_STATUS) |
| | | private MessageChannel statusChannel; |
| | | |
| | | @Resource(name = ChannelName.INBOUND_STATE) |
| | | private MessageChannel stateChannel; |
| | | |
| | | @Resource(name = ChannelName.DEFAULT) |
| | | private MessageChannel defaultChannel; |
| | | |
| | | @Resource(name = ChannelName.INBOUND_SERVICE_REPLY) |
| | | private MessageChannel serviceReplyChannel; |
| | | |
| | | @Resource(name = ChannelName.INBOUND_OSD) |
| | | private MessageChannel osdChannel; |
| | | |
| | | @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 = |
| | | 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. |
| | |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); |
| | | byte[] payload = (byte[])message.getPayload(); |
| | | |
| | | // osd |
| | | if (PATTERN_TOPIC_OSD.matcher(topic).matches()) { |
| | | return Collections.singleton(osdChannel); |
| | | } |
| | | |
| | | log.debug("received topic :{} \t payload :{}", topic, new String(payload)); |
| | | |
| | | // status |
| | | if (PATTERN_TOPIC_STATUS.matcher(topic).matches()) { |
| | | return Collections.singleton(statusChannel); |
| | | } |
| | | DeviceTopicEnum topicEnum = DeviceTopicEnum.find(topic); |
| | | MessageChannel bean = (MessageChannel) SpringBeanUtils.getBean(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); |
| | | return Collections.singleton(bean); |
| | | } |
| | | } |
| New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | import org.springframework.integration.dsl.IntegrationFlows; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | }) |
| | | .<CommonTopicReceiver, RequestsMethodEnum>route( |
| | | receiver -> RequestsMethodEnum.find(receiver.getMethod()), |
| | | mapping -> { |
| | | mapping.channelMapping(RequestsMethodEnum.STORAGE_CONFIG_GET, ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET); |
| | | 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_BIND, ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND); |
| | | mapping.channelMapping(RequestsMethodEnum.UNKNOWN, ChannelName.DEFAULT); |
| | | }) |
| | | mapping -> Arrays.stream(RequestsMethodEnum.values()).forEach( |
| | | methodEnum -> mapping.channelMapping(methodEnum, methodEnum.getChannelName()))) |
| | | .get(); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.component.mqtt.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | 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.dji.sample.component.mqtt.model.ServiceReply; |
| | | import com.dji.sample.manage.model.enums.LogsFileMethodEnum; |
| | | import com.dji.sample.manage.model.receiver.LogsFileUploadList; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | |
| | | byte[] payload = (byte[])message.getPayload(); |
| | | |
| | | CommonTopicReceiver receiver = mapper.readValue(payload, new TypeReference<CommonTopicReceiver>() {}); |
| | | if (ServicesMethodEnum.FILE_UPLOAD_LIST.getMethod().equals(receiver.getMethod())) { |
| | | if (LogsFileMethodEnum.FILE_UPLOAD_LIST.getMethod().equals(receiver.getMethod())) { |
| | | LogsFileUploadList list = mapper.convertValue(receiver.getData(), new TypeReference<LogsFileUploadList>() {}); |
| | | receiver.setData(list); |
| | | } else { |
| | |
| | | public static final String INBOUND_EVENTS_OTA_PROGRESS = "inboundEventsOtaProgress"; |
| | | |
| | | 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"; |
| | | |
| | | public static final String INBOUND_REQUESTS_CONFIG = "inboundRequestsConfig"; |
| | | } |
| | |
| | | |
| | | private Integer needReply; |
| | | |
| | | private String from; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | import com.dji.sample.manage.service.IRequestsConfigService; |
| | | import com.dji.sample.manage.service.impl.ConfigProductServiceImpl; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/10 |
| | | */ |
| | | @Getter |
| | | public enum ConfigScopeEnum { |
| | | |
| | | PRODUCT("product", ConfigProductServiceImpl.class); |
| | | |
| | | String scope; |
| | | |
| | | Class<? extends IRequestsConfigService> clazz; |
| | | |
| | | ConfigScopeEnum(String scope, Class<? extends IRequestsConfigService> clazz) { |
| | | this.scope = scope; |
| | | this.clazz = clazz; |
| | | } |
| | | |
| | | public static Optional<ConfigScopeEnum> find(String scope) { |
| | | return Arrays.stream(ConfigScopeEnum.values()).filter(scopeEnum -> scopeEnum.scope.equals(scope)).findAny(); |
| | | } |
| | | } |
| New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | */ |
| | | @Data |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class EventsReceiver<T> { |
| | | |
| | | private Integer result; |
| | |
| | | |
| | | TIMEOUT("timeout", true), |
| | | |
| | | PARTIALLY_DONE("partially_done", true), |
| | | |
| | | UNKNOWN("unknown", false); |
| | | |
| | | String desc; |
| | |
| | | public static final String LIST = "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"; |
| | | |
| | | } |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | |
| | | * @version 1.0 |
| | | * @date 2022/5/25 |
| | | */ |
| | | @Getter |
| | | public enum RequestsMethodEnum { |
| | | |
| | | STORAGE_CONFIG_GET("storage_config_get"), |
| | | STORAGE_CONFIG_GET("storage_config_get", ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET), |
| | | |
| | | AIRPORT_BIND_STATUS("airport_bind_status"), |
| | | AIRPORT_BIND_STATUS("airport_bind_status", ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS), |
| | | |
| | | AIRPORT_ORGANIZATION_BIND("airport_organization_bind"), |
| | | AIRPORT_ORGANIZATION_BIND("airport_organization_bind", ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND), |
| | | |
| | | AIRPORT_ORGANIZATION_GET("airport_organization_get"), |
| | | AIRPORT_ORGANIZATION_GET("airport_organization_get", ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET), |
| | | |
| | | UNKNOWN("Unknown"); |
| | | FLIGHT_TASK_RESOURCE_GET("flighttask_resource_get", ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET), |
| | | |
| | | CONFIG("config", ChannelName.INBOUND_REQUESTS_CONFIG), |
| | | |
| | | UNKNOWN("Unknown", ChannelName.DEFAULT); |
| | | |
| | | private String method; |
| | | |
| | | RequestsMethodEnum(String method) { |
| | | this.method = method; |
| | | } |
| | | private String channelName; |
| | | |
| | | public String getMethod() { |
| | | return method; |
| | | RequestsMethodEnum(String method, String channelName) { |
| | | this.method = method; |
| | | this.channelName = channelName; |
| | | } |
| | | |
| | | public static RequestsMethodEnum find(String method) { |
| New file |
| | |
| | | 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; |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | 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]+"; |
| | | |
| | | } |
| | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.model.ServiceReply; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @version 0.1 |
| | |
| | | * @param response notification of whether the start is successful. |
| | | * @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. |
| | |
| | | * @param <T> |
| | | * @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); |
| | | } |
| | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Optional; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | /** |
| | |
| | | } |
| | | } |
| | | |
| | | public Optional<ServiceReply> publishWithReply(String topic, CommonTopicResponse response) { |
| | | public ServiceReply publishWithReply(String topic, CommonTopicResponse response) { |
| | | 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); |
| | | // Retry three times |
| | | while (time.getAndIncrement() < retryTime) { |
| | | while (time.getAndIncrement() <= retryTime) { |
| | | this.publish(topic, response); |
| | | |
| | | Chan<CommonTopicReceiver<T>> chan = Chan.getInstance(); |
| | | // 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) { |
| | | continue; |
| | | } |
| | | // Need to match tid and bid. |
| | | if (receiver.getTid().equals(response.getTid()) && |
| | | receiver.getBid().equals(response.getBid())) { |
| | | return Optional.ofNullable(receiver.getData()); |
| | | return receiver.getData(); |
| | | } |
| | | } |
| | | return Optional.empty(); |
| | | throw new RuntimeException("No message reply received."); |
| | | } |
| | | } |
| | |
| | | |
| | | import com.dji.sample.media.model.CredentialsDTO; |
| | | |
| | | import java.io.InputStream; |
| | | import java.net.URL; |
| | | |
| | | /** |
| | |
| | | * @param objectKey |
| | | * @return |
| | | */ |
| | | byte[] getObject(String bucket, String objectKey); |
| | | InputStream getObject(String bucket, String objectKey); |
| | | |
| | | void putObject(String bucket, String objectKey, InputStream input); |
| | | } |
| | |
| | | import com.aliyun.oss.OSSClientBuilder; |
| | | import com.aliyun.oss.OSSException; |
| | | 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.IAcsClient; |
| | | import com.aliyuncs.exceptions.ClientException; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public byte[] getObject(String bucket, String objectKey) { |
| | | public InputStream getObject(String bucket, String objectKey) { |
| | | OSS ossClient = this.createClient(); |
| | | OSSObject object = ossClient.getObject(bucket, objectKey); |
| | | |
| | | try (InputStream stream = object.getObjectContent()) { |
| | | return stream.readAllBytes(); |
| | | try (InputStream input = object.getObjectContent()) { |
| | | return input; |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } finally { |
| | | 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() { |
| | |
| | | import com.amazonaws.auth.BasicAWSCredentials; |
| | | import com.amazonaws.services.s3.AmazonS3; |
| | | import com.amazonaws.services.s3.AmazonS3ClientBuilder; |
| | | import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration; |
| | | import com.amazonaws.services.s3.model.CORSRule; |
| | | import com.amazonaws.services.s3.model.S3Object; |
| | | import com.amazonaws.services.s3.model.*; |
| | | import com.amazonaws.services.securitytoken.AWSSecurityTokenService; |
| | | import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; |
| | | import com.amazonaws.services.securitytoken.model.AssumeRoleRequest; |
| | |
| | | import com.dji.sample.component.oss.model.enums.OssTypeEnum; |
| | | import com.dji.sample.component.oss.service.IOssService; |
| | | import com.dji.sample.media.model.CredentialsDTO; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | |
| | | * @version 1.0 |
| | | * @date 2022/4/27 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class AmazonS3ServiceImpl implements IOssService { |
| | | |
| | |
| | | return true; |
| | | } |
| | | |
| | | public byte[] getObject(String bucket, String objectKey) { |
| | | public InputStream getObject(String bucket, String objectKey) { |
| | | AmazonS3 client = this.createClient(); |
| | | S3Object object = client.getObject(bucket, objectKey); |
| | | |
| | | try (InputStream stream = object.getObjectContent().getDelegateStream()) { |
| | | return stream.readAllBytes(); |
| | | try (InputStream input = object.getObjectContent().getDelegateStream()) { |
| | | return input; |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } finally { |
| | | 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() { |
| | |
| | | import com.dji.sample.component.oss.model.enums.OssTypeEnum; |
| | | import com.dji.sample.component.oss.service.IOssService; |
| | | import com.dji.sample.media.model.CredentialsDTO; |
| | | import io.minio.GetObjectArgs; |
| | | import io.minio.GetPresignedObjectUrlArgs; |
| | | import io.minio.MinioClient; |
| | | import io.minio.RemoveObjectArgs; |
| | | import io.minio.*; |
| | | import io.minio.credentials.AssumeRoleProvider; |
| | | import io.minio.errors.*; |
| | | import io.minio.http.Method; |
| | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.net.URL; |
| | | import java.security.InvalidKeyException; |
| | | import java.security.NoSuchAlgorithmException; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | @Service |
| | | @Slf4j |
| | | public class MinIOServiceImpl implements IOssService { |
| | | |
| | | private MinioClient client; |
| | | |
| | | @Autowired |
| | | private OssConfiguration configuration; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public byte[] getObject(String bucket, String objectKey) { |
| | | MinioClient client = this.createClient(); |
| | | try (InputStream objectResponse = client.getObject(GetObjectArgs.builder().bucket(bucket).object(objectKey).build())) { |
| | | return objectResponse.readAllBytes(); |
| | | } catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) { |
| | | 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 new byte[0]; |
| | | return InputStream.nullInputStream(); |
| | | } |
| | | |
| | | @Override |
| | | public void putObject(String bucket, String objectKey, InputStream input) { |
| | | try { |
| | | MinioClient client = this.createClient(); |
| | | client.statObject(StatObjectArgs.builder().bucket(bucket).object(objectKey).build()); |
| | | throw new RuntimeException("The filename already exists."); |
| | | } catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) { |
| | | 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(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private MinioClient createClient() { |
| | | return MinioClient.builder() |
| | | if (Objects.nonNull(this.client)) { |
| | | return this.client; |
| | | } |
| | | this.client = MinioClient.builder() |
| | | .endpoint(configuration.getEndpoint()) |
| | | .credentials(configuration.getAccessKey(), configuration.getSecretKey()) |
| | | .region(configuration.getRegion()) |
| | | .build(); |
| | | return this.client; |
| | | } |
| | | } |
| | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.io.InputStream; |
| | | import java.net.URL; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | |
| | | 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); |
| | | } |
| | | |
| | | public void putObject(String bucket, String objectKey, InputStream stream) { |
| | | this.ossService.putObject(bucket, objectKey, stream); |
| | | } |
| | | } |
| | |
| | | public static final String STATE_PAYLOAD_PREFIX = "payload" + 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; |
| | | } |
| | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.List; |
| | | import java.util.Set; |
| | |
| | | public Long listLen(String 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); |
| | | } |
| | | |
| | | } |
| | |
| | | objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS); |
| | | objectMapper.registerModules(timeModule); |
| | | 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(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); |
| | | objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); |
| | |
| | | package com.dji.sample.control.controller; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.control.model.param.RemoteDebugParam; |
| | | import com.dji.sample.control.service.IControlService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | |
| | | @PostMapping("/{sn}/jobs/{service_identifier}") |
| | | public ResponseResult createControlJob(@PathVariable String sn, |
| | | @PathVariable("service_identifier") String serviceIdentifier) { |
| | | return controlService.controlDock(sn, serviceIdentifier); |
| | | @PathVariable("service_identifier") String serviceIdentifier, |
| | | @RequestBody(required = false) RemoteDebugParam param) { |
| | | return controlService.controlDock(sn, serviceIdentifier, param); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.control.model.enums.BatteryStoreModeEnum; |
| | | import com.dji.sample.manage.model.receiver.BasicDeviceProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/14 |
| | | */ |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class BatteryStoreMode extends BasicDeviceProperty { |
| | | |
| | | private Integer value; |
| | | |
| | | @Override |
| | | public boolean valid() { |
| | | return Objects.nonNull(value) && BatteryStoreModeEnum.find(value).isPresent(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/14 |
| | | */ |
| | | @Getter |
| | | public enum BatteryStoreModeEnum { |
| | | |
| | | PLAN(1), |
| | | |
| | | EMERGENCY(2); |
| | | |
| | | Integer mode; |
| | | |
| | | BatteryStoreModeEnum(Integer mode) { |
| | | this.mode = mode; |
| | | } |
| | | |
| | | public static Optional<BatteryStoreModeEnum> find(int mode) { |
| | | return Arrays.stream(BatteryStoreModeEnum.values()).filter(modeEnum -> modeEnum.mode == mode).findAny(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.dji.sample.control.model.dto.BatteryStoreMode; |
| | | import com.dji.sample.manage.model.enums.StateSwitchReceiver; |
| | | import com.dji.sample.manage.model.receiver.BasicDeviceProperty; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/14 |
| | | */ |
| | | @Getter |
| | | public enum RemoteControlMethodEnum { |
| | | |
| | | DEBUG_MODE_OPEN("debug_mode_open", false, null), |
| | | |
| | | DEBUG_MODE_CLOSE("debug_mode_close", false, null), |
| | | |
| | | SUPPLEMENT_LIGHT_OPEN("supplement_light_open", false, null), |
| | | |
| | | SUPPLEMENT_LIGHT_CLOSE("supplement_light_close", false, null), |
| | | |
| | | RETURN_HOME("return_home", false, null), |
| | | |
| | | SDR_WORKMODE_SWITCH("sdr_workmode_switch", false, null), |
| | | |
| | | DEVICE_REBOOT("device_reboot", true, null), |
| | | |
| | | DRONE_OPEN("drone_open", true, null), |
| | | |
| | | DRONE_CLOSE("drone_close", true, null), |
| | | |
| | | DEVICE_CHECK("device_check", true, null), |
| | | |
| | | DRONE_FORMAT("drone_format", true, null), |
| | | |
| | | DEVICE_FORMAT("device_format", true, null), |
| | | |
| | | COVER_OPEN("cover_open", true, null), |
| | | |
| | | COVER_CLOSE("cover_close", true, null), |
| | | |
| | | PUTTER_OPEN("putter_open", true, null), |
| | | |
| | | PUTTER_CLOSE("putter_close", true, null), |
| | | |
| | | CHARGE_OPEN("charge_open", true, null), |
| | | |
| | | CHARGE_CLOSE("charge_close", true, null), |
| | | |
| | | BATTERY_MAINTENANCE_SWITCH("battery_maintenance_switch", true, StateSwitchReceiver.class), |
| | | |
| | | ALARM_STATE_SWITCH("alarm_state_switch", true, StateSwitchReceiver.class), |
| | | |
| | | BATTERY_STORE_MODE_SWITCH("battery_store_mode_switch", true, BatteryStoreMode.class), |
| | | |
| | | UNKNOWN("unknown", false, null); |
| | | |
| | | private String method; |
| | | |
| | | private Boolean progress; |
| | | |
| | | private Class<? extends BasicDeviceProperty> clazz; |
| | | |
| | | RemoteControlMethodEnum(String method, Boolean progress, Class<? extends BasicDeviceProperty> clazz) { |
| | | this.method = method; |
| | | this.progress = progress; |
| | | this.clazz = clazz; |
| | | } |
| | | |
| | | public static RemoteControlMethodEnum find(String method) { |
| | | return Arrays.stream(RemoteControlMethodEnum.values()) |
| | | .filter(methodEnum -> methodEnum.method.equals(method)) |
| | | .findAny() |
| | | .orElse(UNKNOWN); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.param; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/14 |
| | | */ |
| | | @Data |
| | | public class RemoteDebugParam { |
| | | |
| | | private Integer action; |
| | | |
| | | } |
| | |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.control.model.param.RemoteDebugParam; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | |
| | | /** |
| | |
| | | */ |
| | | public interface IControlService { |
| | | |
| | | ResponseResult controlDock(String sn, String serviceIdentifier); |
| | | /** |
| | | * Remotely debug the dock via commands. |
| | | * @param sn |
| | | * @param serviceIdentifier |
| | | * @param param |
| | | * @return |
| | | */ |
| | | ResponseResult controlDock(String sn, String serviceIdentifier, RemoteDebugParam param); |
| | | |
| | | /** |
| | | * Handles multi-state command progress information. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | void handleControlProgress(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | |
| | | } |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.common.error.CommonErrorEnum; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.control.model.enums.RemoteControlMethodEnum; |
| | | import com.dji.sample.control.model.param.RemoteDebugParam; |
| | | import com.dji.sample.control.service.IControlService; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.receiver.BasicDeviceProperty; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Optional; |
| | | import java.util.Objects; |
| | | import java.util.UUID; |
| | | |
| | | /** |
| | |
| | | private IWebSocketManageService webSocketManageService; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Autowired |
| | | private ObjectMapper mapper; |
| | | |
| | | @Override |
| | | public ResponseResult controlDock(String sn, String serviceIdentifier) { |
| | | ServicesMethodEnum servicesMethodEnum = ServicesMethodEnum.find(serviceIdentifier); |
| | | if (servicesMethodEnum == ServicesMethodEnum.UNKNOWN) { |
| | | public ResponseResult controlDock(String sn, String serviceIdentifier, RemoteDebugParam param) { |
| | | RemoteControlMethodEnum controlMethodEnum = RemoteControlMethodEnum.find(serviceIdentifier); |
| | | if (RemoteControlMethodEnum.UNKNOWN == controlMethodEnum) { |
| | | return ResponseResult.error("The " + serviceIdentifier + " method does not exist."); |
| | | } |
| | | boolean isExist = redisOps.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + sn) > 0; |
| | | |
| | | // Add parameter validation. |
| | | if (Objects.nonNull(controlMethodEnum.getClazz())) { |
| | | if (Objects.isNull(param)) { |
| | | return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); |
| | | } |
| | | BasicDeviceProperty basicDeviceProperty = mapper.convertValue(param.getAction(), controlMethodEnum.getClazz()); |
| | | if (!basicDeviceProperty.valid()) { |
| | | return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); |
| | | } |
| | | } |
| | | |
| | | boolean isExist = deviceService.checkDeviceOnline(sn); |
| | | if (!isExist) { |
| | | return ResponseResult.error("The dock is offline."); |
| | | } |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + sn + TopicConst.SERVICES_SUF; |
| | | String bid = UUID.randomUUID().toString(); |
| | | Optional<ServiceReply> serviceReplyOpt = messageSenderService.publishWithReply( |
| | | ServiceReply serviceReplyOpt = messageSenderService.publishWithReply( |
| | | topic, CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(bid) |
| | | .method(serviceIdentifier) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data("") |
| | | .data(Objects.requireNonNullElse(param, "")) |
| | | .build()); |
| | | if (serviceReplyOpt.isEmpty()) { |
| | | return ResponseResult.error("No message reply received."); |
| | | } |
| | | |
| | | ServiceReply<EventsOutputReceiver> serviceReply = mapper.convertValue( |
| | | serviceReplyOpt.get(), new TypeReference<ServiceReply<EventsOutputReceiver>>() {}); |
| | | if (serviceReply.getResult() != ResponseResult.CODE_SUCCESS) { |
| | | serviceReplyOpt, new TypeReference<ServiceReply<EventsOutputReceiver>>() {}); |
| | | if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { |
| | | return ResponseResult.error(serviceReply.getResult(), serviceReply.getOutput().getStatus()); |
| | | } |
| | | if (servicesMethodEnum.getProgress()) { |
| | | if (controlMethodEnum.getProgress()) { |
| | | redisOps.setWithExpire(serviceIdentifier + RedisConst.DELIMITER + bid, sn, |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | } |
| | |
| | | .bid(receiver.getBid()) |
| | | .method(receiver.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(ResponseResult.success()) |
| | | .data(RequestsReply.success()) |
| | | .build()); |
| | | } |
| | | } |
| | |
| | | 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.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | 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.service.IDeviceService; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.Message; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Autowired |
| | | private ISendMessageService sendMessageService; |
| | | |
| | | /** |
| | | * Handles the message that the drone goes online. |
| | | * @param receiver The drone information is not empty. |
| | |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(receiver.getMethod()) |
| | | .build()); |
| | | } |
| | | } |
| | |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_OFFLINE, outputChannel = ChannelName.OUTBOUND) |
| | | public void deviceOffline(CommonTopicReceiver<StatusGatewayReceiver> receiver) { |
| | | |
| | | boolean offline = deviceService.deviceOffline(receiver.getData().getSn()); |
| | | boolean offline = deviceService.deviceOffline(receiver.getData()); |
| | | if (offline) { |
| | | // Notify pilot that the device is offline successfully. |
| | | deviceService.publishStatusReply(receiver.getData().getSn(), |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(receiver.getMethod()) |
| | | .build()); |
| | | |
| | | } |
| | |
| | | List<DeviceDTO> devicesList = deviceService.getDevicesTopoForWeb(workspaceId); |
| | | |
| | | 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); |
| | | } |
| | | |
| | | /** |
| | |
| | | @RequestBody List<DeviceFirmwareUpgradeDTO> 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(); |
| | | } |
| | | } |
| | |
| | | return liveStreamService.liveSetQuality(liveParam); |
| | | } |
| | | |
| | | @PostMapping("/streams/switch") |
| | | public ResponseResult liveLensChange(@RequestBody LiveTypeDTO liveParam) { |
| | | return liveStreamService.liveLensChange(liveParam); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.common; |
| | | |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/10 |
| | | */ |
| | | @Component |
| | | @ConfigurationProperties("ntp.server") |
| | | public class NtpServerProperties { |
| | | |
| | | public static String host; |
| | | |
| | | public void setHost(String host) { |
| | | NtpServerProperties.host = host; |
| | | } |
| | | } |
| | |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/22 |
| | |
| | | private String index; |
| | | |
| | | private String type; |
| | | |
| | | private List<String> switchVideoTypes; |
| | | } |
| | |
| | | @JsonProperty("video_quality") |
| | | private Integer videoQuality; |
| | | |
| | | private String videoType; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/10 |
| | | */ |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class NtpServerDTO { |
| | | |
| | | private String ntpServerHost; |
| | | } |
| New file |
| | |
| | | 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(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/14 |
| | | */ |
| | | @Getter |
| | | public enum FirmwareMethodEnum { |
| | | |
| | | OTA_CREATE("ota_create"); |
| | | |
| | | private String method; |
| | | |
| | | FirmwareMethodEnum(String method) { |
| | | this.method = method; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/14 |
| | | */ |
| | | @Getter |
| | | public enum LiveStreamMethodEnum { |
| | | |
| | | LIVE_START_PUSH("live_start_push"), |
| | | |
| | | LIVE_STOP_PUSH("live_stop_push"), |
| | | |
| | | LIVE_SET_QUALITY("live_set_quality"), |
| | | |
| | | LIVE_LENS_CHANGE("live_lens_change"), |
| | | |
| | | UNKNOWN("unknown"); |
| | | |
| | | private String method; |
| | | |
| | | LiveStreamMethodEnum(String method) { |
| | | this.method = method; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/14 |
| | | */ |
| | | @Getter |
| | | public enum LogsFileMethodEnum { |
| | | |
| | | FILE_UPLOAD_LIST("fileupload_list"), |
| | | |
| | | FILE_UPLOAD_START("fileupload_start"), |
| | | |
| | | FILE_UPLOAD_UPDATE("fileupload_update"), |
| | | |
| | | UNKNOWN("unknown"); |
| | | |
| | | private String method; |
| | | |
| | | LogsFileMethodEnum(String method) { |
| | | this.method = method; |
| | | } |
| | | } |
| New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | private Double longitude; |
| | | |
| | | private Double safeLandHeight; |
| | | |
| | | private Integer isConfigured; |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| New file |
| | |
| | | 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 valid(); |
| | | } |
| | | } |
| | |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | | * @date 2021/11/18 |
| | |
| | | private String videoIndex; |
| | | |
| | | private String videoType; |
| | | |
| | | private List<String> switchableVideoTypes; |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| New file |
| | |
| | | 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."); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | |
| | | |
| | | private Integer quality; |
| | | |
| | | private float rate; |
| | | private Float rate; |
| | | } |
| New file |
| | |
| | | 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."); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.0 |
| | | * @date 2022/5/11 |
| | | * @version 1.3 |
| | | * @date 2022/11/3 |
| | | */ |
| | | @Data |
| | | public class OsdDockReceiver { |
| | |
| | | |
| | | private Float environmentTemperature; |
| | | |
| | | private Integer environmentHumidity; |
| | | |
| | | private Float temperature; |
| | | |
| | | private Integer humidity; |
| | |
| | | |
| | | private AlternateLandPointReceiver alternateLandPoint; |
| | | |
| | | private Integer jobNumber; |
| | | |
| | | private Integer accTime; |
| | | |
| | | private Long firstPowerOn; |
| | | |
| | | private PositionStateReceiver positionState; |
| | | |
| | | private StorageReceiver storage; |
| | | |
| | | private Integer electricSupplyVoltage; |
| | | |
| | | private Integer workingVoltage; |
| | | |
| | | private Integer workingCurrent; |
| | | |
| | | private Integer backupBatteryVoltage; |
| | | |
| | | private Integer modeCode; |
| | | |
| | |
| | | |
| | | 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 workingVoltage; |
| | | |
| | | private Integer workingCurrent; |
| | | |
| | | private BackupBatteryReceiver backupBattery; |
| | | |
| | | private DroneBatteryMaintenanceInfoReceiver droneBatteryMaintenanceInfo; |
| | | |
| | | private Integer flighttaskStepCode; |
| | | |
| | | private Integer flighttaskPrepareCapacity; |
| | | |
| | | private DockMediaFileDetailReceiver mediaFileDetail; |
| | | |
| | | private DockSdrReceiver sdr; |
| | | |
| | | private DockWirelessLinkReceiver wirelessLink; |
| | | } |
| | |
| | | private List<OsdPayloadReceiver> payloads; |
| | | |
| | | private StorageReceiver storage; |
| | | |
| | | private Integer nightLightsState; |
| | | |
| | | private Integer heightLimit; |
| | | |
| | | private DistanceLimitStatusReceiver distanceLimitStatus; |
| | | |
| | | private ObstacleAvoidanceReceiver obstacleAvoidance; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/10 |
| | | */ |
| | | @Data |
| | | public class RequestConfigReceiver { |
| | | |
| | | private String configType; |
| | | |
| | | private String configScope; |
| | | } |
| | |
| | | |
| | | /** |
| | | * Query the type data of the device based on domain, device type and sub type. |
| | | * |
| | | * @param domain |
| | | * @param deviceType |
| | | * @param subType |
| | | * @return |
| | | */ |
| | | Optional<DeviceDictionaryDTO> getOneDictionaryInfoByTypeSubType(Integer deviceType, Integer subType); |
| | | Optional<DeviceDictionaryDTO> getOneDictionaryInfoByTypeSubType(Integer domain, Integer deviceType, Integer subType); |
| | | |
| | | } |
| | |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO; |
| | | 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.receiver.FirmwareVersionReceiver; |
| | | 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 java.util.Collection; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | |
| | | |
| | | /** |
| | | * The device goes offline. |
| | | * @param gatewaySn |
| | | * @param gateway |
| | | * @return Whether the offline is successful. |
| | | */ |
| | | Boolean deviceOffline(String gatewaySn); |
| | | Boolean deviceOffline(StatusGatewayReceiver gateway); |
| | | |
| | | /** |
| | | * The aircraft goes offline. |
| | |
| | | |
| | | /** |
| | | * Handle messages from the osd topic. |
| | | * @param topic osd |
| | | * @param payload |
| | | * @param message osd |
| | | */ |
| | | void handleOSD(String topic, byte[] payload); |
| | | void handleOSD(Message<?> message); |
| | | |
| | | /** |
| | | * Update the device information. |
| | |
| | | * @return |
| | | */ |
| | | 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); |
| | | } |
| | |
| | | * @return |
| | | */ |
| | | ResponseResult liveSetQuality(LiveTypeDTO liveParam); |
| | | |
| | | /** |
| | | * Switches the lens of the device during the live streaming. |
| | | * @param liveParam |
| | | * @return |
| | | */ |
| | | ResponseResult liveLensChange(LiveTypeDTO liveParam); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/10 |
| | | */ |
| | | public interface IRequestsConfigService { |
| | | |
| | | /** |
| | | * Get the parameters required by config method. |
| | | * @return |
| | | */ |
| | | Object getConfig(); |
| | | } |
| | |
| | | if (receiver != null) { |
| | | builder.id(UUID.randomUUID().toString()) |
| | | .index(receiver.getVideoIndex()) |
| | | .type(receiver.getVideoType()); |
| | | .type(receiver.getVideoType()) |
| | | .switchVideoTypes(receiver.getSwitchableVideoTypes()); |
| | | } |
| | | return builder.build(); |
| | | } |
| | |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.manage.model.dto.CapacityCameraDTO; |
| | | 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.service.ICameraVideoService; |
| | | import com.dji.sample.manage.service.ICapacityCameraService; |
| | |
| | | // type-subType-index |
| | | if (indexArr.length == 3) { |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByTypeSubType(indexArr[0], indexArr[1]); |
| | | .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), indexArr[0], indexArr[1]); |
| | | dictionaryOpt.ifPresent(dictionary -> |
| | | builder.name(dictionary.getDeviceName())); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.manage.model.common.NtpServerProperties; |
| | | import com.dji.sample.manage.model.dto.NtpServerDTO; |
| | | import com.dji.sample.manage.service.IRequestsConfigService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/10 |
| | | */ |
| | | @Service |
| | | public class ConfigProductServiceImpl implements IRequestsConfigService { |
| | | |
| | | @Override |
| | | public Object getConfig() { |
| | | return new NtpServerDTO(NtpServerProperties.host); |
| | | } |
| | | } |
| | |
| | | private IDeviceDictionaryMapper mapper; |
| | | |
| | | @Override |
| | | public Optional<DeviceDictionaryDTO> getOneDictionaryInfoByTypeSubType(Integer deviceType, Integer subType) { |
| | | if (deviceType == null || subType == null) { |
| | | public Optional<DeviceDictionaryDTO> getOneDictionaryInfoByTypeSubType(Integer domain, Integer deviceType, Integer subType) { |
| | | if (domain == null || deviceType == null || subType == null) { |
| | | return Optional.empty(); |
| | | } |
| | | return Optional.ofNullable( |
| | | entityConvertToDTO( |
| | | mapper.selectOne( |
| | | new LambdaQueryWrapper<DeviceDictionaryEntity>() |
| | | .eq(DeviceDictionaryEntity::getDomain, domain) |
| | | .eq(DeviceDictionaryEntity::getDeviceType, deviceType) |
| | | .eq(DeviceDictionaryEntity::getSubType, subType)))); |
| | | .eq(DeviceDictionaryEntity::getSubType, subType) |
| | | .last(" limit 1 ")))); |
| | | } |
| | | |
| | | /** |
| | |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.param.DeviceOtaCreateParam; |
| | | 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.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | @Autowired |
| | | private IWebSocketManageService webSocketManageService; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Override |
| | | public Optional<DeviceFirmwareDTO> getFirmware(String deviceName, String version) { |
| | | return Optional.ofNullable(entity2Dto(mapper.selectOne( |
| | |
| | | public List<DeviceOtaCreateParam> getDeviceOtaFirmware(List<DeviceFirmwareUpgradeDTO> upgradeDTOS) { |
| | | List<DeviceOtaCreateParam> deviceOtaList = new ArrayList<>(); |
| | | upgradeDTOS.forEach(upgradeDevice -> { |
| | | boolean exist = redisOps.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + upgradeDevice.getSn()) > 0; |
| | | boolean exist = deviceService.checkDeviceOnline(upgradeDevice.getSn()); |
| | | if (!exist) { |
| | | throw new IllegalArgumentException("Device is offline."); |
| | | } |
| | |
| | | .bid(receiver.getBid()) |
| | | .method(receiver.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(ResponseResult.success()) |
| | | .data(RequestsReply.success()) |
| | | .build()); |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.manage.dao.IDeviceLogsMapper; |
| | | import com.dji.sample.manage.model.dto.*; |
| | | import com.dji.sample.manage.model.entity.DeviceLogsEntity; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.enums.DeviceLogsStatusEnum; |
| | | import com.dji.sample.manage.model.enums.LogsFileUpdateMethodEnum; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.enums.*; |
| | | import com.dji.sample.manage.model.param.DeviceLogsCreateParam; |
| | | import com.dji.sample.manage.model.param.DeviceLogsQueryParam; |
| | | import com.dji.sample.manage.model.param.LogsFileUpdateParam; |
| | |
| | | } |
| | | |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF; |
| | | Optional<LogsFileUploadList> serviceReplyOpt = messageSenderService.publishWithReply( |
| | | LogsFileUploadList data = messageSenderService.publishWithReply( |
| | | LogsFileUploadList.class, |
| | | topic, |
| | | CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(UUID.randomUUID().toString()) |
| | | .method(ServicesMethodEnum.FILE_UPLOAD_LIST.getMethod()) |
| | | .method(LogsFileMethodEnum.FILE_UPLOAD_LIST.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(Map.of(MapKeyConst.MODULE_LIST, domainList)) |
| | | .build(), 1); |
| | | if (serviceReplyOpt.isEmpty()) { |
| | | return ResponseResult.error("No message reply received."); |
| | | } |
| | | LogsFileUploadList data = serviceReplyOpt.get(); |
| | | |
| | | for (LogsFileUpload file : data.getFiles()) { |
| | | if (file.getDeviceSn().isBlank()) { |
| | | file.setDeviceSn(deviceSn); |
| | |
| | | |
| | | credentialsDTO.setParams(LogsFileUploadList.builder().files(files).build()); |
| | | String bid = UUID.randomUUID().toString(); |
| | | Optional<ServiceReply> serviceReply = messageSenderService.publishWithReply( |
| | | ServiceReply reply = messageSenderService.publishWithReply( |
| | | TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF, |
| | | CommonTopicResponse.<LogsUploadCredentialsDTO>builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(bid) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(ServicesMethodEnum.FILE_UPLOAD_START.getMethod()) |
| | | .method(LogsFileMethodEnum.FILE_UPLOAD_START.getMethod()) |
| | | .data(credentialsDTO) |
| | | .build()); |
| | | |
| | | if (serviceReply.isEmpty()) { |
| | | return ResponseResult.error("No message reply received."); |
| | | } |
| | | ServiceReply reply = serviceReply.get(); |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | return ResponseResult.error(String.valueOf(reply.getResult())); |
| | | } |
| | |
| | | } |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF; |
| | | String bid = UUID.randomUUID().toString(); |
| | | Optional<ServiceReply> serviceReply = messageSenderService.publishWithReply(topic, |
| | | ServiceReply reply = messageSenderService.publishWithReply(topic, |
| | | CommonTopicResponse.<LogsFileUpdateParam>builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(bid) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(ServicesMethodEnum.FILE_UPLOAD_UPDATE.getMethod()) |
| | | .method(LogsFileMethodEnum.FILE_UPLOAD_UPDATE.getMethod()) |
| | | .data(param) |
| | | .build()); |
| | | |
| | | if (serviceReply.isEmpty()) { |
| | | return ResponseResult.error("No message reply received."); |
| | | } |
| | | ServiceReply reply = serviceReply.get(); |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | return ResponseResult.error("Error Code : " + reply.getResult()); |
| | | } |
| | |
| | | .bid(receiver.getBid()) |
| | | .method(receiver.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(ResponseResult.success()) |
| | | .data(RequestsReply.success()) |
| | | .build()); |
| | | } |
| | | |
| | |
| | | List<LogsExtFileReceiver> fileReceivers = output.getExt().getFiles(); |
| | | if (CollectionUtils.isEmpty(fileReceivers)) { |
| | | redisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); |
| | | return; |
| | | } |
| | | |
| | | // refresh cache. |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | 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.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | |
| | | 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); |
| | | |
| | | sendMessageService.sendBatch(webSessions, wsMessage); |
| | |
| | | import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; |
| | | import com.dji.sample.manage.model.dto.DevicePayloadDTO; |
| | | import com.dji.sample.manage.model.entity.DevicePayloadEntity; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; |
| | | import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; |
| | | import com.dji.sample.manage.service.ICapacityCameraService; |
| | |
| | | |
| | | if (arr.length == 3) { |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByTypeSubType(arr[0], arr[1]); |
| | | .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), arr[0], arr[1]); |
| | | dictionaryOpt.ifPresent(dictionary -> |
| | | builder.payloadName(dictionary.getDeviceName()) |
| | | .payloadDesc(dictionary.getDeviceDesc())); |
| | |
| | | import com.dji.sample.manage.dao.IDeviceMapper; |
| | | import com.dji.sample.manage.model.dto.*; |
| | | import com.dji.sample.manage.model.entity.DeviceEntity; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.enums.DeviceFirmwareStatusEnum; |
| | | import com.dji.sample.manage.model.enums.IconUrlEnum; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.enums.*; |
| | | import com.dji.sample.manage.model.param.DeviceOtaCreateParam; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.model.receiver.*; |
| | | import com.dji.sample.manage.service.*; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.Message; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | @Qualifier("gatewayOSDServiceImpl") |
| | | 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 |
| | | public Boolean deviceOffline(String gatewaySn) { |
| | | public Boolean deviceOffline(StatusGatewayReceiver gateway) { |
| | | String gatewaySn = gateway.getSn(); |
| | | this.subscribeTopicOnline(gatewaySn); |
| | | |
| | | // Only the remote controller is logged in and the aircraft is not connected. |
| | |
| | | Optional<DeviceDTO> gatewayOpt = this.getDeviceBySn(gatewaySn); |
| | | if (gatewayOpt.isPresent()) { |
| | | DeviceDTO value = gatewayOpt.get(); |
| | | value.setChildDeviceSn(value.getDeviceSn()); |
| | | value.setBoundTime(null); |
| | | value.setLoginTime(null); |
| | | redisOps.setWithExpire(key, value, RedisConst.DEVICE_ALIVE_SECOND); |
| | | this.pushDeviceOnlineTopo(value.getWorkspaceId(), gatewaySn, gatewaySn); |
| | | return true; |
| | | } |
| | | DeviceDTO gateway = DeviceDTO.builder() |
| | | .deviceSn(gatewaySn) |
| | | .childDeviceSn(gatewaySn) |
| | | .domain(DeviceDomainEnum.GATEWAY.getDesc()) |
| | | .build(); |
| | | gatewayOpt.map(DeviceDTO::getWorkspaceId).ifPresent(gateway::setWorkspaceId); |
| | | redisOps.setWithExpire(key, gateway, RedisConst.DEVICE_ALIVE_SECOND); |
| | | this.pushDeviceOnlineTopo(gateway.getWorkspaceId(), gatewaySn, gatewaySn); |
| | | return true; |
| | | |
| | | // When connecting for the first time |
| | | DeviceEntity gatewayDevice = deviceGatewayConvertToDeviceEntity(gateway); |
| | | return firstSaveDevice(gatewayDevice, null); |
| | | } |
| | | |
| | | String deviceSn = ((DeviceDTO)(redisOps.get(key))).getChildDeviceSn(); |
| | | if (deviceSn.equals(gatewaySn)) { |
| | | DeviceDTO deviceDTO = (DeviceDTO) (redisOps.get(key)); |
| | | String deviceSn = deviceDTO.getChildDeviceSn(); |
| | | if (!StringUtils.hasText(deviceSn)) { |
| | | return true; |
| | | } |
| | | |
| | |
| | | |
| | | @Override |
| | | public Boolean subDeviceOffline(String deviceSn) { |
| | | // Cancel drone-related subscriptions. |
| | | this.unsubscribeTopicOffline(deviceSn); |
| | | |
| | | payloadService.deletePayloadsByDeviceSn(new ArrayList<>(List.of(deviceSn))); |
| | | // If no information about this gateway device exists in the database, the drone is considered to be offline. |
| | | // If no information about this device exists in the cache, the drone is considered to be offline. |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; |
| | | if (!redisOps.checkExist(key) || redisOps.getExpire(key) <= 0) { |
| | | log.debug("The drone is already offline."); |
| | | return true; |
| | | } |
| | | 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. |
| | | this.pushDeviceOfflineTopo(device.getWorkspaceId(), deviceSn); |
| | | |
| | | redisOps.del(key); |
| | | redisOps.del(RedisConst.OSD_PREFIX + device.getDeviceSn()); |
| | | log.debug("{} offline.", deviceSn); |
| | | return true; |
| | | } |
| | |
| | | // change log: Use redis instead of |
| | | long time = redisOps.getExpire(key); |
| | | long gatewayTime = redisOps.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + deviceGateway.getSn()); |
| | | long now = System.currentTimeMillis(); |
| | | |
| | | if (time > 0 && gatewayTime > 0) { |
| | | redisOps.expireKey(key, RedisConst.DEVICE_ALIVE_SECOND); |
| | |
| | | this.updateDevice(gateway); |
| | | }); |
| | | |
| | | |
| | | DeviceEntity gateway = deviceGatewayConvertToDeviceEntity(deviceGateway); |
| | | gateway.setChildSn(deviceSn); |
| | | // Set the icon of the gateway device displayed in the pilot's map, required in the TSA module. |
| | | gateway.setUrlNormal(IconUrlEnum.NORMAL_PERSON.getUrl()); |
| | | // Set the icon of the gateway device displayed in the pilot's map when it is selected, required in the TSA module. |
| | | gateway.setUrlSelect(IconUrlEnum.SELECT_PERSON.getUrl()); |
| | | gateway.setLoginTime(now); |
| | | |
| | | DeviceEntity subDevice = subDeviceConvertToDeviceEntity(deviceGateway.getSubDevices().get(0)); |
| | | // Set the icon of the drone device displayed in the pilot's map when it is selected, required in the TSA module. |
| | | subDevice.setUrlNormal(IconUrlEnum.NORMAL_EQUIPMENT.getUrl()); |
| | | // Set the icon of the drone device displayed in the pilot's map, required in the TSA module. |
| | | subDevice.setUrlSelect(IconUrlEnum.SELECT_EQUIPMENT.getUrl()); |
| | | subDevice.setLoginTime(now); |
| | | boolean isSave = firstSaveDevice(gateway, deviceSn) && firstSaveDevice(subDevice, null); |
| | | if (!isSave) { |
| | | return false; |
| | | } |
| | | |
| | | // dock go online |
| | | if (deviceGateway.getDomain() != null && DeviceDomainEnum.DOCK.getVal() == deviceGateway.getDomain()) { |
| | | Optional<DeviceDTO> deviceOpt = this.getDeviceBySn(deviceGateway.getSn()); |
| | | if (deviceOpt.isEmpty()) { |
| | | log.info("The dock is not bound and cannot go online."); |
| | | log.info("The dock is not bound and cannot go online. Please refer to the Cloud API document video for binding."); |
| | | return false; |
| | | } |
| | | gateway.setNickname(null); |
| | | subDevice.setNickname(null); |
| | | } |
| | | |
| | | Optional<DeviceEntity> gatewayOpt = this.saveDevice(gateway); |
| | | String workspaceId = this.saveDevice(subDevice).orElse(subDevice).getWorkspaceId(); |
| | | String workspaceId = subDevice.getWorkspaceId(); |
| | | |
| | | redisOps.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + deviceSn, |
| | | DeviceDTO.builder() |
| | | .deviceSn(deviceSn) |
| | | .domain(DeviceDomainEnum.SUB_DEVICE.getDesc()) |
| | | .workspaceId(workspaceId) |
| | | .build(), |
| | | RedisConst.DEVICE_ALIVE_SECOND); |
| | | redisOps.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + gateway.getDeviceSn(), |
| | | DeviceDTO.builder() |
| | | .deviceSn(gateway.getDeviceSn()) |
| | | .workspaceId(gatewayOpt.orElse(gateway).getWorkspaceId()) |
| | | .childDeviceSn(deviceSn) |
| | | .domain(deviceGateway.getDomain() != null ? |
| | | DeviceDomainEnum.getDesc(deviceGateway.getDomain()) : |
| | | DeviceDomainEnum.GATEWAY.getDesc()) |
| | | .build(), |
| | | RedisConst.DEVICE_ALIVE_SECOND); |
| | | log.debug("{} online.", subDevice.getDeviceSn()); |
| | | |
| | | if (StringUtils.hasText(workspaceId)) { |
| | | this.pushDeviceOnlineTopo(workspaceId, deviceGateway.getSn(), deviceSn); |
| | | this.subscribeTopicOnline(deviceGateway.getSn()); |
| | | if (!StringUtils.hasText(workspaceId)) { |
| | | log.info("The drone is not bound and cannot go online. Please refer to the Cloud API document video for binding."); |
| | | return true; |
| | | } |
| | | |
| | | // Subscribe to topic related to drone devices. |
| | | this.subscribeTopicOnline(deviceSn); |
| | | this.subscribeTopicOnline(deviceGateway.getSn()); |
| | | this.pushDeviceOnlineTopo(workspaceId, deviceGateway.getSn(), deviceSn); |
| | | |
| | | log.debug("{} online.", subDevice.getDeviceSn()); |
| | | return true; |
| | | } |
| | | |
| | |
| | | return; |
| | | } |
| | | } |
| | | topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + OSD_SUF); |
| | | topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + STATE_SUF); |
| | | topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + SERVICES_SUF + _REPLY_SUF); |
| | | topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + REQUESTS_SUF); |
| | | topicService.subscribe(THING_MODEL_PRE + PRODUCT + sn + EVENTS_SUF); |
| | | String prefix = THING_MODEL_PRE + PRODUCT + sn; |
| | | INIT_TOPICS_SUFFIX.forEach(suffix -> topicService.subscribe(prefix + suffix)); |
| | | } |
| | | |
| | | @Override |
| | | public void unsubscribeTopicOffline(String sn) { |
| | | topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + OSD_SUF); |
| | | topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + STATE_SUF); |
| | | topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + SERVICES_SUF + _REPLY_SUF); |
| | | topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + REQUESTS_SUF); |
| | | topicService.unsubscribe(THING_MODEL_PRE + PRODUCT + sn + EVENTS_SUF); |
| | | String prefix = THING_MODEL_PRE + PRODUCT + sn; |
| | | INIT_TOPICS_SUFFIX.forEach(suffix -> topicService.unsubscribe(prefix + suffix)); |
| | | } |
| | | |
| | | @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; |
| | | try { |
| | | String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), |
| | |
| | | |
| | | // Query the model information of this gateway device. |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByTypeSubType(gateway.getType(), gateway.getSubType()); |
| | | .getOneDictionaryInfoByTypeSubType(Objects.nonNull(gateway.getDomain()) ? |
| | | gateway.getDomain() : DeviceDomainEnum.GATEWAY.getVal(), |
| | | gateway.getType(), gateway.getSubType()); |
| | | |
| | | dictionaryOpt.ifPresent(entity -> |
| | | builder.deviceName(entity.getDeviceName()) |
| | |
| | | |
| | | // Query the model information of this drone device. |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByTypeSubType(device.getType(), device.getSubType()); |
| | | .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.SUB_DEVICE.getVal(), device.getType(), device.getSubType()); |
| | | |
| | | dictionaryOpt.ifPresent(dictionary -> |
| | | builder.deviceName(dictionary.getDeviceName()) |
| | |
| | | .subType(device.getSubType()) |
| | | .version(device.getVersion()) |
| | | .deviceIndex(device.getIndex()) |
| | | .domain(device.getDomain() != null ? |
| | | device.getDomain() : DeviceDomainEnum.SUB_DEVICE.getVal()) |
| | | .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) |
| | | .build(); |
| | | } |
| | | |
| | |
| | | device.setBoundTime(LocalDateTime.now()); |
| | | |
| | | boolean isUpd = this.saveDevice(this.deviceDTO2Entity(device)).isPresent(); |
| | | if (DeviceDomainEnum.DOCK.getDesc().equals(device.getDomain())) { |
| | | return isUpd; |
| | | } |
| | | if (!isUpd) { |
| | | return false; |
| | | } |
| | | |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(); |
| | | DeviceDTO redisDevice = (DeviceDTO)redisOps.get(key); |
| | | if (Objects.isNull(redisDevice)) { |
| | | return false; |
| | | } |
| | | redisDevice.setWorkspaceId(device.getWorkspaceId()); |
| | | redisOps.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND); |
| | | |
| | |
| | | |
| | | assert dock != null; |
| | | |
| | | Optional<DeviceEntity> dockEntityOpt = this.bindDevice2Entity(dock); |
| | | Optional<DeviceEntity> droneEntityOpt = this.bindDevice2Entity(drone); |
| | | Optional<DeviceEntity> dockEntityOpt = this.bindDevice2Entity(DeviceDomainEnum.DOCK.getVal(), dock); |
| | | Optional<DeviceEntity> droneEntityOpt = this.bindDevice2Entity(DeviceDomainEnum.SUB_DEVICE.getVal(), drone); |
| | | |
| | | List<ErrorInfoReply> bindResult = new ArrayList<>(); |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_FIRMWARE_VERSION) |
| | | 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) { |
| | | final DeviceDTO device = DeviceDTO.builder() |
| | | .deviceSn(receiver.getSn()) |
| | |
| | | |
| | | // The bids in the progress messages reported subsequently are the same. |
| | | String bid = UUID.randomUUID().toString(); |
| | | Optional<ServiceReply> serviceReplyOpt = messageSender.publishWithReply( |
| | | ServiceReply serviceReply = messageSender.publishWithReply( |
| | | topic, CommonTopicResponse.<Map<String, List<DeviceOtaCreateParam>>>builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(bid) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(ServicesMethodEnum.OTA_CREATE.getMethod()) |
| | | .method(FirmwareMethodEnum.OTA_CREATE.getMethod()) |
| | | .data(Map.of(MapKeyConst.DEVICES, deviceOtaFirmwares)) |
| | | .build()); |
| | | if (serviceReplyOpt.isEmpty()) { |
| | | return ResponseResult.error("No message reply received."); |
| | | } |
| | | ServiceReply serviceReply = serviceReplyOpt.get(); |
| | | if (serviceReply.getResult() != ResponseResult.CODE_SUCCESS) { |
| | | return ResponseResult.error(serviceReply.getResult(), "Firmware Error Code: " + serviceReply.getResult()); |
| | | } |
| | | if (ServicesMethodEnum.OTA_CREATE.getProgress()) { |
| | | // Record the device state that needs to be updated. |
| | | deviceOtaFirmwares.forEach(deviceOta -> redisOps.setWithExpire( |
| | | RedisConst.FIRMWARE_UPGRADING_PREFIX + deviceOta.getSn(), |
| | | bid, |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND)); |
| | | } |
| | | |
| | | // Record the device state that needs to be updated. |
| | | deviceOtaFirmwares.forEach(deviceOta -> redisOps.setWithExpire( |
| | | RedisConst.FIRMWARE_UPGRADING_PREFIX + deviceOta.getSn(), |
| | | bid, |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND)); |
| | | 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 binding data object into database entity object. |
| | | * |
| | | * @param domain |
| | | * @param receiver |
| | | * @return |
| | | */ |
| | | private Optional<DeviceEntity> bindDevice2Entity(BindDeviceReceiver receiver) { |
| | | private Optional<DeviceEntity> bindDevice2Entity(Integer domain, BindDeviceReceiver receiver) { |
| | | if (receiver == null) { |
| | | return Optional.empty(); |
| | | } |
| | | 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(); |
| | | |
| | | dictionaryOpt.ifPresent(entity -> |
| | |
| | | .organizationName(device.getWorkspaceName()) |
| | | .build(); |
| | | } |
| | | |
| | | private Boolean firstSaveDevice(DeviceEntity device, String deviceSn) { |
| | | |
| | | Optional<DeviceDTO> deviceOpt = this.getDeviceBySn(device.getDeviceSn()); |
| | | if (deviceOpt.isEmpty()) { |
| | | // Set the icon of the gateway device displayed in the pilot's map, required in the TSA module. |
| | | device.setUrlNormal(IconUrlEnum.NORMAL_PERSON.getUrl()); |
| | | // Set the icon of the gateway device displayed in the pilot's map when it is selected, required in the TSA module. |
| | | device.setUrlSelect(IconUrlEnum.SELECT_PERSON.getUrl()); |
| | | } |
| | | device.setChildSn(deviceSn); |
| | | device.setLoginTime(System.currentTimeMillis()); |
| | | |
| | | Optional<DeviceEntity> saveDeviceOpt = this.saveDevice(device); |
| | | if (saveDeviceOpt.isEmpty()) { |
| | | return false; |
| | | } |
| | | device.setWorkspaceId(saveDeviceOpt.get().getWorkspaceId()); |
| | | |
| | | redisOps.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(), |
| | | DeviceDTO.builder() |
| | | .deviceSn(device.getDeviceSn()) |
| | | .workspaceId(device.getWorkspaceId()) |
| | | .childDeviceSn(deviceSn) |
| | | .domain(DeviceDomainEnum.getDesc(device.getDomain())) |
| | | .type(device.getDeviceType()) |
| | | .subType(device.getSubType()) |
| | | .build(), |
| | | RedisConst.DEVICE_ALIVE_SECOND); |
| | | |
| | | return true; |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.manage.model.dto.TelemetryDTO; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.receiver.OsdDockReceiver; |
| | | import com.dji.sample.manage.model.receiver.OsdDockTransmissionReceiver; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Collection; |
| | |
| | | wsMessage.setBizCode(BizCodeEnum.DOCK_OSD.getCode()); |
| | | OsdDockReceiver data = mapper.convertValue(receiver.getData(), OsdDockReceiver.class); |
| | | wsMessage.getData().setHost(data); |
| | | if (data.getSubDevice() == null) { |
| | | OsdDockTransmissionReceiver transmission = mapper.convertValue(receiver.getData(), OsdDockTransmissionReceiver.class); |
| | | wsMessage.getData().setHost(transmission); |
| | | } |
| | | sendMessageService.sendBatch(webSessions, wsMessage); |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.model.ServiceReply; |
| | | import com.dji.sample.component.mqtt.model.ServicesMethodEnum; |
| | | import com.dji.sample.component.mqtt.model.StateDataEnum; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.manage.model.dto.*; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.enums.LiveStreamMethodEnum; |
| | | import com.dji.sample.manage.model.enums.LiveUrlTypeEnum; |
| | | import com.dji.sample.manage.model.enums.LiveVideoQualityEnum; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | |
| | | return devicesList.stream() |
| | | .filter(device -> redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())) |
| | | .map(device -> CapacityDeviceDTO.builder() |
| | | .name(device.getDeviceName()) |
| | | .name(Objects.requireNonNullElse(device.getNickname(), device.getDeviceName())) |
| | | .sn(device.getDeviceSn()) |
| | | .camerasList(capacityCameraService.getCapacityCameraByDeviceSn(device.getDeviceSn())) |
| | | .build()) |
| | |
| | | public ResponseResult liveStart(LiveTypeDTO liveParam) { |
| | | // Check if this lens is available live. |
| | | ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId()); |
| | | if (responseResult.getCode() != 0) { |
| | | if (ResponseResult.CODE_SUCCESS != responseResult.getCode()) { |
| | | return responseResult; |
| | | } |
| | | |
| | |
| | | // target topic |
| | | String respTopic = THING_MODEL_PRE + PRODUCT + |
| | | data.getDeviceSn() + SERVICES_SUF; |
| | | Optional<ServiceReply> receiveReplyOpt = this.publishLiveStart(respTopic, liveParam); |
| | | ServiceReply receiveReply = this.publishLiveStart(respTopic, liveParam); |
| | | |
| | | if (receiveReplyOpt.isEmpty()) { |
| | | return ResponseResult.error(LiveErrorEnum.NO_REPLY); |
| | | } |
| | | if (receiveReplyOpt.get().getResult() != 0) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult())); |
| | | if (ResponseResult.CODE_SUCCESS != receiveReply.getResult()) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult())); |
| | | } |
| | | |
| | | LiveUrlTypeEnum urlType = LiveUrlTypeEnum.find(liveParam.getUrlType()); |
| | |
| | | .toString()); |
| | | break; |
| | | case RTSP: |
| | | String url = receiveReplyOpt.get().getInfo().toString(); |
| | | String url = receiveReply.getInfo().toString(); |
| | | this.resolveUrlUser(url, live); |
| | | break; |
| | | case UNKNOWN: |
| | |
| | | |
| | | String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; |
| | | |
| | | Optional<ServiceReply> receiveReplyOpt = this.publishLiveStop(respTopic, videoId); |
| | | if (receiveReplyOpt.isEmpty()) { |
| | | return ResponseResult.error(LiveErrorEnum.NO_REPLY); |
| | | } |
| | | if (receiveReplyOpt.get().getResult() != 0) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult())); |
| | | ServiceReply receiveReply = this.publishLiveStop(respTopic, videoId); |
| | | if (receiveReply.getResult() != 0) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult())); |
| | | } |
| | | |
| | | return ResponseResult.success(); |
| | |
| | | |
| | | String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; |
| | | |
| | | Optional<ServiceReply> receiveReplyOpt = this.publishLiveSetQuality(respTopic, liveParam); |
| | | if (receiveReplyOpt.isEmpty()) { |
| | | return ResponseResult.error(LiveErrorEnum.NO_REPLY); |
| | | } |
| | | if (receiveReplyOpt.get().getResult() != 0) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReplyOpt.get().getResult())); |
| | | ServiceReply receiveReply = this.publishLiveSetQuality(respTopic, liveParam); |
| | | if (ResponseResult.CODE_SUCCESS == receiveReply.getResult()) { |
| | | return ResponseResult.error(LiveErrorEnum.find(receiveReply.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(); |
| | | } |
| | | |
| | | private ServiceReply publishLiveLensChange(String respTopic, LiveTypeDTO liveParam) { |
| | | CommonTopicResponse<LiveTypeDTO> response = new CommonTopicResponse<>(); |
| | | response.setTid(UUID.randomUUID().toString()); |
| | | response.setBid(UUID.randomUUID().toString()); |
| | | response.setMethod(LiveStreamMethodEnum.LIVE_LENS_CHANGE.getMethod()); |
| | | response.setData(liveParam); |
| | | |
| | | return messageSender.publishWithReply(respTopic, response); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return |
| | | */ |
| | | private ResponseResult<DeviceDTO> checkBeforeLive(String videoId) { |
| | | if (!StringUtils.hasText(videoId)) { |
| | | return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS); |
| | | } |
| | | String[] videoIdArr = videoId.split("/"); |
| | | // drone sn / enumeration value of the location where the payload is mounted / payload lens |
| | | if (videoIdArr.length != 3) { |
| | |
| | | * @param liveParam |
| | | * @return |
| | | */ |
| | | private Optional<ServiceReply> publishLiveStart(String topic, LiveTypeDTO liveParam) { |
| | | private ServiceReply publishLiveStart(String topic, LiveTypeDTO liveParam) { |
| | | CommonTopicResponse<LiveTypeDTO> response = new CommonTopicResponse<>(); |
| | | response.setTid(UUID.randomUUID().toString()); |
| | | response.setBid(UUID.randomUUID().toString()); |
| | | response.setData(liveParam); |
| | | response.setMethod(ServicesMethodEnum.LIVE_START_PUSH.getMethod()); |
| | | response.setMethod(LiveStreamMethodEnum.LIVE_START_PUSH.getMethod()); |
| | | |
| | | return messageSender.publishWithReply(topic, response); |
| | | } |
| | |
| | | * @param liveParam |
| | | * @return |
| | | */ |
| | | private Optional<ServiceReply> publishLiveSetQuality(String respTopic, LiveTypeDTO liveParam) { |
| | | private ServiceReply publishLiveSetQuality(String respTopic, LiveTypeDTO liveParam) { |
| | | Map<String, Object> data = new ConcurrentHashMap<>(Map.of( |
| | | "video_id", liveParam.getVideoId(), |
| | | "video_quality", liveParam.getVideoQuality())); |
| | | CommonTopicResponse<Map<String, Object>> response = new CommonTopicResponse<>(); |
| | | response.setTid(UUID.randomUUID().toString()); |
| | | response.setBid(UUID.randomUUID().toString()); |
| | | response.setMethod(ServicesMethodEnum.LIVE_SET_QUALITY.getMethod()); |
| | | response.setMethod(LiveStreamMethodEnum.LIVE_SET_QUALITY.getMethod()); |
| | | response.setData(data); |
| | | |
| | | return messageSender.publishWithReply(respTopic, response); |
| | |
| | | * @param videoId |
| | | * @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)); |
| | | CommonTopicResponse<Map<String, String>> response = new CommonTopicResponse<>(); |
| | | response.setTid(UUID.randomUUID().toString()); |
| | | response.setBid(UUID.randomUUID().toString()); |
| | | response.setData(data); |
| | | response.setMethod(ServicesMethodEnum.LIVE_STOP_PUSH.getMethod()); |
| | | response.setMethod(LiveStreamMethodEnum.LIVE_STOP_PUSH.getMethod()); |
| | | |
| | | return messageSender.publishWithReply(topic, response); |
| | | } |
| | |
| | | files.forEach(file -> { |
| | | Optional<LogsFileUpload> fileOpt = this.getFileIndexByFileId(file.getFileId()); |
| | | fileOpt.ifPresent(fileUpload -> { |
| | | fileUpload.setObjectKey(file.getStatus() ? file.getObjectKey() : ""); |
| | | fileUpload.setObjectKey(file.getStatus() ? file.getObjectKey() : null); |
| | | list.add(fileUpload); |
| | | }); |
| | | }); |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.common.util.SpringBeanUtils; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.manage.model.receiver.RequestConfigReceiver; |
| | | import com.dji.sample.manage.service.IRequestsConfigService; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | 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 java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/10 |
| | | */ |
| | | @Service |
| | | public class RequestConfigContext { |
| | | |
| | | @Autowired |
| | | private IMessageSenderService messageSenderService; |
| | | |
| | | @Autowired |
| | | private ObjectMapper objectMapper; |
| | | |
| | | |
| | | /** |
| | | * Handles the config method of the requests topic. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_CONFIG, outputChannel = ChannelName.OUTBOUND) |
| | | void handleConfig(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | RequestConfigReceiver configReceiver = objectMapper.convertValue(receiver.getData(), RequestConfigReceiver.class); |
| | | Optional<ConfigScopeEnum> scopeEnumOpt = ConfigScopeEnum.find(configReceiver.getConfigScope()); |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF; |
| | | CommonTopicResponse.CommonTopicResponseBuilder<Object> build = CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(receiver.getMethod()); |
| | | if (scopeEnumOpt.isEmpty()) { |
| | | messageSenderService.publish(topic, build.build()); |
| | | return; |
| | | } |
| | | |
| | | IRequestsConfigService requestsConfigService = SpringBeanUtils.getBean(scopeEnumOpt.get().getClazz()); |
| | | build.data(requestsConfigService.getConfig()); |
| | | messageSenderService.publish(topic, build.build()); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | 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.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | |
| | | if (!StringUtils.hasText(token)) { |
| | | 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()); |
| | | |
| | | UserDTO user = entityConvertToDTO(this.getUserByUsername(customClaim.getUsername())); |
| | | if (Objects.isNull(user)) { |
| | | return Optional.empty(); |
| | | } |
| | | user.setWorkspaceId(customClaim.getWorkspaceId()); |
| | | user.setAccessToken(refreshToken); |
| | | return Optional.of(user); |
| | |
| | | */ |
| | | private UserDTO entityConvertToDTO(UserEntity entity) { |
| | | if (entity == null) { |
| | | return new UserDTO(); |
| | | return null; |
| | | } |
| | | return UserDTO.builder() |
| | | .userId(entity.getUserId()) |
| | |
| | | @AllArgsConstructor |
| | | public class CredentialsDTO { |
| | | |
| | | private static final int DELAY = 300; |
| | | |
| | | private String accessKeyId; |
| | | |
| | | private String accessKeySecret; |
| | |
| | | this.accessKeyId = credentials.accessKey(); |
| | | this.accessKeySecret = credentials.secretKey(); |
| | | this.securityToken = credentials.sessionToken(); |
| | | this.expire = expire; |
| | | this.expire = expire - DELAY; |
| | | } |
| | | |
| | | public CredentialsDTO(AssumeRoleResponse.Credentials credentials, long expire) { |
| | | this.accessKeyId = credentials.getAccessKeyId(); |
| | | this.accessKeySecret = credentials.getAccessKeySecret(); |
| | | this.securityToken = credentials.getSecurityToken(); |
| | | this.expire = expire; |
| | | this.expire = expire - DELAY; |
| | | } |
| | | |
| | | public CredentialsDTO(com.amazonaws.services.securitytoken.model.Credentials credentials) { |
| | | this.accessKeyId = credentials.getAccessKeyId(); |
| | | this.accessKeySecret = credentials.getSecretAccessKey(); |
| | | this.securityToken = credentials.getSessionToken(); |
| | | this.expire = (credentials.getExpiration().getTime() - System.currentTimeMillis()) / 1000; |
| | | this.expire = (credentials.getExpiration().getTime() - System.currentTimeMillis()) / 1000 - DELAY; |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.component.oss.model.OssConfiguration; |
| | | import com.dji.sample.component.oss.service.impl.OssServiceContext; |
| | | 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.media.dao.IFileMapper; |
| | | import com.dji.sample.media.model.FileUploadDTO; |
| | |
| | | Page<MediaFileEntity> pageData = mapper.selectPage( |
| | | new Page<MediaFileEntity>(page, pageSize), |
| | | new LambdaQueryWrapper<MediaFileEntity>() |
| | | .eq(MediaFileEntity::getWorkspaceId, workspaceId)); |
| | | .eq(MediaFileEntity::getWorkspaceId, workspaceId) |
| | | .orderByDesc(MediaFileEntity::getId)); |
| | | List<MediaFileDTO> records = pageData.getRecords() |
| | | .stream() |
| | | .map(this::entityConvertToDto) |
| | |
| | | .mapToInt(Integer::intValue) |
| | | .toArray(); |
| | | Optional<DeviceDictionaryDTO> payloadDict = deviceDictionaryService |
| | | .getOneDictionaryInfoByTypeSubType(payloadModel[1], payloadModel[2]); |
| | | .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), payloadModel[1], payloadModel[2]); |
| | | payloadDict.ifPresent(payload -> builder.payload(payload.getDeviceName())); |
| | | } |
| | | return builder.build(); |
| | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | 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.FileUploadDTO; |
| | | import com.dji.sample.media.model.MediaFileDTO; |
| | |
| | | |
| | | @Autowired |
| | | private IMessageSenderService messageSenderService; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Override |
| | | public Boolean fastUpload(String workspaceId, String fingerprint) { |
| | |
| | | CommonTopicResponse<Object> data = CommonTopicResponse.builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(EventsMethodEnum.FILE_UPLOAD_CALLBACK.getMethod()) |
| | | .data(ResponseResult.success()) |
| | | .data(RequestsReply.success()) |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .build(); |
| | |
| | | String jobId = callback.getFile().getExt().getFlightId(); |
| | | Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(jobId); |
| | | 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()); |
| | | if (id <= 0) { |
| | | data.setData(ResponseResult.error()); |
| | |
| | | import com.dji.sample.wayline.service.IWaylineFileService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | |
| | | import java.net.URL; |
| | | import java.sql.SQLException; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | |
| | | boolean isDel = waylineFileService.deleteByWaylineId(workspaceId, waylineId); |
| | | 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(); |
| | | } |
| | | } |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.validation.Valid; |
| | | import java.sql.SQLException; |
| | | import java.util.List; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | |
| | | * @throws SQLException |
| | | */ |
| | | @PostMapping("/{workspace_id}/flight-tasks") |
| | | public ResponseResult createJob(HttpServletRequest request, @RequestBody CreateJobParam param, |
| | | public ResponseResult createJob(HttpServletRequest request, @Valid @RequestBody CreateJobParam param, |
| | | @PathVariable(name = "workspace_id") String workspaceId) throws SQLException { |
| | | CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); |
| | | customClaim.setWorkspaceId(workspaceId); |
| | | boolean isCreate = waylineJobService.createJob(param, customClaim); |
| | | return isCreate ? ResponseResult.success() : ResponseResult.error(); |
| | | |
| | | return waylineJobService.publishFlightTask(param, customClaim); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Issue wayline mission to the dock for execution. |
| | | * @param jobId |
| | | * Send the command to cancel the jobs. |
| | | * @param jobIds |
| | | * @param workspaceId |
| | | * @return |
| | | * @throws SQLException |
| | | */ |
| | | @PostMapping("/{workspace_id}/jobs/{job_id}") |
| | | public ResponseResult publishJob(@PathVariable(name = "job_id") String jobId, |
| | | @DeleteMapping("/{workspace_id}/jobs") |
| | | public ResponseResult publishCancelJob(@RequestParam(name = "job_id") List<String> jobIds, |
| | | @PathVariable(name = "workspace_id") String workspaceId) throws SQLException { |
| | | waylineJobService.publishFlightTask(workspaceId, jobId); |
| | | waylineJobService.cancelFlightTask(workspaceId, jobIds); |
| | | return ResponseResult.success(); |
| | | } |
| | | } |
| | |
| | | |
| | | private String flightId; |
| | | |
| | | private String type; |
| | | private Integer taskType; |
| | | |
| | | private Integer waylineType; |
| | | |
| | | private Long executeTime; |
| | | |
| | | private FlightTaskFileDTO file; |
| | | |
| | | private Integer rthAltitude; |
| | | |
| | | private Integer outOfControlAction; |
| | | } |
| | |
| | | |
| | | private String url; |
| | | |
| | | private String sign; |
| | | private String fingerprint; |
| | | } |
| New file |
| | |
| | | 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"; |
| | | } |
| | |
| | | |
| | | private String workspaceId; |
| | | |
| | | private String bid; |
| | | private Integer waylineType; |
| | | |
| | | private String type; |
| | | private Integer taskType; |
| | | |
| | | private LocalDateTime executeTime; |
| | | |
| | | private LocalDateTime endTime; |
| | | |
| | | private Integer status; |
| | | |
| | | private Integer progress; |
| | | |
| | | private String username; |
| | | |
| | | private LocalDateTime updateTime; |
| | | private Integer code; |
| | | |
| | | private Integer rthAltitude; |
| | | |
| | | private Integer outOfControlAction; |
| | | |
| | | private Integer mediaCount; |
| | | } |
| | |
| | | @TableField("workspace_id") |
| | | private String workspaceId; |
| | | |
| | | @TableField("bid") |
| | | private String bid; |
| | | @TableField("task_type") |
| | | private Integer taskType; |
| | | |
| | | @TableField("type") |
| | | private String type; |
| | | @TableField("wayline_type") |
| | | private Integer waylineType; |
| | | |
| | | @TableField("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("rth_altitude") |
| | | private Integer rthAltitude; |
| | | |
| | | @TableField("out_of_control") |
| | | private Integer outOfControlAction; |
| | | |
| | | @TableField("media_count") |
| | | private Integer mediaCount; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| New file |
| | |
| | | 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); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/11/14 |
| | | */ |
| | | @Getter |
| | | public enum WaylineMethodEnum { |
| | | |
| | | FLIGHT_TASK_PREPARE("flighttask_prepare"), |
| | | |
| | | FLIGHT_TASK_EXECUTE("flighttask_execute"), |
| | | |
| | | FLIGHT_TASK_CANCEL("flighttask_undo"); |
| | | |
| | | private String method; |
| | | |
| | | WaylineMethodEnum(String method) { |
| | | this.method = method; |
| | | } |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.wayline.model.param; |
| | | |
| | | import lombok.Data; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | import javax.validation.constraints.NotBlank; |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | @Data |
| | | public class CreateJobParam { |
| | | |
| | | @NotBlank |
| | | private String name; |
| | | |
| | | @NotBlank |
| | | private String fileId; |
| | | |
| | | @NotBlank |
| | | private String dockSn; |
| | | |
| | | private String type; |
| | | @Range(max = 0) |
| | | @NotNull |
| | | private Integer waylineType; |
| | | |
| | | private boolean immediate; |
| | | @Range(max = 1) |
| | | @NotNull |
| | | private Integer taskType; |
| | | |
| | | private Long executeTime; |
| | | |
| | | @Range(min = 20, max = 500) |
| | | @NotNull |
| | | private Integer rthAltitude; |
| | | |
| | | @NotNull |
| | | @Range(max = 2) |
| | | private Integer outOfControlAction; |
| | | } |
| | |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.wayline.model.dto.WaylineFileDTO; |
| | | import com.dji.sample.wayline.model.param.WaylineQueryParam; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.net.URL; |
| | | import java.sql.SQLException; |
| | |
| | | * @param 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); |
| | | } |
| | |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.wayline.model.dto.WaylineJobDTO; |
| | | import com.dji.sample.wayline.model.param.CreateJobParam; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | |
| | | import java.sql.SQLException; |
| | | import java.util.Collection; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | |
| | | public interface IWaylineJobService { |
| | | |
| | | /** |
| | | * Create a wayline mission for the dock. |
| | | * Create wayline job in the database. |
| | | * @param param |
| | | * @param customClaim user info |
| | | * @param customClaim user info |
| | | * @return |
| | | */ |
| | | Boolean createJob(CreateJobParam param, CustomClaim customClaim) throws SQLException; |
| | | Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, CustomClaim customClaim); |
| | | |
| | | /** |
| | | * Issue wayline mission to the dock for execution. |
| | | * @param workspaceId |
| | | * @param jobId |
| | | * Issue wayline mission to the dock. |
| | | * @param param |
| | | * @param customClaim user info |
| | | * @return |
| | | */ |
| | | void publishFlightTask(String workspaceId, String jobId) throws SQLException; |
| | | ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException; |
| | | |
| | | /** |
| | | * Execute the task immediately. |
| | | * @param jobId |
| | | * @throws SQLException |
| | | * @return |
| | | */ |
| | | 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. |
| | |
| | | * @return |
| | | */ |
| | | 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); |
| | | } |
| | |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | 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.IWaylineJobService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.http.HttpStatus; |
| | | 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.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.Objects; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | @Autowired |
| | | private RedisOpsUtils redisOps; |
| | | |
| | | @Autowired |
| | | private IWaylineJobService waylineJobService; |
| | | |
| | | @Override |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS, outputChannel = ChannelName.OUTBOUND) |
| | | public void handleProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | EventsReceiver<FlightTaskProgressReceiver> eventsReceiver = mapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<FlightTaskProgressReceiver>>(){}); |
| | | 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) { |
| | | 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()) |
| | | .mediaCount(output.getExt().getMediaCount()) |
| | | .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()); |
| | | websocketMessageService.sendBatch( |
| | |
| | | .bid(receiver.getBid()) |
| | | .method(EventsMethodEnum.FLIGHT_TASK_PROGRESS.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(ResponseResult.success()) |
| | | .data(RequestsReply.success()) |
| | | .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); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.component.oss.model.OssConfiguration; |
| | | 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.model.dto.KmzFileProperties; |
| | | import com.dji.sample.wayline.model.dto.WaylineFileDTO; |
| | | import com.dji.sample.wayline.model.entity.WaylineFileEntity; |
| | | import com.dji.sample.wayline.model.param.WaylineQueryParam; |
| | | 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.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.DigestUtils; |
| | | 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.nio.charset.StandardCharsets; |
| | | import java.sql.SQLException; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | import java.util.UUID; |
| | | import java.util.*; |
| | | 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 |
| | |
| | | file.setWaylineId(UUID.randomUUID().toString()); |
| | | file.setWorkspaceId(workspaceId); |
| | | |
| | | byte[] object = ossService.getObject(configuration.getBucket(), metadata.getObjectKey()); |
| | | if (object.length == 0) { |
| | | throw new RuntimeException("The file " + metadata.getObjectKey() + |
| | | " does not exist in the bucket[" + configuration.getBucket() + "]."); |
| | | if (!StringUtils.hasText(file.getSign())) { |
| | | try (InputStream object = ossService.getObject(configuration.getBucket(), metadata.getObjectKey())) { |
| | | if (object.available() == 0) { |
| | | throw new RuntimeException("The file " + metadata.getObjectKey() + |
| | | " does not exist in the bucket[" + configuration.getBucket() + "]."); |
| | | } |
| | | file.setSign(DigestUtils.md5DigestAsHex(object)); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | file.setSign(DigestUtils.md5DigestAsHex(object)); |
| | | int insertId = mapper.insert(file); |
| | | return insertId > 0 ? file.getId() : insertId; |
| | | } |
| | |
| | | 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. |
| | | * @param entity |
| | |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(","))) |
| | | .favorited(file.getFavorited()) |
| | | .sign(file.getSign()) |
| | | .build(); |
| | | } |
| | | |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.dji.sample.common.error.CommonErrorEnum; |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.Pagination; |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.model.ServiceReply; |
| | | import com.dji.sample.component.mqtt.model.ServicesMethodEnum; |
| | | import com.dji.sample.component.mqtt.model.TopicConst; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.wayline.dao.IWaylineJobMapper; |
| | | import com.dji.sample.wayline.model.dto.FlightTaskCreateDTO; |
| | | 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.dto.*; |
| | | import com.dji.sample.wayline.model.entity.WaylineJobEntity; |
| | | import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineMethodEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineTaskTypeEnum; |
| | | import com.dji.sample.wayline.model.param.CreateJobParam; |
| | | import com.dji.sample.wayline.service.IWaylineFileService; |
| | | 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 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.transaction.annotation.Isolation; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.net.URL; |
| | | import java.sql.SQLException; |
| | | import java.time.Instant; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | import java.util.UUID; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | @Autowired |
| | | private RedisOpsUtils redisOps; |
| | | |
| | | @Autowired |
| | | private ObjectMapper objectMapper; |
| | | |
| | | |
| | | @Override |
| | | public Boolean createJob(CreateJobParam param, CustomClaim customClaim) throws SQLException { |
| | | if (param == null) { |
| | | return false; |
| | | public Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, CustomClaim customClaim) { |
| | | if (Objects.isNull(param)) { |
| | | return Optional.empty(); |
| | | } |
| | | // Immediate tasks, allocating time on the backend. |
| | | if (Objects.isNull(param.getExecuteTime())) { |
| | | param.setExecuteTime(System.currentTimeMillis()); |
| | | } |
| | | WaylineJobEntity jobEntity = WaylineJobEntity.builder() |
| | | .name(param.getName()) |
| | |
| | | .username(customClaim.getUsername()) |
| | | .workspaceId(customClaim.getWorkspaceId()) |
| | | .jobId(UUID.randomUUID().toString()) |
| | | .type(param.getType()) |
| | | .executeTime(param.getExecuteTime()) |
| | | .status(WaylineJobStatusEnum.PENDING.getVal()) |
| | | .taskType(param.getTaskType()) |
| | | .waylineType(param.getWaylineType()) |
| | | .outOfControlAction(param.getOutOfControlAction()) |
| | | .rthAltitude(param.getRthAltitude()) |
| | | .build(); |
| | | int id = mapper.insert(jobEntity); |
| | | if (id <= 0) { |
| | | return false; |
| | | return Optional.empty(); |
| | | } |
| | | if (param.isImmediate()) { |
| | | publishFlightTask(jobEntity.getWorkspaceId(), jobEntity.getJobId()); |
| | | } |
| | | return true; |
| | | return Optional.ofNullable(this.entity2Dto(jobEntity)); |
| | | } |
| | | |
| | | @Override |
| | | public void publishFlightTask(String workspaceId, String jobId) throws SQLException { |
| | | public ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException { |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.createWaylineJob(param, customClaim); |
| | | if (waylineJobOpt.isEmpty()) { |
| | | throw new SQLException("Failed to create wayline job."); |
| | | } |
| | | WaylineJobDTO waylineJob = waylineJobOpt.get(); |
| | | |
| | | boolean isOnline = deviceService.checkDeviceOnline(waylineJob.getDockSn()); |
| | | if (!isOnline) { |
| | | throw new RuntimeException("Dock is offline."); |
| | | } |
| | | |
| | | // get wayline file |
| | | Optional<WaylineFileDTO> waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId()); |
| | | if (waylineFile.isEmpty()) { |
| | | throw new SQLException("Wayline file doesn't exist."); |
| | | } |
| | | |
| | | // get file url |
| | | URL url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getWaylineId()); |
| | | |
| | | FlightTaskCreateDTO flightTask = FlightTaskCreateDTO.builder() |
| | | .flightId(waylineJob.getJobId()) |
| | | .executeTime(waylineJob.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) |
| | | .taskType(waylineJob.getTaskType()) |
| | | .waylineType(waylineJob.getWaylineType()) |
| | | .rthAltitude(waylineJob.getRthAltitude()) |
| | | .outOfControlAction(waylineJob.getOutOfControlAction()) |
| | | .file(FlightTaskFileDTO.builder() |
| | | .url(url.toString()) |
| | | .fingerprint(waylineFile.get().getSign()) |
| | | .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(WaylineMethodEnum.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."); |
| | | } |
| | | |
| | | long expire = redisOps.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + waylineJob.get().getDockSn()); |
| | | if (expire < 0) { |
| | | boolean isOnline = deviceService.checkDeviceOnline(waylineJob.get().getDockSn()); |
| | | if (!isOnline) { |
| | | throw new RuntimeException("Dock is offline."); |
| | | } |
| | | |
| | | // get wayline file |
| | | Optional<WaylineFileDTO> waylineFile = waylineFileService.getWaylineByWaylineId(workspaceId, waylineJob.get().getFileId()); |
| | | if (waylineFile.isEmpty()) { |
| | | throw new IllegalArgumentException("Wayline file doesn't exist."); |
| | | } |
| | | |
| | | // get file url |
| | | URL url = waylineFileService.getObjectUrl(workspaceId, waylineFile.get().getWaylineId()); |
| | | |
| | | WaylineJobDTO job = waylineJob.get(); |
| | | FlightTaskCreateDTO flightTask = FlightTaskCreateDTO.builder() |
| | | .flightId(jobId) |
| | | .type(job.getType()) |
| | | .file(FlightTaskFileDTO.builder() |
| | | .url(url.toString()) |
| | | .sign(waylineFile.get().getSign()) |
| | | .build()) |
| | | .build(); |
| | | FlightTaskCreateDTO flightTask = FlightTaskCreateDTO.builder().flightId(jobId).build(); |
| | | |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + |
| | | job.getDockSn() + TopicConst.SERVICES_SUF; |
| | | CommonTopicResponse<Object> response = CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(UUID.randomUUID().toString()) |
| | | .bid(jobId) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(flightTask) |
| | | .method(ServicesMethodEnum.FLIGHTTASK_CREATE.getMethod()) |
| | | .method(WaylineMethodEnum.FLIGHT_TASK_EXECUTE.getMethod()) |
| | | .build(); |
| | | |
| | | Optional<ServiceReply> serviceReplyOpt = messageSender.publishWithReply(topic, response); |
| | | if (serviceReplyOpt.isEmpty()) { |
| | | log.info("Timeout to receive reply."); |
| | | throw new RuntimeException("Timeout to receive reply."); |
| | | } |
| | | if (serviceReplyOpt.get().getResult() != 0) { |
| | | log.info("Error code: {}", serviceReplyOpt.get().getResult()); |
| | | throw new RuntimeException("Error code: " + serviceReplyOpt.get().getResult()); |
| | | ServiceReply serviceReply = messageSender.publishWithReply(topic, response); |
| | | if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { |
| | | log.info("Execute job ====> Error code: {}", serviceReply.getResult()); |
| | | this.updateJob(WaylineJobDTO.builder() |
| | | .jobId(jobId) |
| | | .status(WaylineJobStatusEnum.FAILED.getVal()) |
| | | .endTime(LocalDateTime.now()) |
| | | .code(serviceReply.getResult()).build()); |
| | | return false; |
| | | } |
| | | |
| | | job.setBid(response.getBid()); |
| | | boolean isUpd = this.updateJob(job); |
| | | if (!isUpd) { |
| | | throw new SQLException("Failed to update data."); |
| | | 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())); |
| | | } |
| | | |
| | | Set<String> ids = waylineJobs.stream().map(WaylineJobEntity::getJobId).collect(Collectors.toSet()); |
| | | 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)); |
| | | |
| | | } |
| | | |
| | | 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(WaylineMethodEnum.FLIGHT_TASK_CANCEL.getMethod()) |
| | | .build(); |
| | | |
| | | ServiceReply serviceReply = messageSender.publishWithReply(topic, response); |
| | | if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { |
| | | 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 |
| | |
| | | public Boolean updateJob(WaylineJobDTO dto) { |
| | | return mapper.update(this.dto2Entity(dto), |
| | | new LambdaUpdateWrapper<WaylineJobEntity>() |
| | | .eq(WaylineJobEntity::getWorkspaceId, dto.getWorkspaceId()) |
| | | .eq(WaylineJobEntity::getJobId, dto.getJobId())) |
| | | > 0; |
| | | .eq(WaylineJobEntity::getJobId, dto.getJobId())) > 0; |
| | | } |
| | | |
| | | @Override |
| | |
| | | Page<WaylineJobEntity> pageData = mapper.selectPage( |
| | | new Page<WaylineJobEntity>(page, pageSize), |
| | | new LambdaQueryWrapper<WaylineJobEntity>() |
| | | .eq(WaylineJobEntity::getWorkspaceId, workspaceId)); |
| | | .eq(WaylineJobEntity::getWorkspaceId, workspaceId) |
| | | .orderByDesc(WaylineJobEntity::getId)); |
| | | List<WaylineJobDTO> records = pageData.getRecords() |
| | | .stream() |
| | | .map(this::entity2Dto) |
| | |
| | | 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) { |
| | | WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder(); |
| | | if (dto == null) { |
| | | return builder.build(); |
| | | } |
| | | return builder.type(dto.getType()) |
| | | .bid(dto.getBid()) |
| | | if (Objects.nonNull(dto.getEndTime())) { |
| | | 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()) |
| | | .mediaCount(dto.getMediaCount()) |
| | | .name(dto.getJobName()) |
| | | .errorCode(dto.getCode()) |
| | | .build(); |
| | | } |
| | | |
| | |
| | | if (entity == null) { |
| | | return null; |
| | | } |
| | | return WaylineJobDTO.builder() |
| | | |
| | | WaylineJobDTO.WaylineJobDTOBuilder builder = WaylineJobDTO.builder() |
| | | .jobId(entity.getJobId()) |
| | | .bid(entity.getBid()) |
| | | .updateTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getUpdateTime()), ZoneId.systemDefault())) |
| | | .jobName(entity.getName()) |
| | | .fileId(entity.getFileId()) |
| | | .fileName(waylineFileService.getWaylineByWaylineId(entity.getWorkspaceId(), entity.getFileId()) |
| | |
| | | .orElse(DeviceDTO.builder().build()).getNickname()) |
| | | .username(entity.getUsername()) |
| | | .workspaceId(entity.getWorkspaceId()) |
| | | .type(entity.getType()) |
| | | .build(); |
| | | .status(entity.getStatus()) |
| | | .code(entity.getErrorCode()) |
| | | .executeTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getExecuteTime()), ZoneId.systemDefault())) |
| | | .taskType(entity.getTaskType()) |
| | | .waylineType(entity.getWaylineType()) |
| | | .rthAltitude(entity.getRthAltitude()) |
| | | .outOfControlAction(entity.getOutOfControlAction()) |
| | | .mediaCount(entity.getMediaCount()); |
| | | |
| | | 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(); |
| | | } |
| | | } |
| | |
| | | com.dji.sample: debug |
| | | file: |
| | | name: logs/cloud-api-sample.log |
| | | |
| | | ntp: |
| | | server: |
| | | host: Google.mzr.me |