From 56df98ce4952239fbf7d0e99dbeb0e5c71531d6f Mon Sep 17 00:00:00 2001
From: sean.zhou <sean.zhou@dji.com>
Date: Fri, 18 Nov 2022 18:29:06 +0800
Subject: [PATCH] initial v1.3.0

---
 src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java             |    3 
 src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java |   16 
 src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java                 |   29 
 src/main/java/com/dji/sample/manage/service/impl/RequestConfigContext.java                  |   56 +
 src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java                       |    2 
 src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java                     |   10 
 src/main/java/com/dji/sample/component/GlobalExceptionHandler.java                          |    7 
 src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java                        |   29 
 src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java               |   81 -
 src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java                  |    1 
 src/main/java/com/dji/sample/manage/model/enums/FirmwareMethodEnum.java                     |   21 
 src/main/java/com/dji/sample/wayline/model/enums/WaylineTemplateTypeEnum.java               |   26 
 src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java                |  347 +++++-
 src/main/java/com/dji/sample/media/model/CredentialsDTO.java                                |    8 
 src/main/java/com/dji/sample/manage/controller/DeviceController.java                        |   59 
 src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java                 |   20 
 src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java                           |   19 
 src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java                     |   29 
 src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java                  |   15 
 src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java                        |    6 
 src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java             |   30 
 src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java               |  109 +
 src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java                 |   33 
 src/main/java/com/dji/sample/manage/service/impl/CameraVideoServiceImpl.java                |    3 
 src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java                   |   26 
 src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java                   |    4 
 src/main/java/com/dji/sample/manage/service/ILiveStreamService.java                         |    7 
 src/main/java/com/dji/sample/manage/controller/LiveStreamController.java                    |    5 
 src/main/java/com/dji/sample/common/util/SpringBeanUtils.java                               |   30 
 src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java                       |    6 
 pom.xml                                                                                     |   26 
 src/main/java/com/dji/sample/common/util/JwtUtil.java                                       |   20 
 src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java           |   23 
 src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java                      |   46 
 src/main/java/com/dji/sample/control/model/enums/RemoteControlMethodEnum.java               |   81 +
 src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java                     |   53 
 src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java      |   13 
 src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java               |   46 
 src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java              |    6 
 src/main/java/com/dji/sample/manage/model/enums/LiveStreamMethodEnum.java                   |   29 
 src/main/java/com/dji/sample/manage/model/receiver/RequestConfigReceiver.java               |   16 
 src/main/java/com/dji/sample/manage/service/impl/ConfigProductServiceImpl.java              |   20 
 src/main/java/com/dji/sample/manage/model/receiver/MaintainStatusReceiver.java              |   20 
 src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java                |   79 +
 src/main/java/com/dji/sample/component/mqtt/model/ConfigScopeEnum.java                      |   32 
 src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java            |   40 
 src/main/java/com/dji/sample/wayline/model/enums/WaylineMethodEnum.java                     |   24 
 src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java                     |   10 
 src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java               |    4 
 src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java               |   20 
 src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java         |   57 +
 src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java                |    9 
 src/main/resources/application.yml                                                          |    4 
 src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java                 |   85 +
 src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java                              |    2 
 src/main/java/com/dji/sample/component/oss/service/IOssService.java                         |    5 
 src/main/java/com/dji/sample/manage/model/enums/StateSwitchReceiver.java                    |   30 
 src/main/java/com/dji/sample/component/mqtt/model/SetReply.java                             |   14 
 src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java             |    8 
 src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java                          |    6 
 src/main/java/com/dji/sample/manage/service/IRequestsConfigService.java                     |   15 
 sql/cloud_sample.sql                                                                        |   19 
 src/main/java/com/dji/sample/manage/model/enums/LogsFileMethodEnum.java                     |   26 
 src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java                     |  271 +++-
 src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java              |    2 
 src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java                  |   41 
 src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java                        |   25 
 api/Cloud API Demo.postman_collection.json                                                  |  120 ++
 src/main/java/com/dji/sample/common/error/LiveErrorEnum.java                                |    4 
 src/main/java/com/dji/sample/control/model/enums/BatteryStoreModeEnum.java                  |   29 
 src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java            |   27 
 src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java              |    7 
 src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java                  |    3 
 src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java                        |   41 
 src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java                   |   46 
 src/main/java/com/dji/sample/manage/model/common/NtpServerProperties.java                   |   20 
 src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java                |    2 
 src/main/java/com/dji/sample/component/redis/RedisConst.java                                |    4 
 src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java                       |   10 
 src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java                  |   19 
 src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java               |    8 
 src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java          |    2 
 src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java                           |    4 
 src/main/java/com/dji/sample/control/controller/DockController.java                         |   11 
 src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java                    |    5 
 src/main/java/com/dji/sample/manage/model/dto/NtpServerDTO.java                             |   18 
 src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java            |   38 
 src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java                             |   54 +
 src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java              |    3 
 src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java                       |   19 
 src/main/java/com/dji/sample/wayline/model/enums/WaylineJobStatusEnum.java                  |   39 
 src/main/java/com/dji/sample/control/service/IControlService.java                           |   15 
 src/main/java/com/dji/sample/manage/model/receiver/DeviceMaintainStatusReceiver.java        |   16 
 src/main/java/com/dji/sample/manage/service/IDeviceService.java                             |   37 
 src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java                         |    4 
 src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java                     |    1 
 src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java           |   62 +
 src/main/java/com/dji/sample/wayline/model/dto/KmzFileProperties.java                       |   39 
 src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java                          |    5 
 src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java               |    2 
 src/main/java/com/dji/sample/control/model/param/RemoteDebugParam.java                      |   15 
 /dev/null                                                                                   |   18 
 src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java                   |   22 
 src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java                       |   15 
 src/main/java/com/dji/sample/manage/service/impl/DeviceDictionaryServiceImpl.java           |    8 
 src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java                   |   18 
 106 files changed, 2,550 insertions(+), 524 deletions(-)

diff --git a/api/Cloud API Demo.postman_collection.json b/api/Cloud API Demo.postman_collection.json
index 9c96972..445ea40 100644
--- a/api/Cloud API Demo.postman_collection.json
+++ b/api/Cloud API Demo.postman_collection.json
@@ -227,6 +227,34 @@
 					"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",
@@ -703,6 +731,36 @@
 						}
 					},
 					"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": {
@@ -710,7 +768,7 @@
 				"apikey": [
 					{
 						"key": "value",
-						"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NjM1NTkxMTAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NjM2NDU1MTAsImlhdCI6MTY2MzU1OTExMCwidXNlcm5hbWUiOiJhZG1pblBDIn0.LG1JXZkuTdMaqnXn5WMJvnysNkHHbc4HLe_qZPWz_nM",
+						"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njc0NzcwNTUsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njc1NjM0NTUsImlhdCI6MTY2NzQ3NzA1NSwidXNlcm5hbWUiOiJhZG1pblBDIn0.VMJ0ZKn895uvkMDjg2fw3p4trVCUx9ltVFKeP7QmYpo",
 						"type": "string"
 					},
 					{
@@ -1322,6 +1380,37 @@
 						}
 					},
 					"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": {
@@ -1329,7 +1418,7 @@
 				"apikey": [
 					{
 						"key": "value",
-						"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTU0NDk2MDIsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTU1MzYwMDIsImlhdCI6MTY1NTQ0OTYwMiwidXNlcm5hbWUiOiJhZG1pblBDIn0.YZWHJ65Pl_DT2Ampxk0WC01KD_fNTm_rYVUBIHAZD-4",
+						"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg2NTk4MjYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg3NDYyMjYsImlhdCI6MTY2ODY1OTgyNiwidXNlcm5hbWUiOiJhZG1pblBDIn0.ykCpfJcReeb3etUzmNMQk1n0vaoDT6dl47J_aHRoTbU",
 						"type": "string"
 					},
 					{
@@ -1370,7 +1459,7 @@
 						"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"
@@ -1421,20 +1510,31 @@
 					"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
+								}
 							]
 						}
 					},
@@ -1446,7 +1546,7 @@
 				"apikey": [
 					{
 						"key": "value",
-						"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTU4OTA5NTQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTU5NzczNTQsImlhdCI6MTY1NTg5MDk1NCwidXNlcm5hbWUiOiJhZG1pblBDIn0.fd0iIzCd71LDUE6ixexUJvo-YqtnSCqRx-790snCyBI",
+						"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg2NTk4MjYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg3NDYyMjYsImlhdCI6MTY2ODY1OTgyNiwidXNlcm5hbWUiOiJhZG1pblBDIn0.ykCpfJcReeb3etUzmNMQk1n0vaoDT6dl47J_aHRoTbU",
 						"type": "string"
 					},
 					{
@@ -1506,7 +1606,7 @@
 				"apikey": [
 					{
 						"key": "value",
-						"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NjE5NTQwMTQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NjIwNDA0MTQsImlhdCI6MTY2MTk1NDAxNCwidXNlcm5hbWUiOiJhZG1pblBDIn0.GgCh575h2-HvYvdGZIKBW50r0F6CPACQn4ceAVzJfCU",
+						"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg0MzE5MzQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg1MTgzMzQsImlhdCI6MTY2ODQzMTkzNCwidXNlcm5hbWUiOiJhZG1pblBDIn0.QU9xHBeQPHJ2V1vXQcGGWRQ-gYEOWDpaTTXIQga85BU",
 						"type": "string"
 					},
 					{
diff --git a/pom.xml b/pom.xml
index e71dc5f..dd1207c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
 
 	<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>
@@ -156,6 +156,30 @@
 			<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>
diff --git a/sql/cloud_sample.sql b/sql/cloud_sample.sql
index 993aabd..47d2656 100644
--- a/sql/cloud_sample.sql
+++ b/sql/cloud_sample.sql
@@ -127,7 +127,12 @@
 	(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;
@@ -431,15 +436,21 @@
   `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 */;
diff --git a/src/main/java/com/dji/sample/common/error/LiveErrorEnum.java b/src/main/java/com/dji/sample/common/error/LiveErrorEnum.java
index 0a65eed..6ba3f95 100644
--- a/src/main/java/com/dji/sample/common/error/LiveErrorEnum.java
+++ b/src/main/java/com/dji/sample/common/error/LiveErrorEnum.java
@@ -67,9 +67,9 @@
      * @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;
             }
         }
diff --git a/src/main/java/com/dji/sample/common/util/JwtUtil.java b/src/main/java/com/dji/sample/common/util/JwtUtil.java
index 629cea1..918f1d7 100644
--- a/src/main/java/com/dji/sample/common/util/JwtUtil.java
+++ b/src/main/java/com/dji/sample/common/util/JwtUtil.java
@@ -2,7 +2,6 @@
 
 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;
@@ -85,14 +84,7 @@
      * @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);
     }
 
     /**
@@ -101,7 +93,13 @@
      * @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()));
     }
 }
diff --git a/src/main/java/com/dji/sample/common/util/SpringBeanUtils.java b/src/main/java/com/dji/sample/common/util/SpringBeanUtils.java
new file mode 100644
index 0000000..23e8966
--- /dev/null
+++ b/src/main/java/com/dji/sample/common/util/SpringBeanUtils.java
@@ -0,0 +1,30 @@
+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);
+    }
+}
diff --git a/src/main/java/com/dji/sample/component/GlobalExceptionHandler.java b/src/main/java/com/dji/sample/component/GlobalExceptionHandler.java
index 1d5eb14..b0a9fbf 100644
--- a/src/main/java/com/dji/sample/component/GlobalExceptionHandler.java
+++ b/src/main/java/com/dji/sample/component/GlobalExceptionHandler.java
@@ -1,6 +1,7 @@
 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;
@@ -30,4 +31,10 @@
         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());
+    }
+
 }
diff --git a/src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java b/src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java
index 4b278a0..51b138d 100644
--- a/src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java
+++ b/src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java
@@ -152,4 +152,19 @@
         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();
+    }
+
 }
diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java b/src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java
index 3047ea5..8aa9112 100644
--- a/src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java
+++ b/src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java
@@ -1,6 +1,8 @@
 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;
@@ -10,12 +12,8 @@
 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.*;
 
 /**
  *
@@ -26,48 +24,6 @@
 @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.
@@ -81,38 +37,11 @@
         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);
     }
 }
diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java
new file mode 100644
index 0000000..9b503eb
--- /dev/null
+++ b/src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java
@@ -0,0 +1,40 @@
+package com.dji.sample.component.mqtt.handler;
+
+import com.dji.sample.component.mqtt.model.Chan;
+import com.dji.sample.component.mqtt.model.ChannelName;
+import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.messaging.Message;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+/**
+ * @author sean
+ * @version 1.2
+ * @date 2022/9/9
+ */
+@Component
+public class PropertySetReplyHandler {
+
+    @Autowired
+    private ObjectMapper mapper;
+
+    /**
+     * Handle the reply message from the pilot side to the on-demand video.
+     * @param message   reply message
+     * @throws IOException
+     */
+    @ServiceActivator(inputChannel = ChannelName.INBOUND_PROPERTY_SET_REPLY)
+    public void serviceReply(Message<?> message) throws IOException {
+        byte[] payload = (byte[])message.getPayload();
+
+        CommonTopicReceiver receiver = mapper.readValue(payload, new TypeReference<CommonTopicReceiver>() {});
+        Chan<CommonTopicReceiver<?>> chan = Chan.getInstance();
+        // Put the message to the chan object.
+        chan.put(receiver);
+    }
+}
diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java b/src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java
index 753bad2..95e0f7d 100644
--- a/src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java
+++ b/src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java
@@ -11,6 +11,7 @@
 import org.springframework.integration.dsl.IntegrationFlows;
 
 import java.io.IOException;
+import java.util.Arrays;
 
 /**
  * @author sean
@@ -37,13 +38,8 @@
                 })
                 .<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();
     }
 }
diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java
index 1851fd9..c0115ab 100644
--- a/src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java
+++ b/src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java
@@ -1,6 +1,10 @@
 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;
@@ -32,7 +36,7 @@
         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 {
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java b/src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java
index 83913c4..cb5a0f0 100644
--- a/src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java
+++ b/src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java
@@ -68,4 +68,10 @@
     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";
 }
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java b/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java
index 8770bfc..6090338 100644
--- a/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java
+++ b/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java
@@ -31,5 +31,4 @@
 
     private Integer needReply;
 
-    private String from;
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/ConfigScopeEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/ConfigScopeEnum.java
new file mode 100644
index 0000000..7cd4479
--- /dev/null
+++ b/src/main/java/com/dji/sample/component/mqtt/model/ConfigScopeEnum.java
@@ -0,0 +1,32 @@
+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();
+    }
+}
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java
new file mode 100644
index 0000000..00ab2a7
--- /dev/null
+++ b/src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java
@@ -0,0 +1,46 @@
+package com.dji.sample.component.mqtt.model;
+
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+import static com.dji.sample.component.mqtt.model.TopicConst.*;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/28
+ */
+@Getter
+public enum DeviceTopicEnum {
+
+    STATUS(Pattern.compile("^" + BASIC_PRE + PRODUCT + REGEX_SN + STATUS_SUF + "$"), ChannelName.INBOUND_STATUS),
+
+    STATE(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + STATE_SUF + "$"), ChannelName.INBOUND_STATE),
+
+    SERVICE_REPLY(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + SERVICES_SUF + _REPLY_SUF + "$"), ChannelName.INBOUND_SERVICE_REPLY),
+
+    OSD(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + OSD_SUF + "$"), ChannelName.INBOUND_OSD),
+
+    REQUESTS(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + REQUESTS_SUF + "$"), ChannelName.INBOUND_REQUESTS),
+
+    EVENTS(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + EVENTS_SUF + "$"), ChannelName.INBOUND_EVENTS),
+
+    PROPERTY_SET_REPLY(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + PROPERTY_SUF + SET_SUF + _REPLY_SUF + "$"), ChannelName.INBOUND_PROPERTY_SET_REPLY),
+
+    UNKNOWN(null, ChannelName.DEFAULT);
+
+    Pattern pattern;
+
+    String beanName;
+
+    DeviceTopicEnum(Pattern pattern, String beanName) {
+        this.pattern = pattern;
+        this.beanName = beanName;
+    }
+
+    public static DeviceTopicEnum find(String topic) {
+        return Arrays.stream(DeviceTopicEnum.values()).filter(topicEnum -> topicEnum.pattern.matcher(topic).matches()).findAny().orElse(UNKNOWN);
+    }
+}
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java b/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java
index c4d26aa..1597e21 100644
--- a/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java
+++ b/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java
@@ -1,7 +1,10 @@
 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
@@ -10,6 +13,9 @@
  */
 @Data
 @JsonIgnoreProperties(ignoreUnknown = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 public class EventsReceiver<T> {
 
     private Integer result;
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java
index 321e2f8..dd37ac6 100644
--- a/src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java
+++ b/src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java
@@ -28,6 +28,8 @@
 
     TIMEOUT("timeout", true),
 
+    PARTIALLY_DONE("partially_done", true),
+
     UNKNOWN("unknown", false);
 
     String desc;
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java b/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java
index f13ec92..242fd3d 100644
--- a/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java
+++ b/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java
@@ -28,4 +28,9 @@
     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";
+
 }
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java
index ddfa599..8d04ceb 100644
--- a/src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java
+++ b/src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java
@@ -1,5 +1,7 @@
 package com.dji.sample.component.mqtt.model;
 
+import lombok.Getter;
+
 import java.util.Arrays;
 
 /**
@@ -7,26 +9,30 @@
  * @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) {
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/ServicesMethodEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/ServicesMethodEnum.java
deleted file mode 100644
index 40ca0fc..0000000
--- a/src/main/java/com/dji/sample/component/mqtt/model/ServicesMethodEnum.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package com.dji.sample.component.mqtt.model;
-
-import java.util.Arrays;
-
-/**
- * @author sean.zhou
- * @date 2021/11/22
- * @version 0.1
- */
-public enum ServicesMethodEnum {
-
-    LIVE_START_PUSH("live_start_push", false),
-
-    LIVE_STOP_PUSH("live_stop_push", false),
-
-    LIVE_SET_QUALITY("live_set_quality", false),
-
-    FLIGHTTASK_CREATE("flighttask_create", false),
-
-    DEBUG_MODE_OPEN("debug_mode_open", false),
-
-    DEBUG_MODE_CLOSE("debug_mode_close", false),
-
-    SUPPLEMENT_LIGHT_OPEN("supplement_light_open", false),
-
-    SUPPLEMENT_LIGHT_CLOSE("supplement_light_close", false),
-
-    RETURN_HOME("return_home", false),
-
-    SDR_WORKMODE_SWITCH("sdr_workmode_switch", false),
-
-    DEVICE_REBOOT("device_reboot", true),
-
-    DRONE_OPEN("drone_open", true),
-
-    DRONE_CLOSE("drone_close", true),
-
-    DEVICE_CHECK("device_check", true),
-
-    DRONE_FORMAT("drone_format", true),
-
-    DEVICE_FORMAT("device_format", true),
-
-    COVER_OPEN("cover_open", true),
-
-    COVER_CLOSE("cover_close", true),
-
-    PUTTER_OPEN("putter_open", true),
-
-    PUTTER_CLOSE("putter_close", true),
-
-    CHARGE_OPEN("charge_open", true),
-
-    CHARGE_CLOSE("charge_close", true),
-
-    OTA_CREATE("ota_create", true),
-
-    FILE_UPLOAD_LIST("fileupload_list", false),
-
-    FILE_UPLOAD_START("fileupload_start", true),
-
-    FILE_UPLOAD_UPDATE("fileupload_update", false),
-
-    UNKNOWN("unknown", false);
-
-    private String method;
-
-    private Boolean progress;
-
-    ServicesMethodEnum(String method, Boolean progress) {
-        this.method = method;
-        this.progress = progress;
-    }
-
-    public static ServicesMethodEnum find(String method) {
-        return Arrays.stream(ServicesMethodEnum.values())
-                .filter(methodEnum -> methodEnum.method.equals(method))
-                .findAny()
-                .orElse(UNKNOWN);
-    }
-
-    public String getMethod() {
-        return method;
-    }
-
-    public Boolean getProgress() {
-        return progress;
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/SetReply.java b/src/main/java/com/dji/sample/component/mqtt/model/SetReply.java
new file mode 100644
index 0000000..2c7f453
--- /dev/null
+++ b/src/main/java/com/dji/sample/component/mqtt/model/SetReply.java
@@ -0,0 +1,14 @@
+package com.dji.sample.component.mqtt.model;
+
+import lombok.Data;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/28
+ */
+@Data
+public class SetReply {
+
+    private Integer result;
+}
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java
new file mode 100644
index 0000000..5a127e4
--- /dev/null
+++ b/src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java
@@ -0,0 +1,30 @@
+package com.dji.sample.component.mqtt.model;
+
+import lombok.Getter;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/28
+ */
+@Getter
+public enum SetReplyStatusResultEnum {
+
+    SUCCESS(0, "success"),
+
+    FAILED(1, "failed"),
+
+    TIMEOUT(2, "timeout"),
+
+    UNKNOWN(-1, "unknown");
+
+    int val;
+
+    String desc;
+
+    SetReplyStatusResultEnum(int val, String desc) {
+        this.val = val;
+        this.desc = desc;
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java b/src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java
index dc9df7c..215995a 100644
--- a/src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java
+++ b/src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java
@@ -28,6 +28,10 @@
 
     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]+";
 
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java b/src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java
index 15079a6..40f7f18 100644
--- a/src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java
+++ b/src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java
@@ -3,8 +3,6 @@
 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
@@ -33,7 +31,7 @@
      * @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.
@@ -44,5 +42,5 @@
      * @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);
 }
diff --git a/src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java b/src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java
index 10bb7e2..89e5093 100644
--- a/src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java
+++ b/src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java
@@ -12,7 +12,6 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.Optional;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -49,28 +48,28 @@
         }
     }
 
-    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.");
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/component/oss/service/IOssService.java b/src/main/java/com/dji/sample/component/oss/service/IOssService.java
index 53746ef..f8441dd 100644
--- a/src/main/java/com/dji/sample/component/oss/service/IOssService.java
+++ b/src/main/java/com/dji/sample/component/oss/service/IOssService.java
@@ -2,6 +2,7 @@
 
 import com.dji.sample.media.model.CredentialsDTO;
 
+import java.io.InputStream;
 import java.net.URL;
 
 /**
@@ -41,5 +42,7 @@
      * @param objectKey
      * @return
      */
-    byte[] getObject(String bucket, String objectKey);
+    InputStream getObject(String bucket, String objectKey);
+
+    void putObject(String bucket, String objectKey, InputStream input);
 }
diff --git a/src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java b/src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java
index d709b07..d855261 100644
--- a/src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java
+++ b/src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java
@@ -4,6 +4,9 @@
 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;
@@ -93,18 +96,30 @@
     }
 
     @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() {
diff --git a/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java b/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java
index f2bcdc5..b9cdfd3 100644
--- a/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java
+++ b/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java
@@ -5,9 +5,7 @@
 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;
@@ -18,6 +16,7 @@
 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;
 
@@ -34,6 +33,7 @@
  * @version 1.0
  * @date 2022/4/27
  */
+@Slf4j
 @Service
 public class AmazonS3ServiceImpl implements IOssService {
 
@@ -83,18 +83,29 @@
         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() {
diff --git a/src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java b/src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java
index 009a1fb..b162751 100644
--- a/src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java
+++ b/src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java
@@ -4,10 +4,7 @@
 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;
@@ -15,11 +12,13 @@
 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
@@ -29,6 +28,8 @@
 @Service
 @Slf4j
 public class MinIOServiceImpl implements IOssService {
+
+    private MinioClient client;
 
     @Autowired
     private OssConfiguration configuration;
@@ -87,21 +88,44 @@
     }
 
     @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;
     }
 }
diff --git a/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java b/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java
index 9ac8059..e131889 100644
--- a/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java
+++ b/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java
@@ -7,6 +7,7 @@
 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;
@@ -52,7 +53,11 @@
         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);
+    }
 }
diff --git a/src/main/java/com/dji/sample/component/redis/RedisConst.java b/src/main/java/com/dji/sample/component/redis/RedisConst.java
index 718ebee..1fce89d 100644
--- a/src/main/java/com/dji/sample/component/redis/RedisConst.java
+++ b/src/main/java/com/dji/sample/component/redis/RedisConst.java
@@ -34,4 +34,8 @@
     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;
 }
diff --git a/src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java b/src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java
index f5b1066..5e329c9 100644
--- a/src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java
+++ b/src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java
@@ -3,6 +3,7 @@
 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;
@@ -184,4 +185,57 @@
     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);
+    }
+
 }
diff --git a/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java b/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java
index 9887d1e..eee6f48 100644
--- a/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java
+++ b/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java
@@ -35,6 +35,7 @@
         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);
diff --git a/src/main/java/com/dji/sample/control/controller/DockController.java b/src/main/java/com/dji/sample/control/controller/DockController.java
index 8b05ff8..7836e5f 100644
--- a/src/main/java/com/dji/sample/control/controller/DockController.java
+++ b/src/main/java/com/dji/sample/control/controller/DockController.java
@@ -1,13 +1,11 @@
 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
@@ -24,7 +22,8 @@
 
     @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);
     }
 }
diff --git a/src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java b/src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java
new file mode 100644
index 0000000..eaf833f
--- /dev/null
+++ b/src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java
@@ -0,0 +1,29 @@
+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();
+    }
+}
diff --git a/src/main/java/com/dji/sample/control/model/enums/BatteryStoreModeEnum.java b/src/main/java/com/dji/sample/control/model/enums/BatteryStoreModeEnum.java
new file mode 100644
index 0000000..9b3d0a8
--- /dev/null
+++ b/src/main/java/com/dji/sample/control/model/enums/BatteryStoreModeEnum.java
@@ -0,0 +1,29 @@
+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();
+    }
+}
diff --git a/src/main/java/com/dji/sample/control/model/enums/RemoteControlMethodEnum.java b/src/main/java/com/dji/sample/control/model/enums/RemoteControlMethodEnum.java
new file mode 100644
index 0000000..f7350f7
--- /dev/null
+++ b/src/main/java/com/dji/sample/control/model/enums/RemoteControlMethodEnum.java
@@ -0,0 +1,81 @@
+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);
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/control/model/param/RemoteDebugParam.java b/src/main/java/com/dji/sample/control/model/param/RemoteDebugParam.java
new file mode 100644
index 0000000..4d48659
--- /dev/null
+++ b/src/main/java/com/dji/sample/control/model/param/RemoteDebugParam.java
@@ -0,0 +1,15 @@
+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;
+
+}
diff --git a/src/main/java/com/dji/sample/control/service/IControlService.java b/src/main/java/com/dji/sample/control/service/IControlService.java
index 7f2de02..f04c138 100644
--- a/src/main/java/com/dji/sample/control/service/IControlService.java
+++ b/src/main/java/com/dji/sample/control/service/IControlService.java
@@ -2,6 +2,7 @@
 
 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;
 
 /**
@@ -11,8 +12,20 @@
  */
 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);
 
 }
diff --git a/src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java b/src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java
index 2b0bbf4..511416d 100644
--- a/src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java
+++ b/src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java
@@ -1,5 +1,6 @@
 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;
@@ -8,9 +9,13 @@
 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;
@@ -20,7 +25,7 @@
 import org.springframework.messaging.MessageHeaders;
 import org.springframework.stereotype.Service;
 
-import java.util.Optional;
+import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -45,37 +50,50 @@
     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);
         }
@@ -126,7 +144,7 @@
                             .bid(receiver.getBid())
                             .method(receiver.getMethod())
                             .timestamp(System.currentTimeMillis())
-                            .data(ResponseResult.success())
+                            .data(RequestsReply.success())
                             .build());
         }
     }
diff --git a/src/main/java/com/dji/sample/manage/controller/DeviceController.java b/src/main/java/com/dji/sample/manage/controller/DeviceController.java
index e9d80c0..602e8c9 100644
--- a/src/main/java/com/dji/sample/manage/controller/DeviceController.java
+++ b/src/main/java/com/dji/sample/manage/controller/DeviceController.java
@@ -1,21 +1,20 @@
 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;
@@ -34,9 +33,6 @@
     @Autowired
     private IDeviceService deviceService;
 
-    @Autowired
-    private ISendMessageService sendMessageService;
-
     /**
      * Handles the message that the drone goes online.
      * @param receiver  The drone information is not empty.
@@ -50,6 +46,8 @@
                     CommonTopicResponse.builder()
                             .tid(receiver.getTid())
                             .bid(receiver.getBid())
+                            .timestamp(System.currentTimeMillis())
+                            .method(receiver.getMethod())
                             .build());
         }
     }
@@ -61,13 +59,15 @@
     @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());
 
         }
@@ -83,26 +83,6 @@
         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);
     }
 
     /**
@@ -186,4 +166,27 @@
                                        @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();
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java b/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java
index 6154e94..c8bd144 100644
--- a/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java
+++ b/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java
@@ -91,4 +91,9 @@
         return liveStreamService.liveSetQuality(liveParam);
     }
 
+    @PostMapping("/streams/switch")
+    public ResponseResult liveLensChange(@RequestBody LiveTypeDTO liveParam) {
+        return liveStreamService.liveLensChange(liveParam);
+    }
+
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/manage/model/common/NtpServerProperties.java b/src/main/java/com/dji/sample/manage/model/common/NtpServerProperties.java
new file mode 100644
index 0000000..7e562f3
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/common/NtpServerProperties.java
@@ -0,0 +1,20 @@
+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;
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java b/src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java
index 9366472..531e7ec 100644
--- a/src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java
+++ b/src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java
@@ -5,6 +5,8 @@
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.util.List;
+
 /**
  * @author sean.zhou
  * @date 2021/11/22
@@ -21,4 +23,6 @@
     private String index;
 
     private String type;
+
+    private List<String> switchVideoTypes;
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java b/src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java
index ffe341e..4a385ec 100644
--- a/src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java
+++ b/src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java
@@ -23,4 +23,6 @@
     @JsonProperty("video_quality")
     private Integer videoQuality;
 
+    private String videoType;
+
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/manage/model/dto/NtpServerDTO.java b/src/main/java/com/dji/sample/manage/model/dto/NtpServerDTO.java
new file mode 100644
index 0000000..27b05a5
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/dto/NtpServerDTO.java
@@ -0,0 +1,18 @@
+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;
+}
diff --git a/src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java b/src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java
new file mode 100644
index 0000000..38f9428
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java
@@ -0,0 +1,41 @@
+package com.dji.sample.manage.model.enums;
+
+import com.dji.sample.manage.model.receiver.BasicDeviceProperty;
+import com.dji.sample.manage.model.receiver.DistanceLimitStatusReceiver;
+import com.dji.sample.manage.model.receiver.HeightLimitReceiver;
+import com.dji.sample.manage.model.receiver.ObstacleAvoidanceReceiver;
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/27
+ */
+@Getter
+public enum DeviceSetPropertyEnum {
+
+    NIGHT_LIGHTS_STATE("night_lights_state", StateSwitchReceiver.class),
+
+    HEIGHT_LIMIT("height_limit", HeightLimitReceiver.class),
+
+    DISTANCE_LIMIT_STATUS("distance_limit_status", DistanceLimitStatusReceiver.class),
+
+    OBSTACLE_AVOIDANCE("obstacle_avoidance", ObstacleAvoidanceReceiver.class);
+
+
+    String property;
+
+    Class<? extends BasicDeviceProperty> clazz;
+
+    DeviceSetPropertyEnum(String property, Class<? extends BasicDeviceProperty> clazz) {
+        this.property = property;
+        this.clazz = clazz;
+    }
+
+    public static Optional<DeviceSetPropertyEnum> find(String property) {
+        return Arrays.stream(DeviceSetPropertyEnum.values()).filter(propertyEnum -> propertyEnum.property.equals(property)).findAny();
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/model/enums/FirmwareMethodEnum.java b/src/main/java/com/dji/sample/manage/model/enums/FirmwareMethodEnum.java
new file mode 100644
index 0000000..b185546
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/enums/FirmwareMethodEnum.java
@@ -0,0 +1,21 @@
+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;
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/manage/model/enums/LiveStreamMethodEnum.java b/src/main/java/com/dji/sample/manage/model/enums/LiveStreamMethodEnum.java
new file mode 100644
index 0000000..b49cdc1
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/enums/LiveStreamMethodEnum.java
@@ -0,0 +1,29 @@
+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;
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/manage/model/enums/LogsFileMethodEnum.java b/src/main/java/com/dji/sample/manage/model/enums/LogsFileMethodEnum.java
new file mode 100644
index 0000000..42b1b87
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/enums/LogsFileMethodEnum.java
@@ -0,0 +1,26 @@
+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;
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/model/enums/StateSwitchReceiver.java b/src/main/java/com/dji/sample/manage/model/enums/StateSwitchReceiver.java
new file mode 100644
index 0000000..27fb2a9
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/enums/StateSwitchReceiver.java
@@ -0,0 +1,30 @@
+package com.dji.sample.manage.model.enums;
+
+import com.dji.sample.manage.model.receiver.BasicDeviceProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Objects;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/28
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class StateSwitchReceiver extends BasicDeviceProperty {
+
+    public static final int DISABLE = 0;
+
+    public static final int ENABLE = 1;
+
+    private Integer value;
+
+    @Override
+    public boolean valid() {
+        return Objects.nonNull(this.value) && (this.value == DISABLE || this.value == ENABLE);
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java
index bd69415..fa6924e 100644
--- a/src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java
+++ b/src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java
@@ -15,4 +15,6 @@
     private Double longitude;
 
     private Double safeLandHeight;
+
+    private Integer isConfigured;
 }
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java
new file mode 100644
index 0000000..257316d
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java
@@ -0,0 +1,20 @@
+package com.dji.sample.manage.model.receiver;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/11/3
+ */
+@Data
+public class BackupBatteryReceiver {
+
+    private Integer voltage;
+
+    private Float temperature;
+
+    @JsonProperty("switch")
+    private Integer batterySwitch;
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java b/src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java
new file mode 100644
index 0000000..b232ce8
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java
@@ -0,0 +1,20 @@
+package com.dji.sample.manage.model.receiver;
+
+import lombok.Data;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/27
+ */
+@Data
+public class BasicDeviceProperty {
+
+    public boolean valid() {
+        return false;
+    }
+
+    public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) {
+        return valid();
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java
index 616b5a9..6c17c15 100644
--- a/src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java
+++ b/src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java
@@ -4,6 +4,8 @@
 import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * @author sean.zhou
  * @date 2021/11/18
@@ -16,4 +18,6 @@
     private String videoIndex;
 
     private String videoType;
+
+    private List<String> switchableVideoTypes;
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DeviceMaintainStatusReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DeviceMaintainStatusReceiver.java
new file mode 100644
index 0000000..05e3ed4
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/DeviceMaintainStatusReceiver.java
@@ -0,0 +1,16 @@
+package com.dji.sample.manage.model.receiver;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/11/3
+ */
+@Data
+public class DeviceMaintainStatusReceiver {
+
+    private List<MaintainStatusReceiver> maintainStatusArray;
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java
new file mode 100644
index 0000000..72129c7
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java
@@ -0,0 +1,57 @@
+package com.dji.sample.manage.model.receiver;
+
+import com.dji.sample.manage.model.enums.StateSwitchReceiver;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Objects;
+
+/**
+ * The state of the drone's limited distance
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/27
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class DistanceLimitStatusReceiver extends BasicDeviceProperty {
+
+    private Integer state;
+
+    private Integer distanceLimit;
+
+    private static final int DISTANCE_MAX = 8000;
+
+    private static final int DISTANCE_MIN = 15;
+
+    @Override
+    public boolean valid() {
+        boolean valid = Objects.nonNull(state) || Objects.nonNull(distanceLimit);
+        if (Objects.nonNull(state)) {
+            valid = new StateSwitchReceiver(this.state).valid();
+        }
+        if (Objects.nonNull(distanceLimit)) {
+            valid &= distanceLimit >= DISTANCE_MIN && distanceLimit <= DISTANCE_MAX;
+        }
+        return valid;
+    }
+
+    @Override
+    public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) {
+        DistanceLimitStatusReceiver distanceLimitStatus = osd.getDistanceLimitStatus();
+        switch (fieldName) {
+            case "state":
+                return Objects.isNull(distanceLimitStatus.getState()) ||
+                        Objects.nonNull(distanceLimitStatus.getState()) &&
+                                distanceLimitStatus.getState().intValue() != this.state;
+            case "distance_limit":
+                return Objects.isNull(distanceLimitStatus.getDistanceLimit()) ||
+                        Objects.nonNull(distanceLimitStatus.getDistanceLimit()) &&
+                                distanceLimitStatus.getDistanceLimit().intValue() != this.distanceLimit;
+            default:
+                throw new RuntimeException("Property " + fieldName + " does not exist.");
+        }
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java
new file mode 100644
index 0000000..83bfb3f
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java
@@ -0,0 +1,38 @@
+package com.dji.sample.manage.model.receiver;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/11/3
+ */
+@Data
+public class DockWirelessLinkReceiver {
+
+    @JsonProperty("4g_freq_band")
+    private Float fourGFreqBand;
+
+    @JsonProperty("4g_gnd_quality")
+    private Integer fourGGndQuality;
+
+    @JsonProperty("4g_link_state")
+    private Integer fourGLinkState;
+
+    @JsonProperty("4g_quality")
+    private Integer fourGQuality;
+
+    @JsonProperty("4g_uav_quality")
+    private Integer fourGUavQuality;
+
+    private Integer dongleNumber;
+
+    private Integer linkWorkmode;
+
+    private Float sdrFreqBand;
+
+    private Integer sdrLinkState;
+
+    private Integer sdrQuality;
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java
new file mode 100644
index 0000000..1718aca
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java
@@ -0,0 +1,16 @@
+package com.dji.sample.manage.model.receiver;
+
+import lombok.Data;
+
+/**
+ * @author sean
+ * @version 1.4
+ * @date 2022/11/3
+ */
+@Data
+public class DroneBatteryMaintenanceInfoReceiver {
+
+    private Integer maintenanceState;
+
+    private Long maintenanceTimeLeft;
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java
new file mode 100644
index 0000000..7a07565
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java
@@ -0,0 +1,29 @@
+package com.dji.sample.manage.model.receiver;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Objects;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/28
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class HeightLimitReceiver extends BasicDeviceProperty {
+
+    private static final int HEIGHT_LIMIT_MAX = 1500;
+
+    private static final int HEIGHT_LIMIT_MIN = 20;
+
+    private Integer value;
+
+    @Override
+    public boolean valid() {
+        return Objects.nonNull(this.value) && this.value >= HEIGHT_LIMIT_MIN && this.value <= HEIGHT_LIMIT_MAX;
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/MaintainStatusReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/MaintainStatusReceiver.java
new file mode 100644
index 0000000..7896f31
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/MaintainStatusReceiver.java
@@ -0,0 +1,20 @@
+package com.dji.sample.manage.model.receiver;
+
+import lombok.Data;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/11/3
+ */
+@Data
+public class MaintainStatusReceiver {
+
+    private Integer state;
+
+    private Integer lastMaintainType;
+
+    private Long lastMaintainTime;
+
+    private Long lastMaintainWorkSorties;
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java
index 84698cc..2aac8ae 100644
--- a/src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java
+++ b/src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java
@@ -14,5 +14,5 @@
 
     private Integer quality;
 
-    private float rate;
+    private Float rate;
 }
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java
new file mode 100644
index 0000000..e40a341
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java
@@ -0,0 +1,62 @@
+package com.dji.sample.manage.model.receiver;
+
+import com.dji.sample.manage.model.enums.StateSwitchReceiver;
+import lombok.Data;
+
+import java.util.Objects;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/27
+ */
+@Data
+public class ObstacleAvoidanceReceiver extends BasicDeviceProperty {
+
+    private Integer horizon;
+
+    private Integer upside;
+
+    private Integer downside;
+
+    @Override
+    public boolean valid() {
+        boolean valid = Objects.nonNull(this.horizon) || Objects.nonNull(this.upside) || Objects.nonNull(this.downside);
+
+        StateSwitchReceiver stateSwitch = new StateSwitchReceiver();
+        if (Objects.nonNull(this.horizon)) {
+            stateSwitch.setValue(this.horizon);
+            valid = stateSwitch.valid();
+        }
+        if (Objects.nonNull(this.upside)) {
+            stateSwitch.setValue(this.upside);
+            valid &= stateSwitch.valid();
+        }
+        if (Objects.nonNull(this.downside)) {
+            stateSwitch.setValue(this.downside);
+            valid &= stateSwitch.valid();
+        }
+        return valid;
+    }
+
+    @Override
+    public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) {
+        ObstacleAvoidanceReceiver obstacleAvoidance = osd.getObstacleAvoidance();
+        switch (fieldName) {
+            case "horizon":
+                return Objects.isNull(obstacleAvoidance.getHorizon()) ||
+                        Objects.nonNull(obstacleAvoidance.getHorizon()) &&
+                                obstacleAvoidance.getHorizon().intValue() != this.horizon;
+            case "upside":
+                return Objects.isNull(obstacleAvoidance.getUpside()) ||
+                        Objects.nonNull(obstacleAvoidance.getUpside()) &&
+                                obstacleAvoidance.getUpside().intValue() != this.upside;
+            case "downside":
+                return Objects.isNull(obstacleAvoidance.getDownside()) ||
+                        Objects.nonNull(obstacleAvoidance.getDownside()) &&
+                                obstacleAvoidance.getDownside().intValue() != this.downside;
+            default:
+                throw new RuntimeException("Property " + fieldName + " does not exist.");
+        }
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java
index ebd8b34..0cd5196 100644
--- a/src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java
+++ b/src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java
@@ -4,8 +4,8 @@
 
 /**
  * @author sean
- * @version 1.0
- * @date 2022/5/11
+ * @version 1.3
+ * @date 2022/11/3
  */
 @Data
 public class OsdDockReceiver {
@@ -22,8 +22,6 @@
 
     private Float environmentTemperature;
 
-    private Integer environmentHumidity;
-
     private Float temperature;
 
     private Integer humidity;
@@ -36,23 +34,11 @@
 
     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;
 
@@ -60,8 +46,43 @@
 
     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;
 }
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/OsdDockTransmissionReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OsdDockTransmissionReceiver.java
deleted file mode 100644
index cdd9abf..0000000
--- a/src/main/java/com/dji/sample/manage/model/receiver/OsdDockTransmissionReceiver.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.dji.sample.manage.model.receiver;
-
-import lombok.Data;
-
-/**
- * @author sean
- * @version 1.1
- * @date 2022/6/17
- */
-@Data
-public class OsdDockTransmissionReceiver {
-
-    private Integer flighttaskStepCode;
-
-    private DockMediaFileDetailReceiver mediaFileDetail;
-
-    private DockSdrReceiver sdr;
-}
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java
index 2cb54df..b39fb09 100644
--- a/src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java
+++ b/src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java
@@ -58,4 +58,13 @@
     private List<OsdPayloadReceiver> payloads;
 
     private StorageReceiver storage;
+
+    private Integer nightLightsState;
+
+    private Integer heightLimit;
+
+    private DistanceLimitStatusReceiver distanceLimitStatus;
+
+    private ObstacleAvoidanceReceiver obstacleAvoidance;
+
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/manage/model/receiver/RequestConfigReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/RequestConfigReceiver.java
new file mode 100644
index 0000000..e468a6f
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/model/receiver/RequestConfigReceiver.java
@@ -0,0 +1,16 @@
+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;
+}
diff --git a/src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java b/src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java
index 201f82c..97b901b 100644
--- a/src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java
+++ b/src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java
@@ -13,10 +13,12 @@
 
     /**
      * 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);
 
 }
diff --git a/src/main/java/com/dji/sample/manage/service/IDeviceService.java b/src/main/java/com/dji/sample/manage/service/IDeviceService.java
index 37e5a44..ff41435 100644
--- a/src/main/java/com/dji/sample/manage/service/IDeviceService.java
+++ b/src/main/java/com/dji/sample/manage/service/IDeviceService.java
@@ -8,13 +8,17 @@
 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;
 
 /**
@@ -33,10 +37,10 @@
 
     /**
      * 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.
@@ -133,10 +137,9 @@
 
     /**
      * 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.
@@ -205,4 +208,28 @@
      * @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);
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/manage/service/ILiveStreamService.java b/src/main/java/com/dji/sample/manage/service/ILiveStreamService.java
index f2a2470..2fb7a4f 100644
--- a/src/main/java/com/dji/sample/manage/service/ILiveStreamService.java
+++ b/src/main/java/com/dji/sample/manage/service/ILiveStreamService.java
@@ -48,4 +48,11 @@
      * @return
      */
     ResponseResult liveSetQuality(LiveTypeDTO liveParam);
+
+    /**
+     * Switches the lens of the device during the live streaming.
+     * @param liveParam
+     * @return
+     */
+    ResponseResult liveLensChange(LiveTypeDTO liveParam);
 }
diff --git a/src/main/java/com/dji/sample/manage/service/IRequestsConfigService.java b/src/main/java/com/dji/sample/manage/service/IRequestsConfigService.java
new file mode 100644
index 0000000..9f0222b
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/service/IRequestsConfigService.java
@@ -0,0 +1,15 @@
+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();
+}
diff --git a/src/main/java/com/dji/sample/manage/service/impl/CameraVideoServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/CameraVideoServiceImpl.java
index ec769b3..bfe6de9 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/CameraVideoServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/CameraVideoServiceImpl.java
@@ -23,7 +23,8 @@
         if (receiver != null) {
             builder.id(UUID.randomUUID().toString())
                     .index(receiver.getVideoIndex())
-                    .type(receiver.getVideoType());
+                    .type(receiver.getVideoType())
+                    .switchVideoTypes(receiver.getSwitchableVideoTypes());
         }
         return builder.build();
     }
diff --git a/src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java
index be5020d..13ddc9b 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java
@@ -5,6 +5,7 @@
 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;
@@ -68,7 +69,7 @@
         // 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()));
         }
diff --git a/src/main/java/com/dji/sample/manage/service/impl/ConfigProductServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/ConfigProductServiceImpl.java
new file mode 100644
index 0000000..b117586
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/service/impl/ConfigProductServiceImpl.java
@@ -0,0 +1,20 @@
+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);
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceDictionaryServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceDictionaryServiceImpl.java
index f0dbad0..a4574b7 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/DeviceDictionaryServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceDictionaryServiceImpl.java
@@ -25,16 +25,18 @@
     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 "))));
     }
 
     /**
diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java
index 3cf9777..7e8882c 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java
@@ -18,6 +18,7 @@
 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;
@@ -61,6 +62,9 @@
     @Autowired
     private IWebSocketManageService webSocketManageService;
 
+    @Autowired
+    private IDeviceService deviceService;
+
     @Override
     public Optional<DeviceFirmwareDTO> getFirmware(String deviceName, String version) {
         return Optional.ofNullable(entity2Dto(mapper.selectOne(
@@ -83,7 +87,7 @@
     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.");
             }
@@ -169,7 +173,7 @@
                             .bid(receiver.getBid())
                             .method(receiver.getMethod())
                             .timestamp(System.currentTimeMillis())
-                            .data(ResponseResult.success())
+                            .data(RequestsReply.success())
                             .build());
         }
     }
diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java
index eec4b41..b94a0ad 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java
@@ -16,10 +16,7 @@
 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;
@@ -113,20 +110,17 @@
         }
 
         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);
@@ -170,20 +164,16 @@
 
         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()));
         }
@@ -207,19 +197,15 @@
         }
         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());
         }
@@ -249,7 +235,7 @@
                             .bid(receiver.getBid())
                             .method(receiver.getMethod())
                             .timestamp(System.currentTimeMillis())
-                            .data(ResponseResult.success())
+                            .data(RequestsReply.success())
                             .build());
         }
 
@@ -284,7 +270,6 @@
             List<LogsExtFileReceiver> fileReceivers = output.getExt().getFiles();
             if (CollectionUtils.isEmpty(fileReceivers)) {
                 redisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn);
-                return;
             }
 
             // refresh cache.
diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java
index 9fc690d..16ed4be 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java
@@ -1,6 +1,7 @@
 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;
@@ -74,7 +75,7 @@
                 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);
diff --git a/src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java
index 123660b..1e6f52b 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java
@@ -9,6 +9,7 @@
 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;
@@ -204,7 +205,7 @@
 
             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()));
diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
index 1fecf85..75a97d1 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
@@ -20,21 +20,20 @@
 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;
@@ -99,8 +98,12 @@
     @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.
@@ -111,26 +114,21 @@
             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;
         }
 
@@ -139,21 +137,23 @@
 
     @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;
     }
@@ -165,7 +165,6 @@
         // 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);
@@ -197,61 +196,37 @@
                     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;
     }
 
@@ -264,20 +239,14 @@
                 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
@@ -459,7 +428,10 @@
     }
 
     @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(),
@@ -568,7 +540,9 @@
 
         // 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())
@@ -598,7 +572,7 @@
 
         // 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())
@@ -611,8 +585,7 @@
                 .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();
     }
 
@@ -690,15 +663,15 @@
         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);
 
@@ -767,8 +740,8 @@
 
         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<>();
 
@@ -855,7 +828,13 @@
     }
 
     @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())
@@ -885,29 +864,97 @@
 
         // 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;
     }
 
     /**
@@ -940,15 +987,17 @@
 
     /**
      * 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 ->
@@ -993,4 +1042,36 @@
                 .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;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java
index 4c34cd5..f0bac72 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java
@@ -8,7 +8,6 @@
 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;
@@ -40,10 +39,6 @@
             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);
         }
     }
diff --git a/src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java
index e5b6c8f..9aae21e 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java
@@ -4,13 +4,13 @@
 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;
@@ -70,7 +70,7 @@
         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())
@@ -96,7 +96,7 @@
     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;
         }
 
@@ -104,13 +104,10 @@
         // 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());
@@ -132,7 +129,7 @@
                         .toString());
                 break;
             case RTSP:
-                String url = receiveReplyOpt.get().getInfo().toString();
+                String url = receiveReply.getInfo().toString();
                 this.resolveUrlUser(url, live);
                 break;
             case UNKNOWN:
@@ -151,12 +148,9 @@
 
         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();
@@ -177,15 +171,47 @@
 
         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);
     }
 
     /**
@@ -194,6 +220,9 @@
      * @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) {
@@ -272,12 +301,12 @@
      * @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);
     }
@@ -288,14 +317,14 @@
      * @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);
@@ -307,13 +336,13 @@
      * @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);
     }
diff --git a/src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java
index e773828..5eeb5a1 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java
@@ -50,7 +50,7 @@
         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);
             });
         });
diff --git a/src/main/java/com/dji/sample/manage/service/impl/RequestConfigContext.java b/src/main/java/com/dji/sample/manage/service/impl/RequestConfigContext.java
new file mode 100644
index 0000000..5665145
--- /dev/null
+++ b/src/main/java/com/dji/sample/manage/service/impl/RequestConfigContext.java
@@ -0,0 +1,56 @@
+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());
+    }
+}
diff --git a/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java
index 8f3df63..746fe99 100644
--- a/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java
@@ -1,6 +1,8 @@
 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;
@@ -29,6 +31,7 @@
 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;
 
@@ -115,10 +118,22 @@
         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);
@@ -195,7 +210,7 @@
      */
     private UserDTO entityConvertToDTO(UserEntity entity) {
         if (entity == null) {
-            return new UserDTO();
+            return null;
         }
         return UserDTO.builder()
                 .userId(entity.getUserId())
diff --git a/src/main/java/com/dji/sample/media/model/CredentialsDTO.java b/src/main/java/com/dji/sample/media/model/CredentialsDTO.java
index 4280a92..d00e953 100644
--- a/src/main/java/com/dji/sample/media/model/CredentialsDTO.java
+++ b/src/main/java/com/dji/sample/media/model/CredentialsDTO.java
@@ -17,6 +17,8 @@
 @AllArgsConstructor
 public class CredentialsDTO {
 
+    private static final int DELAY = 300;
+
     private String accessKeyId;
 
     private String accessKeySecret;
@@ -29,20 +31,20 @@
         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;
     }
 }
diff --git a/src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java b/src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java
index 821594d..3a2526d 100644
--- a/src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java
+++ b/src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java
@@ -7,6 +7,7 @@
 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;
@@ -89,7 +90,8 @@
         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)
@@ -133,7 +135,7 @@
                     .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();
diff --git a/src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java b/src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java
index 0e92b0b..0e0063a 100644
--- a/src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java
+++ b/src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java
@@ -3,6 +3,8 @@
 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;
@@ -38,6 +40,9 @@
 
     @Autowired
     private IMessageSenderService messageSenderService;
+
+    @Autowired
+    private IDeviceService deviceService;
 
     @Override
     public Boolean fastUpload(String workspaceId, String fingerprint) {
@@ -77,7 +82,7 @@
         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();
@@ -85,6 +90,14 @@
             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());
diff --git a/src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java b/src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java
index 93390e4..c4934ca 100644
--- a/src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java
+++ b/src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java
@@ -9,6 +9,7 @@
 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;
@@ -16,6 +17,7 @@
 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;
 
@@ -158,4 +160,21 @@
         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();
+    }
 }
diff --git a/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java b/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java
index f066f54..e22de66 100644
--- a/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java
+++ b/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java
@@ -10,7 +10,9 @@
 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;
 
@@ -35,12 +37,12 @@
      * @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);
     }
 
     /**
@@ -59,16 +61,16 @@
     }
 
     /**
-     * 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();
     }
 }
diff --git a/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java
index 736a8c5..c4b281d 100644
--- a/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java
+++ b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java
@@ -18,7 +18,15 @@
 
     private String flightId;
 
-    private String type;
+    private Integer taskType;
+
+    private Integer waylineType;
+
+    private Long executeTime;
 
     private FlightTaskFileDTO file;
+
+    private Integer rthAltitude;
+
+    private Integer outOfControlAction;
 }
diff --git a/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java
index ca0accb..e5858ee 100644
--- a/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java
+++ b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java
@@ -18,5 +18,5 @@
 
     private String url;
 
-    private String sign;
+    private String fingerprint;
 }
diff --git a/src/main/java/com/dji/sample/wayline/model/dto/KmzFileProperties.java b/src/main/java/com/dji/sample/wayline/model/dto/KmzFileProperties.java
new file mode 100644
index 0000000..64e29a2
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/model/dto/KmzFileProperties.java
@@ -0,0 +1,39 @@
+package com.dji.sample.wayline.model.dto;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/10/27
+ */
+public class KmzFileProperties {
+
+    private KmzFileProperties() {
+
+    }
+
+    public static final String WAYLINE_FILE_SUFFIX = ".kmz";
+
+    public static final String FILE_DIR_FIRST = "wpmz";
+
+    public static final String FILE_DIR_SECOND_RES = "res";
+
+    public static final String FILE_DIR_SECOND_TEMPLATE = "template.kml";
+
+    public static final String FILE_DIR_SECOND_WAYLINES = "waylines.wpml";
+
+    public static final String TAG_WPML_PREFIX = "wpml:";
+
+    public static final String TAG_DRONE_INFO = "droneInfo";
+
+    public static final String TAG_DRONE_ENUM_VALUE = "droneEnumValue";
+
+    public static final String TAG_DRONE_SUB_ENUM_VALUE = "droneSubEnumValue";
+
+    public static final String TAG_PAYLOAD_INFO = "payloadInfo";
+
+    public static final String TAG_PAYLOAD_ENUM_VALUE = "payloadEnumValue";
+
+    public static final String TAG_PAYLOAD_SUB_ENUM_VALUE = "payloadSubEnumValue";
+
+    public static final String TAG_TEMPLATE_ID = "templateId";
+}
diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java b/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java
index a8820a7..9879879 100644
--- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java
+++ b/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java
@@ -32,12 +32,25 @@
 
     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;
 }
diff --git a/src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java b/src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java
index f2106f6..64800a0 100644
--- a/src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java
+++ b/src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java
@@ -38,15 +38,36 @@
     @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;
 
diff --git a/src/main/java/com/dji/sample/wayline/model/enums/WaylineJobStatusEnum.java b/src/main/java/com/dji/sample/wayline/model/enums/WaylineJobStatusEnum.java
new file mode 100644
index 0000000..b33150b
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/model/enums/WaylineJobStatusEnum.java
@@ -0,0 +1,39 @@
+package com.dji.sample.wayline.model.enums;
+
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/9/26
+ */
+@Getter
+public enum WaylineJobStatusEnum {
+
+    PENDING(1, false),
+
+    IN_PROGRESS(2, false),
+
+    SUCCESS(3, true),
+
+    CANCEL(4, true),
+
+    FAILED(5, true),
+
+    UNKNOWN(6, true);
+
+    int val;
+
+    Boolean end;
+
+    WaylineJobStatusEnum(int val, boolean end) {
+        this.end = end;
+        this.val = val;
+    }
+
+    public static WaylineJobStatusEnum find(int val) {
+        return Arrays.stream(WaylineJobStatusEnum.values()).filter(statue -> statue.val == val).findAny().orElse(UNKNOWN);
+    }
+}
diff --git a/src/main/java/com/dji/sample/wayline/model/enums/WaylineMethodEnum.java b/src/main/java/com/dji/sample/wayline/model/enums/WaylineMethodEnum.java
new file mode 100644
index 0000000..4951d2c
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/model/enums/WaylineMethodEnum.java
@@ -0,0 +1,24 @@
+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;
+    }
+}
diff --git a/src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java b/src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java
new file mode 100644
index 0000000..89e556a
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java
@@ -0,0 +1,22 @@
+package com.dji.sample.wayline.model.enums;
+
+import lombok.Getter;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/9/26
+ */
+@Getter
+public enum WaylineTaskTypeEnum {
+
+    IMMEDIATE(0),
+
+    TIMED(1);
+
+    int val;
+
+    WaylineTaskTypeEnum(int val) {
+        this.val = val;
+    }
+}
diff --git a/src/main/java/com/dji/sample/wayline/model/enums/WaylineTemplateTypeEnum.java b/src/main/java/com/dji/sample/wayline/model/enums/WaylineTemplateTypeEnum.java
new file mode 100644
index 0000000..2388e32
--- /dev/null
+++ b/src/main/java/com/dji/sample/wayline/model/enums/WaylineTemplateTypeEnum.java
@@ -0,0 +1,26 @@
+package com.dji.sample.wayline.model.enums;
+
+import lombok.Getter;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/9/26
+ */
+@Getter
+public enum WaylineTemplateTypeEnum {
+
+    WAYPOINT(0),
+
+    MAPPING_2D(1),
+
+    MAPPING_3D(2),
+
+    MAPPING_STRIP(4);
+
+    int val;
+
+    WaylineTemplateTypeEnum(int val) {
+        this.val = val;
+    }
+}
diff --git a/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java b/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java
index a186838..f8fe651 100644
--- a/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java
+++ b/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java
@@ -1,6 +1,10 @@
 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
@@ -10,13 +14,30 @@
 @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;
 }
diff --git a/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java b/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java
index 83f1820..4a722c8 100644
--- a/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java
+++ b/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java
@@ -3,6 +3,7 @@
 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;
@@ -71,4 +72,13 @@
      * @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);
 }
diff --git a/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java b/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java
index 4dc78d9..d20c53d 100644
--- a/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java
+++ b/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java
@@ -2,10 +2,14 @@
 
 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;
 
 /**
@@ -16,20 +20,36 @@
 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.
@@ -53,4 +73,11 @@
      * @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);
 }
diff --git a/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java b/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java
index 46774bb..83b3247 100644
--- a/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java
+++ b/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java
@@ -12,15 +12,24 @@
 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
@@ -46,18 +55,43 @@
     @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(
@@ -77,8 +111,47 @@
                             .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);
+            }
+        }
+    }
 }
diff --git a/src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java b/src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java
index f12979a..7502950 100644
--- a/src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java
+++ b/src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java
@@ -7,24 +7,36 @@
 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
@@ -94,13 +106,17 @@
         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;
     }
@@ -146,6 +162,78 @@
         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
@@ -192,6 +280,7 @@
                             .map(String::valueOf)
                             .collect(Collectors.joining(",")))
                     .favorited(file.getFavorited())
+                    .sign(file.getSign())
                     .build();
         }
 
diff --git a/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java b/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java
index 9a2336c..5313863 100644
--- a/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java
+++ b/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java
@@ -3,40 +3,44 @@
 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;
 
 /**
@@ -64,10 +68,18 @@
     @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())
@@ -76,75 +88,203 @@
                 .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
@@ -159,9 +299,7 @@
     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
@@ -169,7 +307,8 @@
         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)
@@ -178,14 +317,76 @@
         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();
     }
 
@@ -193,10 +394,9 @@
         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())
@@ -206,7 +406,24 @@
                         .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();
     }
 }
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 80c32ce..027299f 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -107,3 +107,7 @@
     com.dji.sample: debug
   file:
     name: logs/cloud-api-sample.log
+
+ntp:
+  server:
+    host: Google.mzr.me
\ No newline at end of file

--
Gitblit v1.9.3