From 9b2eedb85d53ca32610c32c6e50b5230ab3b16cf Mon Sep 17 00:00:00 2001 From: "sean.zhou" Date: Fri, 22 Jul 2022 20:16:03 +0800 Subject: [PATCH] V1.1.0 for dock --- README.md | 6 +- api/Cloud API Demo.postman_collection.json | 328 ++++++++- api/Cloud API Demo.postman_environment.json | 6 +- pom.xml | 31 +- sql/cloud_sample.sql | 157 ++-- .../dji/sample/CloudApiSampleApplication.java | 1 + .../sample/common/error/CommonErrorEnum.java | 10 + .../sample/common/error/StorageErrorEnum.java | 38 + .../com/dji/sample/common/util/JwtUtil.java | 4 + .../component/ApplicationBootInitial.java | 24 +- .../component/GlobalScheduleService.java | 53 +- .../mqtt/config/MqttInboundConfiguration.java | 5 +- .../mqtt/config/MqttMessageChannel.java | 52 +- .../handler/AbstractStateTopicHandler.java | 39 + .../component/mqtt/handler/EventsRouter.java | 48 ++ .../mqtt/handler/InboundMessageRouter.java | 118 +++ .../mqtt/handler/RequestsRouter.java | 49 ++ .../mqtt/handler/StateDefaultHandler.java | 26 + .../mqtt/handler/StateDeviceBasicHandler.java | 38 + .../handler/StateFirmwareVersionHandler.java | 62 ++ .../handler/StateLiveCapacityHandler.java | 39 + .../component/mqtt/handler/StateRouter.java | 104 +++ .../component/mqtt/handler/StatusRouter.java | 67 ++ .../dji/sample/component/mqtt/model/Chan.java | 45 ++ .../component/mqtt/model/ChannelName.java | 19 + .../mqtt/model/CommonTopicReceiver.java | 6 + .../mqtt/model/CommonTopicResponse.java | 2 + .../component/mqtt/model/ErrorInfoReply.java | 23 + .../mqtt/model/EventsMethodEnum.java | 36 + .../component/mqtt/model/EventsReceiver.java | 21 + .../component/mqtt/model/MapKeyConst.java | 29 + .../mqtt/model/RequestsMethodEnum.java | 38 + .../component/mqtt/model/RequestsReply.java | 44 ++ .../component/mqtt/model/ServiceReply.java | 20 + .../mqtt/model/ServicesMethodEnum.java | 29 + .../component/mqtt/model/StateDataEnum.java | 26 + .../component/mqtt/model/TopicConst.java | 4 + .../mqtt/service/IMessageSenderService.java | 10 + .../impl/MessageSenderServiceImpl.java | 38 +- .../mybatis/MybatisPlusMetaObjectHandler.java | 8 +- .../component/oss/model/OssConfiguration.java | 94 +++ .../oss/model/enums/OssTypeEnum.java | 28 + .../component/oss/service/IOssService.java | 18 + .../service/impl/AliyunOssServiceImpl.java | 69 +- .../oss/service/impl/AmazonS3ServiceImpl.java | 135 ++++ .../oss/service/impl/MinIOServiceImpl.java | 54 +- .../oss/service/impl/OssAspectHandler.java | 33 + .../oss/service/impl/OssServiceContext.java | 58 ++ .../component/redis/RedisConfiguration.java | 63 ++ .../sample/component/redis/RedisConst.java | 29 + .../sample/component/redis/RedisOpsUtils.java | 187 +++++ .../config/AuthPrincipalHandler.java | 2 +- .../config/WebSocketDefaultFactory.java | 7 +- .../config/WebSocketDefaultHandler.java | 18 +- .../websocket/model/BizCodeEnum.java | 8 +- .../service/IWebSocketManageService.java | 23 + .../service/impl/SendMessageServiceImpl.java | 7 +- .../impl/WebSocketManageServiceImpl.java | 90 +++ .../SpringBeanConfiguration.java | 49 ++ .../manage/controller/DeviceController.java | 104 +-- .../controller/DeviceHmsController.java | 53 ++ .../controller/DevicePayloadController.java | 26 +- .../controller/LiveStreamController.java | 21 +- .../manage/controller/LoginController.java | 5 +- .../manage/controller/UserController.java | 41 +- .../sample/manage/dao/IDeviceHmsMapper.java | 12 + .../manage/model/common/HmsJsonUtil.java | 49 ++ .../manage/model/common/HmsMessage.java | 16 + .../manage/model/dto/CapacityCameraDTO.java | 4 +- .../manage/model/dto/CapacityVideoDTO.java | 2 +- .../sample/manage/model/dto/DeviceDTO.java | 22 +- .../sample/manage/model/dto/DeviceHmsDTO.java | 57 ++ .../sample/manage/model/dto/TelemetryDTO.java | 4 +- .../manage/model/dto/TopologyDeviceDTO.java | 11 +- .../sample/manage/model/dto/UserListDTO.java | 34 + .../sample/manage/model/dto/UserLoginDTO.java | 3 + .../sample/manage/model/dto/WorkspaceDTO.java | 4 +- .../manage/model/entity/DeviceEntity.java | 19 + .../manage/model/entity/DeviceHmsEntity.java | 76 ++ .../model/entity/DevicePayloadEntity.java | 4 +- .../manage/model/entity/UserEntity.java | 2 +- .../manage/model/entity/WorkspaceEntity.java | 2 + .../manage/model/enums/DeviceDomainEnum.java | 14 + .../sample/manage/model/enums/HmsEnum.java | 155 ++++ .../manage/model/enums/PayloadModelEnum.java | 61 ++ .../manage/model/enums/UserTypeEnum.java | 26 +- .../model/param/DeviceHmsQueryParam.java | 45 ++ .../manage/model/param/DeviceQueryParam.java | 6 +- .../receiver/AlternateLandPointReceiver.java | 18 + .../model/receiver/BindDeviceReceiver.java | 22 + .../model/receiver/BindStatusReceiver.java | 30 + .../model/receiver/DeviceBasicReceiver.java | 6 - .../model/receiver/DeviceHmsReceiver.java | 28 + .../model/receiver/DevicePayloadReceiver.java | 3 - .../receiver/DockMediaFileDetailReceiver.java | 14 + .../model/receiver/DockSdrReceiver.java | 18 + .../model/receiver/DockSubDeviceReceiver.java | 20 + .../receiver/DroneChargeStateReceiver.java | 16 + .../receiver/FirmwareVersionReceiver.java | 29 + .../model/receiver/HmsArgsReceiver.java | 18 + .../model/receiver/LiveCapacityReceiver.java | 20 + .../model/receiver/NetworkStateReceiver.java | 18 + .../receiver/OrganizationGetReceiver.java | 16 + .../model/receiver/OsdDockReceiver.java | 67 ++ .../receiver/OsdDockTransmissionReceiver.java | 18 + .../model/receiver/OsdPayloadReceiver.java | 33 + .../model/receiver/OsdSubDeviceReceiver.java | 8 +- .../model/receiver/PositionStateReceiver.java | 2 + .../receiver/StatusSubDeviceReceiver.java | 2 + .../model/receiver/StorageReceiver.java | 16 + .../receiver/WirelessLinkStateReceiver.java | 2 +- .../manage/service/ICameraVideoService.java | 26 +- .../service/ICapacityCameraService.java | 17 +- .../service/IDeviceDictionaryService.java | 4 +- .../manage/service/IDeviceHmsService.java | 21 + .../manage/service/IDevicePayloadService.java | 7 + .../sample/manage/service/IDeviceService.java | 71 +- .../manage/service/ILiveStreamService.java | 9 +- .../sample/manage/service/ITSAService.java | 18 + .../sample/manage/service/IUserService.java | 14 +- .../manage/service/IWorkspaceService.java | 22 +- .../service/impl/AbstractTSAService.java | 28 +- .../service/impl/CameraVideoServiceImpl.java | 83 +-- .../impl/CapacityCameraServiceImpl.java | 136 +--- .../impl/DeviceDictionaryServiceImpl.java | 6 +- .../service/impl/DeviceHmsServiceImpl.java | 133 ++++ .../service/impl/DeviceOSDServiceImpl.java | 58 +- .../impl/DevicePayloadServiceImpl.java | 53 +- .../service/impl/DeviceServiceImpl.java | 676 ++++++++++++++---- .../service/impl/DockOSDServiceImpl.java | 50 ++ .../service/impl/GatewayOSDServiceImpl.java | 30 +- .../service/impl/LiveStreamServiceImpl.java | 129 ++-- .../service/impl/TopologyServiceImpl.java | 2 +- .../manage/service/impl/UserServiceImpl.java | 78 +- .../service/impl/WorkspaceServiceImpl.java | 60 +- .../WorkspaceElementController.java | 13 +- .../media/controller/FileController.java | 35 +- .../media/controller/MediaController.java | 24 +- .../sample/media/model/CredentialsDTO.java | 10 +- .../sample/media/model/FileExtensionDTO.java | 2 + .../media/model/FileUploadCallback.java | 18 + .../dji/sample/media/model/MediaFileDTO.java | 6 + .../sample/media/model/MediaFileEntity.java | 3 + .../sample/media/service/IFileService.java | 19 + .../sample/media/service/IMediaService.java | 16 + .../media/service/impl/FileServiceImpl.java | 56 +- .../media/service/impl/MediaServiceImpl.java | 56 ++ .../storage/controller/StorageController.java | 21 +- .../storage/service/IStorageService.java | 9 + .../service/impl/StorageServiceImpl.java | 56 ++ .../controller/WaylineFileController.java | 27 +- .../controller/WaylineJobController.java | 74 ++ .../wayline/dao/IWaylineFileMapper.java | 2 +- .../sample/wayline/dao/IWaylineJobMapper.java | 12 + .../wayline/model/dto/FLightTaskProgress.java | 16 + .../model/dto/FlightTaskCreateDTO.java | 24 + .../wayline/model/dto/FlightTaskFileDTO.java | 22 + .../model/dto/FlightTaskProgressExt.java | 16 + .../model/dto/FlightTaskProgressReceiver.java | 19 + .../wayline/model/dto/WaylineFileDTO.java | 43 ++ .../model/dto/WaylineFileUploadDTO.java | 18 + .../wayline/model/dto/WaylineJobDTO.java | 43 ++ .../model/entity/WaylineFileEntity.java | 62 ++ .../model/entity/WaylineJobEntity.java | 56 ++ .../wayline/model/param/CreateJobParam.java | 22 + .../model/param/WaylineQueryParam.java | 30 + .../wayline/service/IFlightTaskService.java | 18 + .../wayline/service/IWaylineFileService.java | 21 +- .../wayline/service/IWaylineJobService.java | 56 ++ .../service/impl/FlightTaskServiceImpl.java | 84 +++ .../service/impl/WaylineFileServiceImpl.java | 112 +-- .../service/impl/WaylineJobServiceImpl.java | 212 ++++++ src/main/resources/application.yml | 80 ++- 173 files changed, 6123 insertions(+), 998 deletions(-) create mode 100644 src/main/java/com/dji/sample/common/error/StorageErrorEnum.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/AbstractStateTopicHandler.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/EventsRouter.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateDefaultHandler.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateDeviceBasicHandler.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateFirmwareVersionHandler.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateLiveCapacityHandler.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateRouter.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StatusRouter.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/Chan.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/ErrorInfoReply.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/EventsMethodEnum.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/RequestsReply.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/ServiceReply.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/ServicesMethodEnum.java create mode 100644 src/main/java/com/dji/sample/component/mqtt/model/StateDataEnum.java create mode 100644 src/main/java/com/dji/sample/component/oss/model/OssConfiguration.java create mode 100644 src/main/java/com/dji/sample/component/oss/model/enums/OssTypeEnum.java create mode 100644 src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java create mode 100644 src/main/java/com/dji/sample/component/oss/service/impl/OssAspectHandler.java create mode 100644 src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java create mode 100644 src/main/java/com/dji/sample/component/redis/RedisConfiguration.java create mode 100644 src/main/java/com/dji/sample/component/redis/RedisConst.java create mode 100644 src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java create mode 100644 src/main/java/com/dji/sample/component/websocket/service/IWebSocketManageService.java create mode 100644 src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketManageServiceImpl.java create mode 100644 src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java create mode 100644 src/main/java/com/dji/sample/manage/controller/DeviceHmsController.java create mode 100644 src/main/java/com/dji/sample/manage/dao/IDeviceHmsMapper.java create mode 100644 src/main/java/com/dji/sample/manage/model/common/HmsJsonUtil.java create mode 100644 src/main/java/com/dji/sample/manage/model/common/HmsMessage.java create mode 100644 src/main/java/com/dji/sample/manage/model/dto/DeviceHmsDTO.java create mode 100644 src/main/java/com/dji/sample/manage/model/dto/UserListDTO.java create mode 100644 src/main/java/com/dji/sample/manage/model/entity/DeviceHmsEntity.java create mode 100644 src/main/java/com/dji/sample/manage/model/enums/HmsEnum.java create mode 100644 src/main/java/com/dji/sample/manage/model/enums/PayloadModelEnum.java create mode 100644 src/main/java/com/dji/sample/manage/model/param/DeviceHmsQueryParam.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/BindDeviceReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/BindStatusReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DeviceHmsReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DockMediaFileDetailReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DockSdrReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DockSubDeviceReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DroneChargeStateReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/FirmwareVersionReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/HmsArgsReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/LiveCapacityReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OrganizationGetReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OsdDockTransmissionReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OsdPayloadReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/model/receiver/StorageReceiver.java create mode 100644 src/main/java/com/dji/sample/manage/service/IDeviceHmsService.java create mode 100644 src/main/java/com/dji/sample/manage/service/impl/DeviceHmsServiceImpl.java create mode 100644 src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java create mode 100644 src/main/java/com/dji/sample/media/model/FileUploadCallback.java create mode 100644 src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java create mode 100644 src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java create mode 100644 src/main/java/com/dji/sample/wayline/dao/IWaylineJobMapper.java create mode 100644 src/main/java/com/dji/sample/wayline/model/dto/FLightTaskProgress.java create mode 100644 src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java create mode 100644 src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java create mode 100644 src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressExt.java create mode 100644 src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressReceiver.java create mode 100644 src/main/java/com/dji/sample/wayline/model/dto/WaylineFileDTO.java create mode 100644 src/main/java/com/dji/sample/wayline/model/dto/WaylineFileUploadDTO.java create mode 100644 src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java create mode 100644 src/main/java/com/dji/sample/wayline/model/entity/WaylineFileEntity.java create mode 100644 src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java create mode 100644 src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java create mode 100644 src/main/java/com/dji/sample/wayline/model/param/WaylineQueryParam.java create mode 100644 src/main/java/com/dji/sample/wayline/service/IFlightTaskService.java create mode 100644 src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java create mode 100644 src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java create mode 100644 src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java diff --git a/README.md b/README.md index 2071047..faf73ce 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ The launch of the Cloud API mainly solves the problem of developers reinventing ## Docker -If you don't want to install the development environment, you can try deploying with docker. [Click the link to download.](https://terra-sz-hc1pro-cloudapi.oss-cn-shenzhen.aliyuncs.com/c0af9fe0d7eb4f35a8fe5b695e4d0b96/docker/cloud_api_sample_docker_1.0.0.zip) +If you don't want to install the development environment, you can try deploying with docker. [Click the link to download.](https://terra-sz-hc1pro-cloudapi.oss-cn-shenzhen.aliyuncs.com/c0af9fe0d7eb4f35a8fe5b695e4d0b96/docker/cloud_api_sample_docker.zip) ## Usage -For more documentation, please visit the [DJI Developer Documentation](https://developer.dji.com/cn/document/209883f1-f2ad-406e-b99c-be7498df7f10). +For more documentation, please visit the [DJI Developer Documentation](https://developer.dji.com/doc/cloud-api-tutorial/cn/). ## Latest Release -Cloud API 1.0.0 was released on 21 March 2022. For more information, please visit the [Release Note](https://developer.dji.com/cn/document/87026f9b-e906-4809-9aba-870f569061b5). +Cloud API 1.1.0 was released on 22 July 2022. For more information, please visit the [Release Note](https://developer.dji.com/doc/cloud-api-tutorial/cn/). ## License diff --git a/api/Cloud API Demo.postman_collection.json b/api/Cloud API Demo.postman_collection.json index 6c380a8..fc0fd32 100644 --- a/api/Cloud API Demo.postman_collection.json +++ b/api/Cloud API Demo.postman_collection.json @@ -18,7 +18,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"username\": \"adminPC\",\r\n \"password\": \"adminPC\"\r\n}", + "raw": "{\r\n \"username\": \"adminPC\",\r\n \"password\": \"adminPC\",\r\n \"flag\": 1\r\n}", "options": { "raw": { "language": "json" @@ -100,12 +100,13 @@ "method": "GET", "header": [], "url": { - "raw": "{{base_url}}{{manage_version}}/devices/devices", + "raw": "{{base_url}}{{manage_version}}/devices/{{workspace_id}}/devices", "host": [ "{{base_url}}{{manage_version}}" ], "path": [ "devices", + "{{workspace_id}}", "devices" ] } @@ -132,12 +133,23 @@ }, { "name": "Start Livestream", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], "request": { "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"url_type\": 0,\r\n \"url\": \"\",\r\n \"video_id\": \"\",\r\n \"video_quality\": 0\r\n}", + "raw": "{\r\n \"url\": \"rtmp://192.168.1.1/live/1651053434895\",\r\n \"url_type\": 1,\r\n \"video_id\": \"1581F4BN/52-0-0/zoom-0\",\r\n \"video_quality\": 0\r\n}", "options": { "raw": { "language": "json" @@ -165,7 +177,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"video_id\": \"\"\r\n}", + "raw": "{\r\n \"video_id\": \"1581F4BNDQ/39-0-7/normal-0\"\r\n}", "options": { "raw": { "language": "json" @@ -213,19 +225,177 @@ } }, "response": [] + }, + { + "name": "Get All Users Info", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}{{manage_version}}/users/{{workspace_id}}/users?page=1&page_size=10", + "host": [ + "{{base_url}}{{manage_version}}" + ], + "path": [ + "users", + "{{workspace_id}}", + "users" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "page_size", + "value": "10" + } + ] + } + }, + "response": [] + }, + { + "name": "Update User Info", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"mqtt_username\": \"admin\",\r\n \"mqtt_password\": \"admin\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}{{manage_version}}/users/{{workspace_id}}/users/a1559e7c-8dd8-4780-b952-100cc4797da2", + "host": [ + "{{base_url}}{{manage_version}}" + ], + "path": [ + "users", + "{{workspace_id}}", + "users", + "a1559e7c-8dd8-4780-b952-100cc4797da2" + ] + } + }, + "response": [] + }, + { + "name": "Bind Device", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"user_id\": \"be7c6c3d-afe9-4be4-b9eb-c55066c0914e\",\r\n \"workspace_id\": \"e3dea0f5-37f2-4d79-ae58-490af3228069\",\r\n \"device_sn\": \"1ZMDG009\",\r\n \"child_device_sn\": \"\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}{{manage_version}}/devices/binding", + "host": [ + "{{base_url}}{{manage_version}}" + ], + "path": [ + "devices", + "binding" + ] + } + }, + "response": [] + }, + { + "name": "Get Binding Devices", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}{{manage_version}}/devices/{{workspace_id}}/devices/bound?page=1&page_size=10&domain=sub-device", + "host": [ + "{{base_url}}{{manage_version}}" + ], + "path": [ + "devices", + "{{workspace_id}}", + "devices", + "bound" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "page_size", + "value": "10" + }, + { + "key": "domain", + "value": "sub-device" + } + ] + } + }, + "response": [] + }, + { + "name": "Get Device", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}{{manage_version}}/devices/{{workspace_id}}/devices/{{device_sn}}", + "host": [ + "{{base_url}}{{manage_version}}" + ], + "path": [ + "devices", + "{{workspace_id}}", + "devices", + "{{device_sn}}" + ] + } + }, + "response": [] + }, + { + "name": "Unbind Device", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{base_url}}{{manage_version}}/devices/{{device_sn}}/unbinding", + "host": [ + "{{base_url}}{{manage_version}}" + ], + "path": [ + "devices", + "{{device_sn}}", + "unbinding" + ] + } + }, + "response": [] } ], "auth": { "type": "apikey", "apikey": [ { - "key": "key", - "value": "x-auth-token", + "key": "value", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTI2OTUxOTcsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTI3ODE1OTcsImlhdCI6MTY1MjY5NTE5NywidXNlcm5hbWUiOiJhZG1pblBDIn0.BHTwW8imw5ab0GUypRyJ2gkoz5av9q99NrxoFlL53dA", "type": "string" }, { - "key": "value", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NDg1MjI5ODAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NDg2MDkzODAsImlhdCI6MTY0ODUyMjk4MCwidXNlcm5hbWUiOiJhZG1pblBDIn0.QEDpB_60G4YLLXR_6rLZHWZNVbA1j162Xs1fZx8wEOM", + "key": "key", + "value": "x-auth-token", "type": "string" } ] @@ -390,7 +560,7 @@ "apikey": [ { "key": "value", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NDg1MjI5ODAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NDg2MDkzODAsImlhdCI6MTY0ODUyMjk4MCwidXNlcm5hbWUiOiJhZG1pblBDIn0.QEDpB_60G4YLLXR_6rLZHWZNVbA1j162Xs1fZx8wEOM", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTMzNzI3NDUsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTM0NTkxNDUsImlhdCI6MTY1MzM3Mjc0NSwidXNlcm5hbWUiOiJhZG1pblBDIn0.Zyb_f4umcGY2-WDaQKA1LHGOs9qYfJuPc3rQeIS-4hY", "type": "string" }, { @@ -520,15 +690,12 @@ }, { "name": "Check Tiny Fingerprints", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, "request": { - "method": "GET", + "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"tiny_fingerprints\":[\r\n \r\n ]\r\n}", + "raw": "{\r\n \"tiny_fingerprints\":[\r\n \"4a3a67101ffb81d079338d4729315a8c_2022_3_3_11_38_58\",\r\n \"8e0fedb981be23dd034cf7927919da51_2022_3_3_11_45_26\"\r\n ]\r\n}", "options": { "raw": { "language": "json" @@ -536,7 +703,7 @@ } }, "url": { - "raw": "{{base_url}}{{media_version}}/workspaces/{{workspace_id}}/files/tiny-fingerprints?tiny_fingerprint=045040860fb014916c81082407e9ff8b_2021_12_2_16_17_8,922aca3dee753f2f9ee1ad0565967334_2021_12_8_22_13_12", + "raw": "{{base_url}}{{media_version}}/workspaces/{{workspace_id}}/files/tiny-fingerprints", "host": [ "{{base_url}}{{media_version}}" ], @@ -545,12 +712,6 @@ "{{workspace_id}}", "files", "tiny-fingerprints" - ], - "query": [ - { - "key": "tiny_fingerprint", - "value": "045040860fb014916c81082407e9ff8b_2021_12_2_16_17_8,922aca3dee753f2f9ee1ad0565967334_2021_12_8_22_13_12" - } ] } }, @@ -562,7 +723,7 @@ "apikey": [ { "key": "value", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NDg1MjI5ODAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NDg2MDkzODAsImlhdCI6MTY0ODUyMjk4MCwidXNlcm5hbWUiOiJhZG1pblBDIn0.QEDpB_60G4YLLXR_6rLZHWZNVbA1j162Xs1fZx8wEOM", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTgzOTIyNzksImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTg0Nzg2NzksImlhdCI6MTY1ODM5MjI3OSwidXNlcm5hbWUiOiJhZG1pblBDIn0.ErClyQS1YzoQVBcLYxpFEiyFTb1L-eg2-vsudlL9WJU", "type": "string" }, { @@ -622,7 +783,7 @@ "apikey": [ { "key": "value", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NDg1MjI5ODAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NDg2MDkzODAsImlhdCI6MTY0ODUyMjk4MCwidXNlcm5hbWUiOiJhZG1pblBDIn0.QEDpB_60G4YLLXR_6rLZHWZNVbA1j162Xs1fZx8wEOM", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTI2OTUxOTcsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTI3ODE1OTcsImlhdCI6MTY1MjY5NTE5NywidXNlcm5hbWUiOiJhZG1pblBDIn0.BHTwW8imw5ab0GUypRyJ2gkoz5av9q99NrxoFlL53dA", "type": "string" }, { @@ -848,7 +1009,124 @@ "apikey": [ { "key": "value", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NDg1MjI5ODAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NDg2MDkzODAsImlhdCI6MTY0ODUyMjk4MCwidXNlcm5hbWUiOiJhZG1pblBDIn0.QEDpB_60G4YLLXR_6rLZHWZNVbA1j162Xs1fZx8wEOM", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTU0NDk2MDIsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTU1MzYwMDIsImlhdCI6MTY1NTQ0OTYwMiwidXNlcm5hbWUiOiJhZG1pblBDIn0.YZWHJ65Pl_DT2Ampxk0WC01KD_fNTm_rYVUBIHAZD-4", + "type": "string" + }, + { + "key": "key", + "value": "x-auth-token", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "job", + "item": [ + { + "name": "Create Flight Job", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"\",\r\n \"fild_id\": \"\",\r\n \"dock_sn\": \"\",\r\n \"type\": \"\",\r\n \"immediate\": false\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/flight-tasks", + "host": [ + "{{base_url}}{{wayline_version}}" + ], + "path": [ + "workspaces", + "{{workspace_id}}", + "flight-tasks" + ] + } + }, + "response": [] + }, + { + "name": "Get Jobs", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/jobs?page=1&pageSize=10", + "host": [ + "{{base_url}}{{wayline_version}}" + ], + "path": [ + "workspaces", + "{{workspace_id}}", + "jobs" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + } + }, + "response": [] + }, + { + "name": "Execute Job", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/jobs/{{plan_id}}", + "host": [ + "{{base_url}}{{wayline_version}}" + ], + "path": [ + "workspaces", + "{{workspace_id}}", + "jobs", + "{{plan_id}}" + ] + } + }, + "response": [] + } + ], + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTU4OTA5NTQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTU5NzczNTQsImlhdCI6MTY1NTg5MDk1NCwidXNlcm5hbWUiOiJhZG1pblBDIn0.fd0iIzCd71LDUE6ixexUJvo-YqtnSCqRx-790snCyBI", "type": "string" }, { @@ -885,7 +1163,7 @@ "apikey": [ { "key": "value", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Mzg5MzY2OTEsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2MzkwMjMwOTEsImlhdCI6MTYzODkzNjY5MSwidXNlcm5hbWUiOiJhZG1pblBDIn0.W_v88rbhVKivl61MnJ0Cz_0Eq6Gw0RotiHCdLj0UPSQ", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NTA0MjY3MzAsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NTA1MTMxMzAsImlhdCI6MTY1MDQyNjczMCwidXNlcm5hbWUiOiJhZG1pblBDIn0.-FBpi0ktuZ68jV6-HQ7mwm8iC07YH-Hw2aRiREzJ8hs", "type": "string" }, { diff --git a/api/Cloud API Demo.postman_environment.json b/api/Cloud API Demo.postman_environment.json index f4faf3a..d2360f1 100644 --- a/api/Cloud API Demo.postman_environment.json +++ b/api/Cloud API Demo.postman_environment.json @@ -4,7 +4,7 @@ "values": [ { "key": "ip", - "value": "192.168.1.1", + "value": "localhost", "type": "default", "enabled": true }, @@ -55,6 +55,6 @@ } ], "_postman_variable_scope": "environment", - "_postman_exported_at": "2022-03-29T03:52:14.600Z", - "_postman_exported_using": "Postman/9.7.1" + "_postman_exported_at": "2022-07-21T08:35:48.441Z", + "_postman_exported_using": "Postman/9.19.0" } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5cbe4c6..b45b2f2 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.dji cloud-api-sample - 1.0.0 + 1.1.0 cloud-api-sample @@ -127,6 +127,35 @@ ${glassfish-jaxb.version} + + org.springframework.boot + spring-boot-starter-data-redis + + + org.apache.commons + commons-pool2 + + + + com.amazonaws + aws-java-sdk-s3 + 1.12.200 + + + com.amazonaws + aws-java-sdk-sts + 1.12.200 + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + org.springframework.boot + spring-boot-starter-aop + diff --git a/sql/cloud_sample.sql b/sql/cloud_sample.sql index 6f0e968..f644248 100644 --- a/sql/cloud_sample.sql +++ b/sql/cloud_sample.sql @@ -10,40 +10,8 @@ SET NAMES utf8mb4; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -# manage_camera_video -# ------------------------------------------------------------ - -DROP TABLE IF EXISTS `manage_camera_video`; - -CREATE TABLE `manage_camera_video` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `camera_id` int NOT NULL, - `video_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `video_type` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; - - -# manage_capacity_camera -# ------------------------------------------------------------ - -DROP TABLE IF EXISTS `manage_capacity_camera`; - -CREATE TABLE `manage_capacity_camera` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', - `description` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, - `camera_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `coexist_video_number_max` int NOT NULL, - `available_video_number` int NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; - - - -# manage_device +# manage_device # ------------------------------------------------------------ DROP TABLE IF EXISTS `manage_device`; @@ -52,25 +20,31 @@ CREATE TABLE `manage_device` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `device_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', - `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `device_type` smallint NOT NULL, - `sub_type` smallint NOT NULL, - `domain` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `version` smallint NOT NULL, - `device_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, - `child_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, + `user_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', + `nickname` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', + `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', + `device_type` smallint NOT NULL DEFAULT '-1', + `sub_type` smallint NOT NULL DEFAULT '-1', + `domain` smallint NOT NULL DEFAULT '-1', + `firmware_version` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', + `version` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', + `device_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', + `child_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', `create_time` bigint NOT NULL, `update_time` bigint NOT NULL, - `device_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, - `url_normal` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, - `url_select` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, + `bound_time` bigint DEFAULT NULL, + `bound_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:bund; 1:not bound', + `login_time` bigint DEFAULT NULL, + `device_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', + `url_normal` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', + `url_select` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `product_sn_UNIQUE` (`device_sn`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; -# manage_device_dictionary +# manage_device_dictionary # ------------------------------------------------------------ DROP TABLE IF EXISTS `manage_device_dictionary`; @@ -106,15 +80,37 @@ VALUES (14,1,165,0,'DJI Dock Camera',NULL), (15,1,90742,0,'L1',NULL), (16,2,56,0,'DJI Smart Controller','Remote control for M300'), - (17,2,119,0,'Matrice 30 Smart Controller','Remote control for M30'), - (18,3,1,0,'DJI Dock','DJI Airport'); - + (17,2,119,0,'DJI RC Plus','Remote control for M30'), + (18,3,1,0,'DJI Dock',''); /*!40000 ALTER TABLE `manage_device_dictionary` ENABLE KEYS */; UNLOCK TABLES; -# manage_device_payload +# manage_device_hms +# ------------------------------------------------------------ + +DROP TABLE IF EXISTS `manage_device_hms`; + +CREATE TABLE `manage_device_hms` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `hms_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `tid` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `bid` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `sn` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `level` smallint NOT NULL, + `module` tinyint NOT NULL, + `hms_key` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `message_zh` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `message_en` varchar(300) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `create_time` bigint NOT NULL, + `update_time` bigint NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQUE_hms_id` (`hms_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + + +# manage_device_payload # ------------------------------------------------------------ DROP TABLE IF EXISTS `manage_device_payload`; @@ -125,7 +121,7 @@ CREATE TABLE `manage_device_payload` ( `payload_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', `payload_type` smallint NOT NULL, `sub_type` smallint NOT NULL, - `version` smallint DEFAULT NULL, + `firmware_version` varchar(32) DEFAULT NULL, `payload_index` smallint NOT NULL, `device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `payload_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, @@ -137,7 +133,7 @@ CREATE TABLE `manage_device_payload` ( -# manage_user +# manage_user # ------------------------------------------------------------ DROP TABLE IF EXISTS `manage_user`; @@ -147,7 +143,7 @@ CREATE TABLE `manage_user` ( `user_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `workspace_id` int NOT NULL, + `workspace_id` varchar(64) NOT NULL DEFAULT '', `user_type` smallint NOT NULL, `mqtt_username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `mqtt_password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', @@ -162,14 +158,14 @@ LOCK TABLES `manage_user` WRITE; INSERT INTO `manage_user` (`id`, `user_id`, `username`, `password`, `workspace_id`, `user_type`, `mqtt_username`, `mqtt_password`, `create_time`, `update_time`) VALUES - (1,'a1559e7c-8dd8-4780-b952-100cc4797da2','adminPC','adminPC',1,1,'admin','admin',1634898410751,1634898410751), - (2,'be7c6c3d-afe9-4be4-b9eb-c55066c0914e','pilot','pilot123',1,2,'pilot','pilot123',1634898410751,1634898410751); + (1,'a1559e7c-8dd8-4780-b952-100cc4797da2','adminPC','adminPC','e3dea0f5-37f2-4d79-ae58-490af3228069',1,'admin','admin',1634898410751,1650880112310), + (2,'be7c6c3d-afe9-4be4-b9eb-c55066c0914e','pilot','pilot123','e3dea0f5-37f2-4d79-ae58-490af3228069',2,'pilot','pilot123',1634898410751,1634898410751); /*!40000 ALTER TABLE `manage_user` ENABLE KEYS */; UNLOCK TABLES; -# manage_workspace +# manage_workspace # ------------------------------------------------------------ DROP TABLE IF EXISTS `manage_workspace`; @@ -182,22 +178,24 @@ CREATE TABLE `manage_workspace` ( `platform_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `create_time` bigint NOT NULL, `update_time` bigint NOT NULL, + `bind_code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', PRIMARY KEY (`id`), - UNIQUE KEY `workspace_id_UNIQUE` (`workspace_id`) + UNIQUE KEY `workspace_id_UNIQUE` (`workspace_id`), + UNIQUE KEY `bind_code_UNIQUE` (`bind_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; LOCK TABLES `manage_workspace` WRITE; /*!40000 ALTER TABLE `manage_workspace` DISABLE KEYS */; -INSERT INTO `manage_workspace` (`id`, `workspace_id`, `workspace_name`, `workspace_desc`, `platform_name`, `create_time`, `update_time`) +INSERT INTO `manage_workspace` (`id`, `workspace_id`, `workspace_name`, `workspace_desc`, `platform_name`, `create_time`, `update_time`, `bind_code`) VALUES - (1,'e3dea0f5-37f2-4d79-ae58-490af3228069','Test Group One','Cloud Sample Test Platform','Cloud Api Platform',1634898410751,1634898410751); + (1,'e3dea0f5-37f2-4d79-ae58-490af3228069','Test Group One','Cloud Sample Test Platform','Cloud Api Platform',1634898410751,1634898410751,'qwe'); /*!40000 ALTER TABLE `manage_workspace` ENABLE KEYS */; UNLOCK TABLES; -# map_element_coordinate +# map_element_coordinate # ------------------------------------------------------------ DROP TABLE IF EXISTS `map_element_coordinate`; @@ -213,7 +211,7 @@ CREATE TABLE `map_element_coordinate` ( -# map_group +# map_group # ------------------------------------------------------------ DROP TABLE IF EXISTS `map_group`; @@ -244,7 +242,7 @@ VALUES UNLOCK TABLES; -# map_group_element +# map_group_element # ------------------------------------------------------------ DROP TABLE IF EXISTS `map_group_element`; @@ -267,7 +265,7 @@ CREATE TABLE `map_group_element` ( -# media_file +# media_file # ------------------------------------------------------------ DROP TABLE IF EXISTS `media_file`; @@ -277,22 +275,22 @@ CREATE TABLE `media_file` ( `file_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `file_path` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `fingerprint` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `fingerprint` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', `tinny_fingerprint` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `object_key` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', - `sub_file_type` int NOT NULL, + `object_key` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `sub_file_type` int DEFAULT NULL, `is_original` tinyint(1) NOT NULL, `drone` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', `payload` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined', + `job_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '', `create_time` bigint NOT NULL, `update_time` bigint NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `fingerprint_UNIQUE` (`fingerprint`) + PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; -# wayline_file +# wayline_file # ------------------------------------------------------------ DROP TABLE IF EXISTS `wayline_file`; @@ -304,18 +302,41 @@ CREATE TABLE `wayline_file` ( `drone_model_key` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `payload_model_keys` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `sign` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'MD5', `favorited` tinyint(1) NOT NULL DEFAULT '0', `template_types` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `object_key` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `user_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `create_time` bigint NOT NULL, - `update_time` bigint NOT NULL COMMENT 'required, can not modify.', + `update_time` bigint NOT NULL COMMENT 'required, can''t modify.', PRIMARY KEY (`id`), UNIQUE KEY `wayline_id_UNIQUE` (`wayline_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +# wayline_job +# ------------------------------------------------------------ + +DROP TABLE IF EXISTS `wayline_job`; + +CREATE TABLE `wayline_job` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `job_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `file_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `dock_sn` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `workspace_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `bid` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `type` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + `create_time` bigint NOT NULL, + `update_time` bigint NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `job_id_UNIQUE` (`job_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + + /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; diff --git a/src/main/java/com/dji/sample/CloudApiSampleApplication.java b/src/main/java/com/dji/sample/CloudApiSampleApplication.java index 14cca32..a3619ab 100644 --- a/src/main/java/com/dji/sample/CloudApiSampleApplication.java +++ b/src/main/java/com/dji/sample/CloudApiSampleApplication.java @@ -8,6 +8,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; @MapperScan("com.dji.sample.*.dao") @SpringBootApplication @EnableScheduling +//@EnableConfigurationProperties(OssConfiguration.class) public class CloudApiSampleApplication { public static void main(String[] args) { diff --git a/src/main/java/com/dji/sample/common/error/CommonErrorEnum.java b/src/main/java/com/dji/sample/common/error/CommonErrorEnum.java index 1dab9d1..290cc42 100644 --- a/src/main/java/com/dji/sample/common/error/CommonErrorEnum.java +++ b/src/main/java/com/dji/sample/common/error/CommonErrorEnum.java @@ -7,6 +7,16 @@ package com.dji.sample.common.error; */ public enum CommonErrorEnum implements IErrorInfo { + ILLEGAL_ARGUMENT(200001, "illegal argument"), + + GET_ORGANIZATION_FAILED(210230, "Failed to get organization."), + + DEVICE_BINDING_FAILED(210231, "Failed to bind device."), + + NON_REPEATABLE_BINDING(210232, "The device has been bound to another organization and can't be bound repeatedly."), + + GET_DEVICE_BINDING_STATUS_FAILED(210233, "Failed to get device binding status."), + SYSTEM_ERROR(600500, "system error"), SECRET_INVALID(600100, "secret invalid"), diff --git a/src/main/java/com/dji/sample/common/error/StorageErrorEnum.java b/src/main/java/com/dji/sample/common/error/StorageErrorEnum.java new file mode 100644 index 0000000..16b5d2f --- /dev/null +++ b/src/main/java/com/dji/sample/common/error/StorageErrorEnum.java @@ -0,0 +1,38 @@ +package com.dji.sample.common.error; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/25 + */ +public enum StorageErrorEnum implements IErrorInfo { + + GENERATE_CREDENTIALS_ERROR(217001, "Failed to generate temporary credentials."), + + NO_BUCKET(217002, "The bucket does not exist."), + + ILLEGAL_PATH_FORMAT(217006, "Illegal path format."), + + FILE_CREATION_FAILED(217007, "File creation failed."), + + DIR_CREATION_FAILED(217008, "Directory creation failed"); + + private String msg; + + private int code; + + StorageErrorEnum(int code, String msg) { + this.msg = msg; + this.code = code; + } + + @Override + public String getErrorMsg() { + return msg; + } + + @Override + public Integer getErrorCode() { + return code; + } +} 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 9c2cb9a..629cea1 100644 --- a/src/main/java/com/dji/sample/common/util/JwtUtil.java +++ b/src/main/java/com/dji/sample/common/util/JwtUtil.java @@ -54,6 +54,10 @@ public class JwtUtil { JwtUtil.algorithm = Algorithm.HMAC256(secret); } + private JwtUtil() { + + } + /** * Create a token based on custom information. * @param claims custom information diff --git a/src/main/java/com/dji/sample/component/ApplicationBootInitial.java b/src/main/java/com/dji/sample/component/ApplicationBootInitial.java index 226fe1a..f35aa3c 100644 --- a/src/main/java/com/dji/sample/component/ApplicationBootInitial.java +++ b/src/main/java/com/dji/sample/component/ApplicationBootInitial.java @@ -1,15 +1,12 @@ package com.dji.sample.component; -import com.dji.sample.manage.model.DeviceStatusManager; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; -import com.dji.sample.manage.model.param.DeviceQueryParam; +import com.dji.sample.component.redis.RedisConst; +import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.manage.service.IDeviceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; -import java.time.LocalDateTime; - /** * @author sean.zhou * @date 2021/11/24 @@ -21,20 +18,21 @@ public class ApplicationBootInitial implements CommandLineRunner { @Autowired private IDeviceService deviceService; + @Autowired + private RedisOpsUtils redisOps; + /** - * Subscribe to the devices that exist in the database when the program starts, + * Subscribe to the devices that exist in the redis when the program starts, * to prevent the data from being different from the pilot side due to program interruptions. * @param args * @throws Exception */ @Override public void run(String... args) throws Exception { - deviceService.getDevicesByParams(DeviceQueryParam.builder().build()) - .forEach(device -> { - deviceService.subscribeTopicOnline(device.getDeviceSn()); - DeviceStatusManager.STATUS_MANAGER.put( - DeviceDomainEnum.getVal(device.getDomain()) + "/" - + device.getDeviceSn(), LocalDateTime.now()); - }); + int start = RedisConst.DEVICE_ONLINE_PREFIX.length(); + + redisOps.getAllKeys(RedisConst.DEVICE_ONLINE_PREFIX + "*") + .forEach(key -> deviceService.subscribeTopicOnline(key.substring(start))); + } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/GlobalScheduleService.java b/src/main/java/com/dji/sample/component/GlobalScheduleService.java index ea44a62..2813388 100644 --- a/src/main/java/com/dji/sample/component/GlobalScheduleService.java +++ b/src/main/java/com/dji/sample/component/GlobalScheduleService.java @@ -1,5 +1,9 @@ package com.dji.sample.component; +import com.dji.sample.component.mqtt.service.IMqttTopicService; +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.model.enums.DeviceDomainEnum; import com.dji.sample.manage.service.IDeviceService; import lombok.extern.slf4j.Slf4j; @@ -7,13 +11,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import java.time.LocalDateTime; -import java.util.Map; +import java.util.Arrays; import java.util.concurrent.TimeUnit; -import static com.dji.sample.manage.model.DeviceStatusManager.DEFAULT_ALIVE_SECOND; -import static com.dji.sample.manage.model.DeviceStatusManager.STATUS_MANAGER; - /** * @author sean.zhou * @date 2021/11/24 @@ -26,33 +26,34 @@ public class GlobalScheduleService { @Autowired private IDeviceService deviceService; + @Autowired + private RedisOpsUtils redisOps; + + @Autowired + private IMqttTopicService topicService; + /** * Check the status of the devices every 30 seconds. It is recommended to use cache. */ - @Scheduled(fixedRate = 30, timeUnit = TimeUnit.SECONDS) + @Scheduled(initialDelay = 30, fixedRate = 30, timeUnit = TimeUnit.SECONDS) private void deviceStatusListen() { - for (Map.Entry entry : STATUS_MANAGER.entrySet()) { - if (entry.getValue().isAfter( - LocalDateTime.now().minusSeconds(DEFAULT_ALIVE_SECOND))) { - continue; - } - - String device = entry.getKey(); - int index = device.indexOf("/"); - - STATUS_MANAGER.remove(device); - - int type = Integer.parseInt(device.substring(0, index)); - String sn = device.substring(index + 1); - // Determine whether it is a gateway device. - if (DeviceDomainEnum.GATEWAY.getVal() == type) { - deviceService.deviceOffline(sn); - deviceService.unsubscribeTopicOffline(sn); - continue; + int start = RedisConst.DEVICE_ONLINE_PREFIX.length(); + + redisOps.getAllKeys(RedisConst.DEVICE_ONLINE_PREFIX + "*").forEach(key -> { + long expire = redisOps.getExpire(key); + if (expire <= 30) { + DeviceDTO device = (DeviceDTO) redisOps.get(key); + if (device.getDomain().equals(DeviceDomainEnum.SUB_DEVICE.getDesc())) { + deviceService.subDeviceOffline(key.substring(start)); + } else { + deviceService.unsubscribeTopicOffline(key.substring(start)); + deviceService.pushDeviceOfflineTopo(device.getWorkspaceId(), device.getDeviceSn()); + } + redisOps.del(key); } + }); - deviceService.subDeviceOffline(sn); - } + log.info("Subscriptions: {}", Arrays.toString(topicService.getSubscribedTopic())); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java b/src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java index df6f04f..3964aaf 100644 --- a/src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java +++ b/src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java @@ -11,6 +11,7 @@ import org.springframework.integration.endpoint.MessageProducerSupport; import org.springframework.integration.mqtt.core.MqttPahoClientFactory; import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; +import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; @@ -62,7 +63,9 @@ public class MqttInboundConfiguration { @ServiceActivator(inputChannel = ChannelName.DEFAULT) public MessageHandler defaultInboundHandler() { return message -> { - log.info("The default channel does not handle messages."); + log.info("The default channel does not handle messages." + + "\nTopic: " + message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC) + + "\nPayload: " + message.getPayload()); }; } 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 2f06938..3d54599 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 @@ -74,7 +74,7 @@ public class MqttMessageChannel { @Bean(name = ChannelName.INBOUND_OSD) public MessageChannel osdChannel() { - return new DirectChannel(); + return new ExecutorChannel(threadPool); } @Bean(name = ChannelName.DEFAULT) @@ -87,4 +87,54 @@ public class MqttMessageChannel { return new DirectChannel(); } + @Bean(name = ChannelName.INBOUND_STATE_FIRMWARE_VERSION) + public MessageChannel stateFirmwareVersionChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_REQUESTS) + public MessageChannel requestsChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET) + public MessageChannel requestsConfigGetChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_EVENTS) + public MessageChannel eventsChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS) + public MessageChannel eventsFlightTaskProgressChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK) + public MessageChannel eventsFileUploadCallbackChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS) + public MessageChannel requestsAirportBindStatusChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET) + public MessageChannel requestsAirportOrganizationGetChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND) + public MessageChannel requestsAirportOrganizationBindChannel() { + return new DirectChannel(); + } + + @Bean(name = ChannelName.INBOUND_EVENTS_HMS) + public MessageChannel eventsHms() { + return new DirectChannel(); + } + } diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/AbstractStateTopicHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/AbstractStateTopicHandler.java new file mode 100644 index 0000000..c3969f3 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/AbstractStateTopicHandler.java @@ -0,0 +1,39 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.redis.RedisOpsUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Map; + +/** + * @author sean + * @version 0.3 + * @date 2022/2/21 + */ +public abstract class AbstractStateTopicHandler { + + protected AbstractStateTopicHandler handler; + + @Autowired + protected ObjectMapper mapper; + + @Autowired + protected RedisOpsUtils redisOps; + + protected AbstractStateTopicHandler(AbstractStateTopicHandler handler) { + this.handler = handler; + } + + /** + * Passing dataNode data, using different processing methods depending on the data selection. + * @param dataNode + * @param stateReceiver + * @param sn + * @return + * @throws JsonProcessingException + */ + public abstract CommonTopicReceiver handleState(Map dataNode, CommonTopicReceiver stateReceiver, String sn) throws JsonProcessingException; +} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/EventsRouter.java b/src/main/java/com/dji/sample/component/mqtt/handler/EventsRouter.java new file mode 100644 index 0000000..a2baabe --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/EventsRouter.java @@ -0,0 +1,48 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.ChannelName; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.mqtt.model.EventsMethodEnum; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.dsl.IntegrationFlow; +import org.springframework.integration.dsl.IntegrationFlows; + +import java.io.IOException; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Configuration +public class EventsRouter { + + @Autowired + private ObjectMapper mapper; + + @Bean + public IntegrationFlow eventsMethodRouterFlow() { + return IntegrationFlows + .from(ChannelName.INBOUND_EVENTS) + .transform(payload -> { + try { + return mapper.readValue(payload, CommonTopicReceiver.class); + } catch (IOException e) { + e.printStackTrace(); + } + return new CommonTopicReceiver(); + }) + .route( + receiver -> EventsMethodEnum.find(receiver.getMethod()), + mapping -> { + mapping.channelMapping(EventsMethodEnum.FILE_UPLOAD_CALLBACK, ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK); + mapping.channelMapping(EventsMethodEnum.FLIGHT_TASK_PROGRESS, ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS); + mapping.channelMapping(EventsMethodEnum.HMS, ChannelName.INBOUND_EVENTS_HMS); + mapping.channelMapping(EventsMethodEnum.UNKNOWN, ChannelName.DEFAULT); + }) + .get(); + } +} 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 new file mode 100644 index 0000000..3047ea5 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java @@ -0,0 +1,118 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.ChannelName; +import lombok.extern.slf4j.Slf4j; +import org.springframework.integration.annotation.Router; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.integration.router.AbstractMessageRouter; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Pattern; + +import static com.dji.sample.component.mqtt.model.TopicConst.*; + +/** + * + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +@Component +@Slf4j +public class InboundMessageRouter extends AbstractMessageRouter { + + @Resource(name = ChannelName.INBOUND) + private MessageChannel inboundChannel; + + @Resource(name = ChannelName.INBOUND_STATUS) + private MessageChannel statusChannel; + + @Resource(name = ChannelName.INBOUND_STATE) + private MessageChannel stateChannel; + + @Resource(name = ChannelName.DEFAULT) + private MessageChannel defaultChannel; + + @Resource(name = ChannelName.INBOUND_SERVICE_REPLY) + private MessageChannel serviceReplyChannel; + + @Resource(name = ChannelName.INBOUND_OSD) + private MessageChannel osdChannel; + + @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. + * @param message message from mqtt broker + * @return channel + */ + @Override + @Router(inputChannel = ChannelName.INBOUND) + protected Collection determineTargetChannels(Message message) { + MessageHeaders headers = message.getHeaders(); + String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); + byte[] payload = (byte[])message.getPayload(); + + // osd + if (PATTERN_TOPIC_OSD.matcher(topic).matches()) { + return Collections.singleton(osdChannel); + } + + log.debug("received topic :{} \t payload :{}", topic, new String(payload)); + + // status + if (PATTERN_TOPIC_STATUS.matcher(topic).matches()) { + return Collections.singleton(statusChannel); + } + + // state + if (PATTERN_TOPIC_STATE.matcher(topic).matches()) { + return Collections.singleton(stateChannel); + } + + // services_reply + if (PATTERN_TOPIC_SERVICE_REPLY.matcher(topic).matches()) { + return Collections.singleton(serviceReplyChannel); + } + + // 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); + } +} 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 new file mode 100644 index 0000000..753bad2 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java @@ -0,0 +1,49 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.ChannelName; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.mqtt.model.RequestsMethodEnum; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.dsl.IntegrationFlow; +import org.springframework.integration.dsl.IntegrationFlows; + +import java.io.IOException; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/25 + */ +@Configuration +public class RequestsRouter { + + @Autowired + private ObjectMapper mapper; + + @Bean + public IntegrationFlow requestsMethodRouterFlow() { + return IntegrationFlows + .from(ChannelName.INBOUND_REQUESTS) + .transform(payload -> { + try { + return mapper.readValue(payload, CommonTopicReceiver.class); + } catch (IOException e) { + e.printStackTrace(); + } + return new CommonTopicReceiver(); + }) + .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); + }) + .get(); + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/StateDefaultHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/StateDefaultHandler.java new file mode 100644 index 0000000..a1d2624 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/StateDefaultHandler.java @@ -0,0 +1,26 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * @author sean + * @version 0.3 + * @date 2022/3/21 + */ +@Service +public class StateDefaultHandler extends AbstractStateTopicHandler { + + protected StateDefaultHandler() { + super(null); + } + + @Override + public CommonTopicReceiver handleState(Map dataNode, CommonTopicReceiver stateReceiver, String sn) throws JsonProcessingException { + // If no suitable handler is found for the data, it is not processed. + return stateReceiver; + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/StateDeviceBasicHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/StateDeviceBasicHandler.java new file mode 100644 index 0000000..b85ebca --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/StateDeviceBasicHandler.java @@ -0,0 +1,38 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.mqtt.model.StateDataEnum; +import com.dji.sample.manage.model.receiver.DeviceBasicReceiver; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * @author sean + * @version 0.3 + * @date 2022/2/21 + */ +@Service +public class StateDeviceBasicHandler extends AbstractStateTopicHandler { + + public StateDeviceBasicHandler(@Autowired @Qualifier("stateLiveCapacityHandler") AbstractStateTopicHandler handler) { + super(handler); + } + + @Override + public CommonTopicReceiver handleState(Map dataNode, CommonTopicReceiver stateReceiver, String sn) throws JsonProcessingException { + // handle device basic data + if (dataNode.containsKey(StateDataEnum.PAYLOADS.getDesc())) { + DeviceBasicReceiver data = mapper.convertValue(stateReceiver.getData(), DeviceBasicReceiver.class); + data.setDeviceSn(sn); + data.getPayloads().forEach(payload -> payload.setDeviceSn(sn)); + + stateReceiver.setData(data); + return stateReceiver; + } + return handler.handleState(dataNode, stateReceiver, sn); + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/StateFirmwareVersionHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/StateFirmwareVersionHandler.java new file mode 100644 index 0000000..aceeae4 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/StateFirmwareVersionHandler.java @@ -0,0 +1,62 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.mqtt.model.StateDataEnum; +import com.dji.sample.manage.model.enums.DeviceDomainEnum; +import com.dji.sample.manage.model.enums.PayloadModelEnum; +import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * @author sean + * @version 0.3 + * @date 2022/2/21 + */ +@Service +public class StateFirmwareVersionHandler extends AbstractStateTopicHandler { + + protected StateFirmwareVersionHandler(@Autowired @Qualifier("stateDefaultHandler") AbstractStateTopicHandler handler) { + super(handler); + } + + @Override + public CommonTopicReceiver handleState(Map dataNode, CommonTopicReceiver stateReceiver, String sn) throws JsonProcessingException { + // Parse the firmware version of the device. + if (dataNode.containsKey(StateDataEnum.FIRMWARE_VERSION.getDesc())) { + FirmwareVersionReceiver firmware = mapper.convertValue(dataNode, FirmwareVersionReceiver.class); + firmware.setSn(sn); + firmware.setDomain(DeviceDomainEnum.SUB_DEVICE); + stateReceiver.setData(firmware); + return stateReceiver; + } + + // Parse the firmware version of the payload. + List payloads = PayloadModelEnum.getAllModel(); + long count = dataNode.keySet() + .stream() + .map(key -> { + int end = key.indexOf("-"); + return end == -1 ? key : key.substring(0, end); + }) + .filter(payloads::contains) + .count(); + if (count > 0) { + FirmwareVersionReceiver firmware = FirmwareVersionReceiver.builder() + .firmwareVersion(((Map)(dataNode.values().iterator().next())) + .get(StateDataEnum.FIRMWARE_VERSION.getDesc())) + .sn(sn) + .domain(DeviceDomainEnum.PAYLOAD) + .build(); + stateReceiver.setData(firmware); + return stateReceiver; + } + + return handler.handleState(dataNode, stateReceiver, sn); + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/StateLiveCapacityHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/StateLiveCapacityHandler.java new file mode 100644 index 0000000..dc593ab --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/StateLiveCapacityHandler.java @@ -0,0 +1,39 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.mqtt.model.StateDataEnum; +import com.dji.sample.manage.model.receiver.LiveCapacityReceiver; +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * @author sean + * @version 0.3 + * @date 2022/2/21 + */ +@Service +@Slf4j +public class StateLiveCapacityHandler extends AbstractStateTopicHandler { + + protected StateLiveCapacityHandler(@Autowired @Qualifier("stateFirmwareVersionHandler") AbstractStateTopicHandler handler) { + super(handler); + } + + @Override + public CommonTopicReceiver handleState(Map dataNode, CommonTopicReceiver stateReceiver, String sn) throws JsonProcessingException { + // Determine if it is live capacity data based on name. + if (dataNode.containsKey(StateDataEnum.LIVE_CAPACITY.getDesc())) { + stateReceiver.setData(mapper.convertValue( + dataNode.get(StateDataEnum.LIVE_CAPACITY.getDesc()), + LiveCapacityReceiver.class)); + log.info("Analyze live stream capabilities."); + return stateReceiver; + } + return handler.handleState(dataNode, stateReceiver, sn); + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/StateRouter.java b/src/main/java/com/dji/sample/component/mqtt/handler/StateRouter.java new file mode 100644 index 0000000..4f0e1e5 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/StateRouter.java @@ -0,0 +1,104 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.ChannelName; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.manage.model.receiver.DeviceBasicReceiver; +import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; +import com.dji.sample.manage.model.receiver.LiveCapacityReceiver; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.MessageEndpoint; +import org.springframework.integration.annotation.Router; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.annotation.Splitter; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.integration.router.MessageRouter; +import org.springframework.integration.router.PayloadTypeRouter; +import org.springframework.messaging.Message; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import static com.dji.sample.component.mqtt.model.TopicConst.*; + +/** + * + * @author sean.zhou + * @date 2021/11/17 + * @version 0.1 + */ +@MessageEndpoint +@Slf4j +@Configuration +public class StateRouter { + + @Resource(name = "stateDeviceBasicHandler") + private AbstractStateTopicHandler handler; + + @Autowired + private ObjectMapper mapper; + + /** + * Handles the routing of state topic messages. Depending on the data, it is assigned to different channels for handling. + * @param message + * @return + * @throws IOException + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE, outputChannel = ChannelName.INBOUND_STATE_SPLITTER) + public CommonTopicReceiver resolveStateData(Message message) throws IOException { + byte[] payload = (byte[])message.getPayload(); + String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString(); + + CommonTopicReceiver stateReceiver = mapper.readValue(payload, CommonTopicReceiver.class); + // Get the sn of the topic source. + String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), + topic.indexOf(STATE_SUF)); + + try { + Map data = (Map) (stateReceiver.getData()); + + return handler.handleState(data, stateReceiver, from); + + } catch (UnrecognizedPropertyException e) { + log.info("The {} data is not processed.", e.getPropertyName()); + } + return stateReceiver; + } + + /** + * Split the state message data to different channels for handling according to their different types. + * @param receiver state message + * @return + */ + @Splitter(inputChannel = ChannelName.INBOUND_STATE_SPLITTER, outputChannel = ChannelName.INBOUND_STATE_ROUTER) + public Collection splitState(CommonTopicReceiver receiver) { + ArrayList type = new ArrayList<>(); + type.add(receiver.getData()); + return type; + } + + @Bean + @Router(inputChannel = ChannelName.INBOUND_STATE_ROUTER) + public MessageRouter resolveStateRouter() { + PayloadTypeRouter router = new PayloadTypeRouter(); + // Channel mapping for basic data. + router.setChannelMapping(DeviceBasicReceiver.class.getName(), + ChannelName.INBOUND_STATE_BASIC); + // Channel mapping for live streaming capabilities. + router.setChannelMapping(LiveCapacityReceiver.class.getName(), + ChannelName.INBOUND_STATE_CAPACITY); + router.setChannelMapping(FirmwareVersionReceiver.class.getName(), + ChannelName.INBOUND_STATE_FIRMWARE_VERSION); + router.setChannelMapping(Map.class.getName(), + ChannelName.DEFAULT); + return router; + } + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/StatusRouter.java b/src/main/java/com/dji/sample/component/mqtt/handler/StatusRouter.java new file mode 100644 index 0000000..0b34137 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/handler/StatusRouter.java @@ -0,0 +1,67 @@ +package com.dji.sample.component.mqtt.handler; + +import com.dji.sample.component.mqtt.model.ChannelName; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; +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.MessageEndpoint; +import org.springframework.integration.annotation.Router; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.Message; +import org.springframework.util.CollectionUtils; + +import static com.dji.sample.component.mqtt.model.TopicConst.*; + +/** + * + * @author sean.zhou + * @date 2021/11/12 + * @version 0.1 + */ +@MessageEndpoint +public class StatusRouter { + + @Autowired + private ObjectMapper mapper; + + /** + * Converts the status data sent by the gateway device into an object. + * @param message + * @return + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS, outputChannel = ChannelName.INBOUND_STATUS_ROUTER) + public CommonTopicReceiver resolveStatus(Message message) { + CommonTopicReceiver statusReceiver = new CommonTopicReceiver<>(); + try { + statusReceiver = mapper.readValue( + (byte[])message.getPayload(), + new TypeReference>() {}); + + String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString(); + + // set gateway's sn + statusReceiver.getData().setSn( + topic.substring((BASIC_PRE + PRODUCT).length(), + topic.indexOf(STATUS_SUF))); + } catch (Exception e) { + e.printStackTrace(); + } + return statusReceiver; + } + + /** + * Handles the routing of status topic messages. Depending on the data, it is assigned to different channels for handling. + * @param receiver + * @return + */ + @Router(inputChannel = ChannelName.INBOUND_STATUS_ROUTER) + public String resolveStatusRouter(CommonTopicReceiver receiver) { + // Determine whether the drone is online or offline according to whether the data of the sub-device is empty. + return CollectionUtils.isEmpty(receiver.getData().getSubDevices()) ? + ChannelName.INBOUND_STATUS_OFFLINE : ChannelName.INBOUND_STATUS_ONLINE; + } + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/model/Chan.java b/src/main/java/com/dji/sample/component/mqtt/model/Chan.java new file mode 100644 index 0000000..8fa6c64 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/Chan.java @@ -0,0 +1,45 @@ +package com.dji.sample.component.mqtt.model; + +import java.util.concurrent.locks.LockSupport; + +/** + * The demo is only for functional closure, which is not recommended. + * @author sean.zhou + * @date 2021/11/22 + * @version 0.1 + */ +public class Chan { + + private static final long THREAD_WAIT_TIME = 1000_000 * 2000; + + private volatile T data; + + private volatile Thread t; + + private Chan () { + + } + + public static Chan getInstance() { + return ChanSingleton.INSTANCE; + } + + public T get(Object blocker) { + this.t = Thread.currentThread(); + LockSupport.parkNanos(blocker, THREAD_WAIT_TIME); + this.t = null; + return data; + } + + public void put(T data) { + this.data = data; + if (t == null) { + return; + } + LockSupport.unpark(t); + } + + private static class ChanSingleton { + private static final Chan INSTANCE = new Chan<>(); + } +} \ No newline at end of file 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 c8f5f01..08cec5a 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 @@ -43,4 +43,23 @@ public class ChannelName { public static final String OUTBOUND = "outbound"; + public static final String INBOUND_STATE_FIRMWARE_VERSION = "inboundStateFirmwareVersion"; + + public static final String INBOUND_REQUESTS = "inboundRequests"; + + public static final String INBOUND_REQUESTS_STORAGE_CONFIG_GET = "inboundRequestsConfigGet"; + + public static final String INBOUND_EVENTS = "inboundEvents"; + + public static final String INBOUND_EVENTS_FLIGHT_TASK_PROGRESS = "inboundEventsFlightTaskProgress"; + + public static final String INBOUND_EVENTS_FILE_UPLOAD_CALLBACK = "inboundEventsFileUploadCallback"; + + public static final String INBOUND_REQUESTS_AIRPORT_BIND_STATUS = "inboundRequestsAirportBindStatus"; + + public static final String INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET = "inboundRequestsAirportOrganizationGet"; + + public static final String INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND = "inboundRequestsAirportOrganizationBind"; + + public static final String INBOUND_EVENTS_HMS = "inboundEventsHms"; } 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 5a6fe45..8770bfc 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 @@ -26,4 +26,10 @@ public class CommonTopicReceiver { private Long timestamp; private T data; + + private String gateway; + + private Integer needReply; + + private String from; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicResponse.java b/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicResponse.java index effd5c1..e8055c0 100644 --- a/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicResponse.java +++ b/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicResponse.java @@ -30,4 +30,6 @@ public class CommonTopicResponse { private String method; private T data; + + private Long timestamp; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/model/ErrorInfoReply.java b/src/main/java/com/dji/sample/component/mqtt/model/ErrorInfoReply.java new file mode 100644 index 0000000..160a86e --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/ErrorInfoReply.java @@ -0,0 +1,23 @@ +package com.dji.sample.component.mqtt.model; + +import com.dji.sample.common.model.ResponseResult; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/14 + */ +@Data +@AllArgsConstructor +public class ErrorInfoReply { + + private String sn; + + private Integer errCode; + + public static ErrorInfoReply success(String sn) { + return new ErrorInfoReply(sn, ResponseResult.CODE_SUCCESS); + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/EventsMethodEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/EventsMethodEnum.java new file mode 100644 index 0000000..2a71ac2 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/EventsMethodEnum.java @@ -0,0 +1,36 @@ +package com.dji.sample.component.mqtt.model; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +public enum EventsMethodEnum { + + FLIGHT_TASK_PROGRESS("flighttask_progress"), + + FILE_UPLOAD_CALLBACK("file_upload_callback"), + + HMS("hms"), + + UNKNOWN("Unknown"); + + private String method; + + EventsMethodEnum(String method) { + this.method = method; + } + + public String getMethod() { + return method; + } + + public static EventsMethodEnum find(String method) { + return Arrays.stream(EventsMethodEnum.values()) + .filter(methodEnum -> methodEnum.method.equals(method)) + .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 new file mode 100644 index 0000000..bff226d --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java @@ -0,0 +1,21 @@ +package com.dji.sample.component.mqtt.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class EventsReceiver { + + private Integer result; + + private T output; + + private String bid; + +} 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 new file mode 100644 index 0000000..97433ff --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java @@ -0,0 +1,29 @@ +package com.dji.sample.component.mqtt.model; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/14 + */ +public final class MapKeyConst { + + private MapKeyConst(){ + + } + + public static final String ORGANIZATION_NAME = "organization_name"; + + public static final String DEVICES = "devices"; + + public static final String SN = "sn"; + + public static final String BIND_DEVICES = "bind_devices"; + + public static final String ERR_INFOS = "err_infos"; + + public static final String TINY_FINGERPRINTS = "tiny_fingerprints"; + + public static final String BIND_STATUS = "bind_status"; + + public static final String LIST = "list"; +} 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 new file mode 100644 index 0000000..ddfa599 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java @@ -0,0 +1,38 @@ +package com.dji.sample.component.mqtt.model; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/25 + */ +public enum RequestsMethodEnum { + + STORAGE_CONFIG_GET("storage_config_get"), + + AIRPORT_BIND_STATUS("airport_bind_status"), + + AIRPORT_ORGANIZATION_BIND("airport_organization_bind"), + + AIRPORT_ORGANIZATION_GET("airport_organization_get"), + + UNKNOWN("Unknown"); + + private String method; + + RequestsMethodEnum(String method) { + this.method = method; + } + + public String getMethod() { + return method; + } + + public static RequestsMethodEnum find(String method) { + return Arrays.stream(RequestsMethodEnum.values()) + .filter(methodEnum -> methodEnum.method.equals(method)) + .findAny() + .orElse(UNKNOWN); + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/RequestsReply.java b/src/main/java/com/dji/sample/component/mqtt/model/RequestsReply.java new file mode 100644 index 0000000..e788466 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/RequestsReply.java @@ -0,0 +1,44 @@ +package com.dji.sample.component.mqtt.model; + +import com.dji.sample.common.error.IErrorInfo; +import com.dji.sample.common.model.ResponseResult; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/13 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RequestsReply { + + private Integer result; + + private T output; + + + public static RequestsReply error(IErrorInfo errorInfo) { + return RequestsReply.builder() + .result(errorInfo.getErrorCode()) + .output(errorInfo.getErrorMsg()) + .build(); + } + + public static RequestsReply success(T data) { + return RequestsReply.builder() + .result(ResponseResult.CODE_SUCCESS) + .output(data) + .build(); + } + public static RequestsReply success() { + return RequestsReply.builder() + .result(ResponseResult.CODE_SUCCESS) + .build(); + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/ServiceReply.java b/src/main/java/com/dji/sample/component/mqtt/model/ServiceReply.java new file mode 100644 index 0000000..d0b1562 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/ServiceReply.java @@ -0,0 +1,20 @@ +package com.dji.sample.component.mqtt.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/22 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ServiceReply { + + private Integer result; + + private T info; + + private T output; +} \ No newline at end of file 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 new file mode 100644 index 0000000..4f56b15 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/ServicesMethodEnum.java @@ -0,0 +1,29 @@ +package com.dji.sample.component.mqtt.model; + +/** + * @author sean.zhou + * @date 2021/11/22 + * @version 0.1 + */ +public enum ServicesMethodEnum { + + LIVE_START_PUSH("live_start_push"), + + LIVE_STOP_PUSH("live_stop_push"), + + LIVE_SET_QUALITY("live_set_quality"), + + FLIGHTTASK_CREATE("flighttask_create"), + + UNKNOWN("unknown"); + + private String method; + + ServicesMethodEnum(String method) { + this.method = method; + } + + public String getMethod() { + return method; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/model/StateDataEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/StateDataEnum.java new file mode 100644 index 0000000..82bed19 --- /dev/null +++ b/src/main/java/com/dji/sample/component/mqtt/model/StateDataEnum.java @@ -0,0 +1,26 @@ +package com.dji.sample.component.mqtt.model; + +/** + * + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public enum StateDataEnum { + + FIRMWARE_VERSION("firmware_version"), + + LIVE_CAPACITY("live_capacity"), + + PAYLOADS("payloads"); + + private String desc; + + StateDataEnum(String desc) { + this.desc = desc; + } + + public String getDesc() { + return this.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 667c969..dc9df7c 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 @@ -24,6 +24,10 @@ public class TopicConst { public static final String OSD_SUF = "/osd"; + public static final String REQUESTS_SUF = "/requests"; + + public static final String EVENTS_SUF = "/events"; + 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 7930d41..152d6a8 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 @@ -1,6 +1,9 @@ package com.dji.sample.component.mqtt.service; import com.dji.sample.component.mqtt.model.CommonTopicResponse; +import com.dji.sample.component.mqtt.model.ServiceReply; + +import java.util.Optional; /** * @author sean.zhou @@ -24,4 +27,11 @@ public interface IMessageSenderService { */ void publish(String topic, int qos, CommonTopicResponse response); + /** + * Send live streaming start message and receive a response at the same time + * @param topic + * @param response notification of whether the start is successful. + * @return + */ + Optional publishWithReply(String topic, CommonTopicResponse response); } 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 2510ead..ae24b37 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 @@ -1,15 +1,20 @@ package com.dji.sample.component.mqtt.service.impl; +import com.dji.sample.component.mqtt.model.Chan; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.component.mqtt.model.CommonTopicResponse; +import com.dji.sample.component.mqtt.model.ServiceReply; import com.dji.sample.component.mqtt.service.IMessageSenderService; import com.dji.sample.component.mqtt.service.IMqttMessageGateway; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; + /** * @author sean.zhou * @date 2021/11/16 @@ -22,11 +27,11 @@ public class MessageSenderServiceImpl implements IMessageSenderService { @Autowired private IMqttMessageGateway messageGateway; + @Autowired + private ObjectMapper mapper; + public void publish(String topic, CommonTopicResponse response) { try { - ObjectMapper mapper = new ObjectMapper(); - // Only parameters whose value is not null will be serialised. - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); messageGateway.publish(topic, mapper.writeValueAsBytes(response)); } catch (JsonProcessingException e) { @@ -37,14 +42,31 @@ public class MessageSenderServiceImpl implements IMessageSenderService { public void publish(String topic, int qos, CommonTopicResponse response) { try { - ObjectMapper mapper = new ObjectMapper(); - // Only parameters whose value is not null will be serialised. - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - messageGateway.publish(topic, mapper.writeValueAsBytes(response), qos); } catch (JsonProcessingException e) { log.info("Failed to publish the message. {}", response.toString()); e.printStackTrace(); } } + + public Optional publishWithReply(String topic, CommonTopicResponse response) { + AtomicInteger time = new AtomicInteger(0); + // Retry three times + while (time.getAndIncrement() < 3) { + this.publish(topic, response); + + Chan> chan = Chan.getInstance(); + // If the message is not received in 0.5 seconds then resend it again. + CommonTopicReceiver receiver = chan.get(response.getMethod()); + if (receiver == null) { + continue; + } + // Need to match tid and bid. + if (receiver.getTid().equals(response.getTid()) && + receiver.getBid().equals(response.getBid())) { + return Optional.ofNullable(receiver.getData()); + } + } + return Optional.empty(); + } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mybatis/MybatisPlusMetaObjectHandler.java b/src/main/java/com/dji/sample/component/mybatis/MybatisPlusMetaObjectHandler.java index 88170ed..ffd6bb1 100644 --- a/src/main/java/com/dji/sample/component/mybatis/MybatisPlusMetaObjectHandler.java +++ b/src/main/java/com/dji/sample/component/mybatis/MybatisPlusMetaObjectHandler.java @@ -5,7 +5,7 @@ import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.time.LocalDateTime; -import java.time.ZoneOffset; +import java.time.ZoneId; /** * Automatic filling for set values @@ -20,9 +20,9 @@ public class MybatisPlusMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", Long.class, - LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli()); + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); this.strictInsertFill(metaObject, "updateTime", Long.class, - LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli()); + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } /** @@ -32,6 +32,6 @@ public class MybatisPlusMetaObjectHandler implements MetaObjectHandler { @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", Long.class, - LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli()); + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } } diff --git a/src/main/java/com/dji/sample/component/oss/model/OssConfiguration.java b/src/main/java/com/dji/sample/component/oss/model/OssConfiguration.java new file mode 100644 index 0000000..491ee44 --- /dev/null +++ b/src/main/java/com/dji/sample/component/oss/model/OssConfiguration.java @@ -0,0 +1,94 @@ +package com.dji.sample.component.oss.model; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/9 + */ +@ConfigurationProperties(prefix = "oss") +@Component +@Data +public class OssConfiguration { + + /** + * @see com.dji.sample.component.oss.model.enums.OssTypeEnum + */ + private String provider; + + /** + * Whether to use the object storage service. + */ + private boolean enable; + + /** + * The protocol needs to be included at the beginning of the address. + */ + private String endpoint; + + private String accessKey; + + private String secretKey; + + private String region; + + private Long expire; + + private String roleSessionName; + + private String roleArn; + + private String bucket; + + private String objectDirPrefix; + + public void setProvider(String provider) { + this.provider = provider; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public void setRegion(String region) { + this.region = region; + } + + public void setExpire(Long expire) { + this.expire = expire; + } + + public void setRoleSessionName(String roleSessionName) { + this.roleSessionName = roleSessionName; + } + + public void setRoleArn(String roleArn) { + this.roleArn = roleArn; + } + + public void setBucket(String bucket) { + this.bucket = bucket; + } + + public void setObjectDirPrefix(String objectDirPrefix) { + this.objectDirPrefix = objectDirPrefix; + } +} + + + diff --git a/src/main/java/com/dji/sample/component/oss/model/enums/OssTypeEnum.java b/src/main/java/com/dji/sample/component/oss/model/enums/OssTypeEnum.java new file mode 100644 index 0000000..58e608c --- /dev/null +++ b/src/main/java/com/dji/sample/component/oss/model/enums/OssTypeEnum.java @@ -0,0 +1,28 @@ +package com.dji.sample.component.oss.model.enums; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/30 + */ +public enum OssTypeEnum { + + ALIYUN("ali"), + + AWS("aws"), + + /* + * MinIO is temporarily unavailable. + */ + MINIO("minio"); + + private String type; + + OssTypeEnum(String type) { + this.type = type; + } + + public String getType() { + return type; + } +} 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 beb9db6..53746ef 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 @@ -11,6 +11,8 @@ import java.net.URL; */ public interface IOssService { + String getOssType(); + /** * Get temporary credentials. * @return @@ -24,4 +26,20 @@ public interface IOssService { * @return download link */ URL getObjectUrl(String bucket, String objectKey); + + /** + * Deletes the object in the storage bucket. + * @param bucket + * @param objectKey + * @return + */ + Boolean deleteObject(String bucket, String objectKey); + + /** + * Get the contents of an object. + * @param bucket + * @param objectKey + * @return + */ + byte[] getObject(String bucket, String objectKey); } 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 5ffae6c..4767aec 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 @@ -1,13 +1,16 @@ package com.dji.sample.component.oss.service.impl; import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.model.OSSObject; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest; import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse; -import com.dji.sample.component.oss.model.AliyunOSSConfiguration; +import com.dji.sample.component.oss.model.OssConfiguration; +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; @@ -15,6 +18,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.Date; @@ -27,24 +32,29 @@ import java.util.Date; @Slf4j public class AliyunOssServiceImpl implements IOssService { - @Autowired(required = false) - private OSS ossClient; + @Autowired + public OssConfiguration configuration; + + @Override + public String getOssType() { + return OssTypeEnum.ALIYUN.getType(); + } @Override public CredentialsDTO getCredentials() { try { DefaultProfile profile = DefaultProfile.getProfile( - AliyunOSSConfiguration.region, AliyunOSSConfiguration.accessKey, AliyunOSSConfiguration.secretKey); + configuration.getRegion(), configuration.getAccessKey(), configuration.getSecretKey()); IAcsClient client = new DefaultAcsClient(profile); AssumeRoleRequest request = new AssumeRoleRequest(); - request.setDurationSeconds(AliyunOSSConfiguration.expire); - request.setRoleArn(AliyunOSSConfiguration.roleArn); - request.setRoleSessionName(AliyunOSSConfiguration.roleSessionName); + request.setDurationSeconds(configuration.getExpire()); + request.setRoleArn(configuration.getRoleArn()); + request.setRoleSessionName(configuration.getRoleSessionName()); AssumeRoleResponse response = client.getAcsResponse(request); - return new CredentialsDTO(response.getCredentials(), AliyunOSSConfiguration.expire); + return new CredentialsDTO(response.getCredentials(), configuration.getExpire()); } catch (ClientException e) { log.debug("Failed to obtain sts."); @@ -58,16 +68,45 @@ public class AliyunOssServiceImpl implements IOssService { if (!StringUtils.hasText(bucket) || !StringUtils.hasText(objectKey)) { return null; } - try { - // First check if the object can be fetched. - ossClient.getObject(bucket, objectKey); + OSS ossClient = this.createClient(); + // First check if the object can be fetched. + ossClient.getObject(bucket, objectKey); + + return ossClient.generatePresignedUrl(bucket, objectKey, + new Date(System.currentTimeMillis() + configuration.getExpire() * 1000)); + } + + @Override + public Boolean deleteObject(String bucket, String objectKey) { + OSS ossClient = this.createClient(); + ossClient.deleteObject(bucket, objectKey); + ossClient.shutdown(); + return true; + } + + @Override + public byte[] getObject(String bucket, String objectKey) { + OSS ossClient = this.createClient(); + OSSObject object = ossClient.getObject(bucket, objectKey); + InputStream stream = object.getObjectContent(); - return ossClient.generatePresignedUrl(bucket, objectKey, - new Date(System.currentTimeMillis() + AliyunOSSConfiguration.expire * 1000)); - } catch (NullPointerException e) { + try { + return stream.readAllBytes(); + } catch (IOException e) { e.printStackTrace(); + } finally { + try { + stream.close(); + ossClient.shutdown(); + } catch (IOException e) { + e.printStackTrace(); + } } - return null; + return new byte[0]; } + private OSS createClient() { + return new OSSClientBuilder() + .build(configuration.getEndpoint(), configuration.getAccessKey(), configuration.getSecretKey()); + } } 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 new file mode 100644 index 0000000..6d0b8af --- /dev/null +++ b/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java @@ -0,0 +1,135 @@ +package com.dji.sample.component.oss.service.impl; + +import com.amazonaws.HttpMethod; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +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.securitytoken.AWSSecurityTokenService; +import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; +import com.amazonaws.services.securitytoken.model.AssumeRoleRequest; +import com.amazonaws.services.securitytoken.model.AssumeRoleResult; +import com.amazonaws.services.securitytoken.model.Credentials; +import com.dji.sample.component.AuthInterceptor; +import com.dji.sample.component.oss.model.OssConfiguration; +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/27 + */ +@Service +public class AmazonS3ServiceImpl implements IOssService { + + @Autowired + private OssConfiguration configuration; + + @Override + public String getOssType() { + return OssTypeEnum.AWS.getType(); + } + + @Override + public CredentialsDTO getCredentials() { + AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider( + new BasicAWSCredentials(configuration.getAccessKey(), configuration.getSecretKey()))) + .withRegion(configuration.getRegion()).build(); + + AssumeRoleRequest request = new AssumeRoleRequest() + .withRoleArn(configuration.getRoleArn()) + .withRoleSessionName(configuration.getRoleSessionName()) + .withDurationSeconds(Math.toIntExact(configuration.getExpire())); + AssumeRoleResult result = stsClient.assumeRole(request); + Credentials credentials = result.getCredentials(); + stsClient.shutdown(); + return new CredentialsDTO(credentials); + } + + @Override + public URL getObjectUrl(String bucket, String objectKey) { + AmazonS3 client = this.createClient(); + URL url = client.generatePresignedUrl(bucket, objectKey, + new Date(System.currentTimeMillis() + configuration.getExpire() * 1000), HttpMethod.GET); + client.shutdown(); + return url; + } + + @Override + public Boolean deleteObject(String bucket, String objectKey) { + AmazonS3 client = this.createClient(); + client.deleteObject(bucket, objectKey); + client.shutdown(); + return true; + } + + public byte[] getObject(String bucket, String objectKey) { + AmazonS3 client = this.createClient(); + S3Object object = client.getObject(bucket, objectKey); + InputStream stream = object.getObjectContent().getDelegateStream(); + try { + return stream.readAllBytes(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + stream.close(); + client.shutdown(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return new byte[0]; + } + + private AmazonS3 createClient() { + return AmazonS3ClientBuilder.standard() + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(configuration.getAccessKey(), configuration.getSecretKey()))) + .withRegion(configuration.getRegion()) + .build(); + } + + /** + * Configuring cross-origin resource sharing + */ + @PostConstruct + private void configCORS() { + if (!configuration.isEnable() || !OssTypeEnum.AWS.getType().equals(configuration.getProvider())) { + return; + } + List allowedMethods = new ArrayList<>(); + allowedMethods.add(CORSRule.AllowedMethods.GET); + allowedMethods.add(CORSRule.AllowedMethods.POST); + allowedMethods.add(CORSRule.AllowedMethods.DELETE); + + CORSRule rule = new CORSRule() + .withId("CORSAccessRule") + .withAllowedOrigins(List.of("*")) + .withAllowedHeaders(List.of(AuthInterceptor.PARAM_TOKEN)) + .withAllowedMethods(allowedMethods); + + AmazonS3 client = this.createClient(); + + client.setBucketCrossOriginConfiguration(this.configuration.getBucket(), + new BucketCrossOriginConfiguration().withRules(rule)); + client.shutdown(); + } +} 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 76d4e5d..633c35f 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 @@ -1,6 +1,7 @@ package com.dji.sample.component.oss.service.impl; -import com.dji.sample.component.oss.model.MinIOConfiguration; +import com.dji.sample.component.oss.model.OssConfiguration; +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.GetPresignedObjectUrlArgs; @@ -26,16 +27,21 @@ import java.security.NoSuchAlgorithmException; @Slf4j public class MinIOServiceImpl implements IOssService { - @Autowired(required = false) - private MinioClient client; + @Autowired + private OssConfiguration configuration; + + @Override + public String getOssType() { + return OssTypeEnum.MINIO.getType(); + } @Override public CredentialsDTO getCredentials() { try { - AssumeRoleProvider provider = new AssumeRoleProvider(MinIOConfiguration.endpoint, MinIOConfiguration.accessKey, - MinIOConfiguration.secretKey, MinIOConfiguration.expire, - null, null, null, null, null, null); - return new CredentialsDTO(provider.fetch(), MinIOConfiguration.expire); + AssumeRoleProvider provider = new AssumeRoleProvider(configuration.getEndpoint(), configuration.getAccessKey(), + configuration.getSecretKey(), Math.toIntExact(configuration.getExpire()), + null, configuration.getRegion(), null, null, null, null); + return new CredentialsDTO(provider.fetch(), Math.toIntExact(configuration.getExpire())); } catch (NoSuchAlgorithmException e) { log.debug("Failed to obtain sts."); e.printStackTrace(); @@ -47,20 +53,38 @@ public class MinIOServiceImpl implements IOssService { public URL getObjectUrl(String bucket, String objectKey) { try { return new URL( - client.getPresignedObjectUrl( - GetPresignedObjectUrlArgs.builder() - .method(Method.GET) - .bucket(bucket) - .object(objectKey) - .expiry(MinIOConfiguration.expire) - .build())); + this.createClient() + .getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Method.GET) + .bucket(bucket) + .object(objectKey) + .expiry(Math.toIntExact(configuration.getExpire())) + .build())); } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | XmlParserException | ServerException e) { - log.error("The file does not exist on the oss."); + log.error("The file does not exist on the OssConfiguration."); e.printStackTrace(); } return null; } + @Override + public Boolean deleteObject(String bucket, String objectKey) { + return null; + } + + @Override + public byte[] getObject(String bucket, String objectKey) { + return new byte[0]; + } + + private MinioClient createClient() { + return MinioClient.builder() + .endpoint(configuration.getEndpoint()) + .credentials(configuration.getAccessKey(), configuration.getSecretKey()) + .region(configuration.getRegion()) + .build(); + } } diff --git a/src/main/java/com/dji/sample/component/oss/service/impl/OssAspectHandler.java b/src/main/java/com/dji/sample/component/oss/service/impl/OssAspectHandler.java new file mode 100644 index 0000000..f7f9223 --- /dev/null +++ b/src/main/java/com/dji/sample/component/oss/service/impl/OssAspectHandler.java @@ -0,0 +1,33 @@ +package com.dji.sample.component.oss.service.impl; + +import com.dji.sample.component.oss.model.OssConfiguration; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/20 + */ +@Component +@Aspect +public class OssAspectHandler { + + @Autowired + private OssServiceContext ossServiceContext; + + @Autowired + private OssConfiguration configuration; + + @Before("execution(public * com.dji.sample.component.oss.service.impl.OssServiceContext.*(..))") + public void before() { + if (!this.configuration.isEnable()) { + throw new IllegalArgumentException("Please enable OssConfiguration."); + } + if (this.ossServiceContext.getOssService() == null) { + throw new IllegalArgumentException("Please check the OssConfiguration configuration."); + } + } +} 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 new file mode 100644 index 0000000..9ac8059 --- /dev/null +++ b/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java @@ -0,0 +1,58 @@ +package com.dji.sample.component.oss.service.impl; + +import com.dji.sample.component.oss.model.OssConfiguration; +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.net.URL; +import java.util.Arrays; +import java.util.List; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/30 + */ +@Service +public class OssServiceContext { + + private IOssService ossService; + + private OssConfiguration configuration; + + @Autowired + public OssServiceContext(List ossServices, OssConfiguration configuration) { + this.configuration = configuration; + if (!configuration.isEnable()) { + return; + } + this.ossService = ossServices.stream() + .filter(ossService -> ossService.getOssType().equals(configuration.getProvider())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Oss provider is illegal. Optional: " + + Arrays.toString(Arrays.stream(OssTypeEnum.values()).map(OssTypeEnum::getType).toArray()))); + } + + IOssService getOssService() { + return this.ossService; + } + + public CredentialsDTO getCredentials() { + return this.ossService.getCredentials(); + } + + public URL getObjectUrl(String bucket, String objectKey) { + return this.ossService.getObjectUrl(bucket, objectKey); + } + + public Boolean deleteObject(String bucket, String objectKey) { + return this.ossService.deleteObject(bucket, objectKey); + } + + public byte[] getObject(String bucket, String objectKey) { + return this.ossService.getObject(bucket, objectKey); + } +} diff --git a/src/main/java/com/dji/sample/component/redis/RedisConfiguration.java b/src/main/java/com/dji/sample/component/redis/RedisConfiguration.java new file mode 100644 index 0000000..f73261e --- /dev/null +++ b/src/main/java/com/dji/sample/component/redis/RedisConfiguration.java @@ -0,0 +1,63 @@ +package com.dji.sample.component.redis; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/19 + */ +@Configuration +@EnableRedisRepositories +public class RedisConfiguration { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory factory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(factory); + + ObjectMapper objectMapper = new ObjectMapper(); + JavaTimeModule timeModule = new JavaTimeModule(); + timeModule.addDeserializer(LocalDateTime.class, + new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + timeModule.addSerializer(LocalDateTime.class, + new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS); + objectMapper.registerModules(timeModule); + objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), + ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + + objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + + StringRedisSerializer serializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(serializer); + redisTemplate.setHashKeySerializer(serializer); + + GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper); + redisTemplate.setValueSerializer(jsonRedisSerializer); + redisTemplate.setHashValueSerializer(jsonRedisSerializer); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + + } +} diff --git a/src/main/java/com/dji/sample/component/redis/RedisConst.java b/src/main/java/com/dji/sample/component/redis/RedisConst.java new file mode 100644 index 0000000..4d8acf1 --- /dev/null +++ b/src/main/java/com/dji/sample/component/redis/RedisConst.java @@ -0,0 +1,29 @@ +package com.dji.sample.component.redis; + +import com.dji.sample.manage.model.enums.DeviceDomainEnum; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/21 + */ +public final class RedisConst { + + private RedisConst() { + + } + + public static final Integer DEVICE_ALIVE_SECOND = 60; + + public static final Integer WEBSOCKET_ALIVE_SECOND = 60 * 60 * 24; + + public static final String ONLINE_PREFIX = "online:"; + + public static final String DEVICE_ONLINE_PREFIX = ONLINE_PREFIX + DeviceDomainEnum.SUB_DEVICE + ":"; + + public static final String WEBSOCKET_PREFIX = "webSocket:"; + + public static final String WEBSOCKET_ALL = "webSocket:all"; + + public static final String HMS_PREFIX = "hms:"; +} diff --git a/src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java b/src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java new file mode 100644 index 0000000..f5b1066 --- /dev/null +++ b/src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java @@ -0,0 +1,187 @@ +package com.dji.sample.component.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/19 + */ +@Component +public class RedisOpsUtils { + + @Autowired + private RedisTemplate redisTemplate; + + /** + * HSET + * @param key + * @param field + * @param value + */ + public void hashSet(String key, String field, Object value) { + redisTemplate.opsForHash().put(key, field, value); + } + + /** + * HGET + * @param key + * @param field + * @return + */ + public Object hashGet(String key, String field) { + return redisTemplate.opsForHash().get(key, field); + } + + /** + * HKEYS + * @param key + * @return + */ + public Set hashKeys(String key) { + return redisTemplate.opsForHash().keys(key); + } + + /** + * HEXISTS + * @param key + * @param field + * @return + */ + public boolean hashCheck(String key, String field) { + return redisTemplate.opsForHash().hasKey(key, field); + } + + /** + * HDEL + * @param key + * @param fields + * @return + */ + public boolean hashDel(String key, Object[] fields) { + return redisTemplate.opsForHash().delete(key, fields) > 0; + } + + /** + * EXPIRE + * @param key + * @param timeout + * @return + */ + public boolean expireKey(String key, long timeout) { + return redisTemplate.expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * SET + * @param key + * @param value + */ + public void set(String key, Object value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * GET + * @param key + * @return + */ + public Object get(String key) { + return redisTemplate.opsForValue().get(key); + } + + /** + * SETEX + * @param key + * @param value + * @param expire + */ + public void setWithExpire(String key, Object value, long expire) { + redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS); + } + + /** + * TTL + * @param key + * @return + */ + public long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * EXISTS + * @param key + * @return + */ + public boolean checkExist(String key) { + return redisTemplate.hasKey(key); + } + + /** + * DEL + * @param key + * @return + */ + public boolean del(String key) { + return this.checkExist(key) && redisTemplate.delete(key); + } + + /** + * KEYS + * @param pattern + * @return + */ + public Set getAllKeys(String pattern) { + return redisTemplate.keys(pattern); + } + + /** + * RPUSH + * @param key + * @param value + */ + public void listRPush(String key, Object... value) { + if (value.length == 0) { + return; + } + for (Object val : value) { + redisTemplate.opsForList().rightPush(key, val); + } + } + + /** + * LRANGE + * @param key + * @param start + * @param end + * @return + */ + public List listGet(String key, long start, long end) { + return redisTemplate.opsForList().range(key, start, end); + } + + /** + * LRANGE + * @param key + * @return + */ + public List listGetAll(String key) { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * LLen + * @param key + * @return + */ + public Long listLen(String key) { + return redisTemplate.opsForList().size(key); + } +} diff --git a/src/main/java/com/dji/sample/component/websocket/config/AuthPrincipalHandler.java b/src/main/java/com/dji/sample/component/websocket/config/AuthPrincipalHandler.java index 68f070e..05fbd2d 100644 --- a/src/main/java/com/dji/sample/component/websocket/config/AuthPrincipalHandler.java +++ b/src/main/java/com/dji/sample/component/websocket/config/AuthPrincipalHandler.java @@ -35,7 +35,7 @@ public class AuthPrincipalHandler extends DefaultHandshakeHandler { if (!StringUtils.hasText(token)) { return false; } - + log.debug("token:" + token); Optional customClaim = JwtUtil.parseToken(token); if (customClaim.isEmpty()) { return false; diff --git a/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java b/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java index 4fdd774..f6ad8fb 100644 --- a/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java +++ b/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java @@ -1,5 +1,7 @@ package com.dji.sample.component.websocket.config; +import com.dji.sample.component.websocket.service.IWebSocketManageService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; @@ -13,8 +15,11 @@ import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; @Component public class WebSocketDefaultFactory implements WebSocketHandlerDecoratorFactory { + @Autowired + private IWebSocketManageService webSocketManageService; + @Override public WebSocketHandler decorate(WebSocketHandler handler) { - return new WebSocketDefaultHandler(handler); + return new WebSocketDefaultHandler(handler, webSocketManageService); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultHandler.java b/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultHandler.java index d4c7e7a..11ee861 100644 --- a/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultHandler.java +++ b/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultHandler.java @@ -1,9 +1,7 @@ package com.dji.sample.component.websocket.config; -import com.dji.sample.component.websocket.model.WebSocketManager; -import com.dji.sample.component.websocket.service.ISendMessageService; +import com.dji.sample.component.websocket.service.IWebSocketManageService; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.WebSocketHandler; @@ -22,20 +20,20 @@ import java.security.Principal; @Slf4j public class WebSocketDefaultHandler extends WebSocketHandlerDecorator { - @Autowired - private ISendMessageService sendMessageService; + private IWebSocketManageService webSocketManageService; - WebSocketDefaultHandler(WebSocketHandler delegate) { + WebSocketDefaultHandler(WebSocketHandler delegate, IWebSocketManageService webSocketManageService) { super(delegate); + this.webSocketManageService = webSocketManageService; } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { Principal principal = session.getPrincipal(); if (StringUtils.hasText(principal.getName())) { - WebSocketManager.put(principal.getName(), new ConcurrentWebSocketSession(session)); + webSocketManageService.put(principal.getName(), new ConcurrentWebSocketSession(session)); log.debug("{} is connected. ID: {}. WebSocketSession[current count: {}]", - principal.getName(), session.getId(), WebSocketManager.getConnectedCount()); + principal.getName(), session.getId(), webSocketManageService.getConnectedCount()); return; } session.close(); @@ -45,9 +43,9 @@ public class WebSocketDefaultHandler extends WebSocketHandlerDecorator { public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { Principal principal = session.getPrincipal(); if (StringUtils.hasText(principal.getName())) { - WebSocketManager.remove(principal.getName(), session.getId()); + webSocketManageService.remove(principal.getName(), session.getId()); log.debug("{} is disconnected. ID: {}. WebSocketSession[current count: {}]", - principal.getName(), session.getId(), WebSocketManager.getConnectedCount()); + principal.getName(), session.getId(), webSocketManageService.getConnectedCount()); } } diff --git a/src/main/java/com/dji/sample/component/websocket/model/BizCodeEnum.java b/src/main/java/com/dji/sample/component/websocket/model/BizCodeEnum.java index 6ac35c2..50f8978 100644 --- a/src/main/java/com/dji/sample/component/websocket/model/BizCodeEnum.java +++ b/src/main/java/com/dji/sample/component/websocket/model/BizCodeEnum.java @@ -17,13 +17,19 @@ public enum BizCodeEnum { GATEWAY_OSD("gateway_osd"), + DOCK_OSD("dock_osd"), + MAP_ELEMENT_CREATE("map_element_create"), MAP_ELEMENT_UPDATE("map_element_update"), MAP_ELEMENT_DELETE("map_element_delete"), - MAP_GROUP_REFRESH("map_group_refresh"); + MAP_GROUP_REFRESH("map_group_refresh"), + + FLIGHT_TASK_PROGRESS("flighttask_progress"), + + DEVICE_HMS("device_hms"); private String code; diff --git a/src/main/java/com/dji/sample/component/websocket/service/IWebSocketManageService.java b/src/main/java/com/dji/sample/component/websocket/service/IWebSocketManageService.java new file mode 100644 index 0000000..f09a6e2 --- /dev/null +++ b/src/main/java/com/dji/sample/component/websocket/service/IWebSocketManageService.java @@ -0,0 +1,23 @@ +package com.dji.sample.component.websocket.service; + +import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; + +import java.util.Collection; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/25 + */ +public interface IWebSocketManageService { + + void put(String key, ConcurrentWebSocketSession val); + + void remove(String key, String sessionId); + + Collection getValueWithWorkspace(String workspaceId); + + Collection getValueWithWorkspaceAndUserType(String workspaceId, Integer userType); + + Long getConnectedCount(); +} diff --git a/src/main/java/com/dji/sample/component/websocket/service/impl/SendMessageServiceImpl.java b/src/main/java/com/dji/sample/component/websocket/service/impl/SendMessageServiceImpl.java index 8ee6131..0546728 100644 --- a/src/main/java/com/dji/sample/component/websocket/service/impl/SendMessageServiceImpl.java +++ b/src/main/java/com/dji/sample/component/websocket/service/impl/SendMessageServiceImpl.java @@ -5,6 +5,7 @@ import com.dji.sample.component.websocket.model.CustomWebSocketMessage; import com.dji.sample.component.websocket.service.ISendMessageService; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.socket.TextMessage; @@ -20,6 +21,9 @@ import java.util.Collection; @Slf4j public class SendMessageServiceImpl implements ISendMessageService { + @Autowired + private ObjectMapper mapper; + @Override public void sendMessage(ConcurrentWebSocketSession session, CustomWebSocketMessage message) { if (session == null) { @@ -33,7 +37,6 @@ public class SendMessageServiceImpl implements ISendMessageService { return; } - ObjectMapper mapper = new ObjectMapper(); session.sendMessage(new TextMessage(mapper.writeValueAsBytes(message))); } catch (IOException e) { @@ -50,7 +53,6 @@ public class SendMessageServiceImpl implements ISendMessageService { try { - ObjectMapper mapper = new ObjectMapper(); TextMessage data = new TextMessage(mapper.writeValueAsBytes(message)); for (ConcurrentWebSocketSession session : sessions) { @@ -60,7 +62,6 @@ public class SendMessageServiceImpl implements ISendMessageService { return; } session.sendMessage(data); - } } catch (IOException e) { diff --git a/src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketManageServiceImpl.java b/src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketManageServiceImpl.java new file mode 100644 index 0000000..e2734c0 --- /dev/null +++ b/src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketManageServiceImpl.java @@ -0,0 +1,90 @@ +package com.dji.sample.component.websocket.service.impl; + +import com.dji.sample.component.redis.RedisConst; +import com.dji.sample.component.redis.RedisOpsUtils; +import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; +import com.dji.sample.component.websocket.service.IWebSocketManageService; +import com.dji.sample.manage.model.enums.UserTypeEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/25 + */ +@Slf4j +@Service +public class WebSocketManageServiceImpl implements IWebSocketManageService { + + private static final ConcurrentHashMap SESSIONS = new ConcurrentHashMap<>(16); + + @Autowired + private RedisOpsUtils redisOps; + + @Override + public void put(String key, ConcurrentWebSocketSession val) { + String[] name = key.split("/"); + if (name.length != 3) { + log.debug("The key is out of format. [{workspaceId}/{userType}/{userId}]"); + return; + } + String sessionId = val.getId(); + String workspaceKey = RedisConst.WEBSOCKET_PREFIX + name[0]; + String userTypeKey = RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(Integer.parseInt(name[1])).getDesc(); + redisOps.hashSet(workspaceKey, sessionId, name[2]); + redisOps.hashSet(userTypeKey, sessionId, name[2]); + SESSIONS.put(sessionId, val); + redisOps.expireKey(workspaceKey, RedisConst.WEBSOCKET_ALIVE_SECOND); + redisOps.expireKey(userTypeKey, RedisConst.WEBSOCKET_ALIVE_SECOND); + } + + @Override + public void remove(String key, String sessionId) { + String[] name = key.split("/"); + if (name.length != 3) { + log.debug("The key is out of format. [{workspaceId}/{userType}/{userId}]"); + return; + } + redisOps.hashDel(RedisConst.WEBSOCKET_PREFIX + name[0], new String[] {sessionId}); + redisOps.hashDel(RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(Integer.parseInt(name[1])), new String[] {sessionId}); + SESSIONS.remove(sessionId); + } + + @Override + public Collection getValueWithWorkspace(String workspaceId) { + if (!StringUtils.hasText(workspaceId)) { + return Collections.emptySet(); + } + String key = RedisConst.WEBSOCKET_PREFIX + workspaceId; + + return redisOps.hashKeys(key) + .stream() + .map(SESSIONS::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + @Override + public Collection getValueWithWorkspaceAndUserType(String workspaceId, Integer userType) { + String key = RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(userType).getDesc(); + return redisOps.hashKeys(key) + .stream() + .map(SESSIONS::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + @Override + public Long getConnectedCount() { + return SESSIONS.mappingCount(); + } +} diff --git a/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java b/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java new file mode 100644 index 0000000..f1a481e --- /dev/null +++ b/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java @@ -0,0 +1,49 @@ +package com.dji.sample.configuration; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Configuration +public class SpringBeanConfiguration { + + @Bean + @ConditionalOnMissingBean(ObjectMapper.class) + public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { + ObjectMapper objectMapper = builder.createXmlMapper(false).build(); + objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + JavaTimeModule timeModule = new JavaTimeModule(); + timeModule.addDeserializer(LocalDateTime.class, + new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + timeModule.addSerializer(LocalDateTime.class, + new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + + objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS); + objectMapper.registerModules(timeModule); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); + objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer() { + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(""); + } + }); + return objectMapper; + } +} 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 08a245d..ad2579a 100644 --- a/src/main/java/com/dji/sample/manage/controller/DeviceController.java +++ b/src/main/java/com/dji/sample/manage/controller/DeviceController.java @@ -1,19 +1,13 @@ package com.dji.sample.manage.controller; -import com.dji.sample.common.model.CustomClaim; +import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.AuthInterceptor; import com.dji.sample.component.mqtt.model.ChannelName; import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.component.mqtt.model.CommonTopicResponse; -import com.dji.sample.component.websocket.model.BizCodeEnum; -import com.dji.sample.component.websocket.model.CustomWebSocketMessage; -import com.dji.sample.component.websocket.model.WebSocketManager; import com.dji.sample.component.websocket.service.ISendMessageService; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.dto.WorkspaceDTO; -import com.dji.sample.manage.model.enums.UserTypeEnum; -import com.dji.sample.manage.model.param.DeviceQueryParam; +import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; import com.dji.sample.manage.service.IDeviceService; import lombok.extern.slf4j.Slf4j; @@ -21,12 +15,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.Message; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; -import javax.servlet.http.HttpServletRequest; import java.util.List; +import java.util.Optional; /** * @author sean.zhou @@ -58,10 +50,6 @@ public class DeviceController { .tid(receiver.getTid()) .bid(receiver.getBid()) .build()); - - // Publish the latest device topology information in the current workspace to the pilot. - deviceService.pushDeviceOnlineTopo(WorkspaceDTO.DEFAULT_WORKSPACE_ID, - receiver.getData().getSn(), receiver.getData().getSubDevices().get(0).getSn()); } } @@ -81,22 +69,16 @@ public class DeviceController { .bid(receiver.getBid()) .build()); - // Publish the latest device topology information in the current workspace to the pilot. - deviceService.pushDeviceOfflineTopo(WorkspaceDTO.DEFAULT_WORKSPACE_ID, receiver.getData().getSn()); } } /** - * Get the topology list of all devices in the current user workspace. - * @param request + * Get the topology list of all online devices in one workspace. + * @param workspaceId * @return */ - @GetMapping("/devices") - public ResponseResult> getDevices(HttpServletRequest request) { - // Get information about the current user. - CustomClaim claim = (CustomClaim)request.getAttribute(AuthInterceptor.TOKEN_CLAIM); - String workspaceId = claim.getWorkspaceId(); - // Get information about the devices in the current user's workspace. + @GetMapping("/{workspace_id}/devices") + public ResponseResult> getDevices(@PathVariable("workspace_id") String workspaceId) { List devicesList = deviceService.getDevicesTopoForWeb(workspaceId); return ResponseResult.success(devicesList); @@ -109,30 +91,54 @@ public class DeviceController { deviceService.handleOSD(topic, payload); } + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_FIRMWARE_VERSION) + public void updateFirmwareVersion(FirmwareVersionReceiver receiver) { + deviceService.updateFirmwareVersion(receiver); + } + + @PostMapping("/{device_sn}/binding") + public ResponseResult bindDevice(@RequestBody DeviceDTO device, @PathVariable("device_sn") String deviceSn) { + device.setDeviceSn(deviceSn); + boolean isUpd = deviceService.bindDevice(device); + return isUpd ? ResponseResult.success() : ResponseResult.error(); + } + + @GetMapping("/{workspace_id}/devices/{device_sn}") + public ResponseResult getDevice(@PathVariable("workspace_id") String workspaceId, + @PathVariable("device_sn") String deviceSn) { + Optional deviceOpt = deviceService.getDeviceBySn(deviceSn); + return deviceOpt.isEmpty() ? ResponseResult.error("device not found.") : ResponseResult.success(deviceOpt.get()); + } + /** - * Handles the payloads data of the drone. - * @param deviceSn drone's sn + * Get the binding devices list in one workspace. + * @param workspaceId + * @param page + * @param pageSize + * @return */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE) - public void pushWebSocketDevices(String deviceSn) { - List devicesList = deviceService.getDevicesByParams( - DeviceQueryParam.builder() - .deviceSn(deviceSn) - .build()); - // Get drone information based on the sn of the drone. The sn of the drone is unique. - DeviceDTO device = devicesList.get(0); - // Set the remote controller and payloads information of the drone. - deviceService.spliceDeviceTopo(device); - - CustomWebSocketMessage wsMessage = CustomWebSocketMessage.builder() - .timestamp(System.currentTimeMillis()) - .bizCode(BizCodeEnum.DEVICE_UPDATE_TOPO.getCode()) - .data(device) - .build(); - // Update the topology of the drone via WebSocket notifications to the web side. - sendMessageService.sendBatch(WebSocketManager - .getValueWithWorkspaceAndUserType( - device.getWorkspaceId(), UserTypeEnum.WEB.getVal()), - wsMessage); + @GetMapping("/{workspace_id}/devices/bound") + public ResponseResult> getBoundDevicesWithDomain( + @PathVariable("workspace_id") String workspaceId, String domain, + @RequestParam(defaultValue = "1") Long page, + @RequestParam(value = "page_size", defaultValue = "50") Long pageSize) { + PaginationData devices = deviceService.getBoundDevicesWithDomain(workspaceId, page, pageSize, domain); + + return ResponseResult.success(devices); + } + + @DeleteMapping("/{device_sn}/unbinding") + public ResponseResult unbindingDevice(@PathVariable("device_sn") String deviceSn) { + deviceService.unbindDevice(deviceSn); + return ResponseResult.success(); + } + + @PutMapping("/{workspace_id}/devices/{device_sn}") + public ResponseResult updateDevice(@RequestBody DeviceDTO device, + @PathVariable("workspace_id") String workspaceId, + @PathVariable("device_sn") String deviceSn) { + device.setDeviceSn(deviceSn); + boolean isUpd = deviceService.updateDevice(device); + return isUpd ? ResponseResult.success() : ResponseResult.error(); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/controller/DeviceHmsController.java b/src/main/java/com/dji/sample/manage/controller/DeviceHmsController.java new file mode 100644 index 0000000..f058399 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/controller/DeviceHmsController.java @@ -0,0 +1,53 @@ +package com.dji.sample.manage.controller; + +import com.dji.sample.common.model.PaginationData; +import com.dji.sample.common.model.ResponseResult; +import com.dji.sample.manage.model.dto.DeviceHmsDTO; +import com.dji.sample.manage.model.param.DeviceHmsQueryParam; +import com.dji.sample.manage.service.IDeviceHmsService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ + +@RestController +@Slf4j +@RequestMapping("${url.manage.prefix}${url.manage.version}/devices") +public class DeviceHmsController { + + @Autowired + private IDeviceHmsService deviceHmsService; + + @GetMapping("/{workspace_id}/devices/hms") + public ResponseResult> getBoundDevicesWithDomain(DeviceHmsQueryParam param, + @PathVariable("workspace_id") String workspaceId) { + PaginationData devices = deviceHmsService.getDeviceHmsByParam(param); + + return ResponseResult.success(devices); + } + + @PutMapping("/{workspace_id}/devices/hms/{device_sn}") + public ResponseResult updateReadHmsByDeviceSn(@PathVariable("device_sn") String deviceSn) { + deviceHmsService.updateUnreadHms(deviceSn); + return ResponseResult.success(); + } + + @GetMapping("/{workspace_id}/devices/hms/{device_sn}") + public ResponseResult> getUnreadHmsByDeviceSn(@PathVariable("device_sn") String deviceSn) { + PaginationData paginationData = deviceHmsService.getDeviceHmsByParam( + DeviceHmsQueryParam.builder() + .deviceSn(new HashSet<>(Set.of(deviceSn))) + .updateTime(0L) + .build()); + return ResponseResult.success(paginationData.getList()); + } +} diff --git a/src/main/java/com/dji/sample/manage/controller/DevicePayloadController.java b/src/main/java/com/dji/sample/manage/controller/DevicePayloadController.java index 0c0ba2c..4a768a2 100644 --- a/src/main/java/com/dji/sample/manage/controller/DevicePayloadController.java +++ b/src/main/java/com/dji/sample/manage/controller/DevicePayloadController.java @@ -2,7 +2,6 @@ package com.dji.sample.manage.controller; import com.dji.sample.component.mqtt.model.ChannelName; import com.dji.sample.manage.model.receiver.DeviceBasicReceiver; -import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; import com.dji.sample.manage.service.IDevicePayloadService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -23,25 +22,6 @@ public class DevicePayloadController { @Autowired private IDevicePayloadService devicePayloadService; - /** - * Handles the data for the payload messages in the state topic. - * @param payloadsList List of payload information. - * @return drone's sn - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_PAYLOAD, - outputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE) - public String statePayload(List payloadsList) { - // Delete all payload information for the drone based on the drone's sn. - devicePayloadService.deletePayloadsByDeviceSn(List.of(payloadsList.get(0).getDeviceSn())); - - // Save the new payload information. - devicePayloadService.savePayloadDTOs(payloadsList); - - log.debug("The result of saving the payload is successful."); - - return payloadsList.get(0).getDeviceSn(); - } - /** * Handles messages in the state topic about basic drone data. * @@ -50,9 +30,8 @@ public class DevicePayloadController { * @param deviceBasic basic drone data * @return drone's sn */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_BASIC, - outputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE) - public String stateBasic(DeviceBasicReceiver deviceBasic) { + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_BASIC) + public void stateBasic(DeviceBasicReceiver deviceBasic) { // Delete all payload information for the drone based on the drone's sn. devicePayloadService.deletePayloadsByDeviceSn(List.of(deviceBasic.getDeviceSn())); @@ -61,6 +40,5 @@ public class DevicePayloadController { log.debug("The result of saving the payloads is {}.", isSave); - return deviceBasic.getDeviceSn(); } } \ 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 ef89f34..7f6e90c 100644 --- a/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java +++ b/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java @@ -2,13 +2,13 @@ package com.dji.sample.manage.controller; import com.dji.sample.common.model.CustomClaim; import com.dji.sample.common.model.ResponseResult; +import com.dji.sample.component.mqtt.model.Chan; import com.dji.sample.component.mqtt.model.ChannelName; import com.dji.sample.component.mqtt.model.CommonTopicReceiver; -import com.dji.sample.manage.model.Chan; +import com.dji.sample.component.mqtt.model.ServiceReply; import com.dji.sample.manage.model.dto.CapacityDeviceDTO; import com.dji.sample.manage.model.dto.LiveTypeDTO; -import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; -import com.dji.sample.manage.model.receiver.ServiceReplyReceiver; +import com.dji.sample.manage.model.receiver.LiveCapacityReceiver; import com.dji.sample.manage.service.ILiveStreamService; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -38,15 +38,17 @@ public class LiveStreamController { @Autowired private ILiveStreamService liveStreamService; + @Autowired + private ObjectMapper mapper; + /** * Analyze the live streaming capabilities of drones. * This data is necessary if drones are required for live streaming. - * @param device the capacity of drone + * @param liveCapacity the capacity of drone and dock */ @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_CAPACITY) - public void stateCapacity(CapacityDeviceReceiver device) { - boolean parseCapacity = liveStreamService.saveLiveCapacity(device); - log.debug("The result of parsing the live capacity is {}.", parseCapacity); + public void stateCapacity(LiveCapacityReceiver liveCapacity) { + liveStreamService.saveLiveCapacity(liveCapacity); } /** @@ -102,9 +104,8 @@ public class LiveStreamController { @ServiceActivator(inputChannel = ChannelName.INBOUND_SERVICE_REPLY) public void serviceReply(Message message) throws IOException { byte[] payload = (byte[])message.getPayload(); - ObjectMapper mapper = new ObjectMapper(); - CommonTopicReceiver receiver = mapper.readValue(payload, - new TypeReference>() { + CommonTopicReceiver receiver = mapper.readValue(payload, + new TypeReference>() { }); Chan chan = Chan.getInstance(); // Put the message to the chan object. diff --git a/src/main/java/com/dji/sample/manage/controller/LoginController.java b/src/main/java/com/dji/sample/manage/controller/LoginController.java index b0cacb4..23c84d9 100644 --- a/src/main/java/com/dji/sample/manage/controller/LoginController.java +++ b/src/main/java/com/dji/sample/manage/controller/LoginController.java @@ -27,9 +27,10 @@ public class LoginController { @PostMapping("/login") public ResponseResult login(@RequestBody UserLoginDTO loginDTO) { + String username = loginDTO.getUsername(); String password = loginDTO.getPassword(); - return userService.userLogin(username, password); + return userService.userLogin(username, password, loginDTO.getFlag()); } @PostMapping("/token/refresh") @@ -42,6 +43,6 @@ public class LoginController { return ResponseResult.error(CommonErrorEnum.NO_TOKEN.getErrorMsg()); } - return ResponseResult.success(user); + return ResponseResult.success(user.get()); } } diff --git a/src/main/java/com/dji/sample/manage/controller/UserController.java b/src/main/java/com/dji/sample/manage/controller/UserController.java index 06cfb4d..91990e7 100644 --- a/src/main/java/com/dji/sample/manage/controller/UserController.java +++ b/src/main/java/com/dji/sample/manage/controller/UserController.java @@ -1,12 +1,12 @@ package com.dji.sample.manage.controller; import com.dji.sample.common.model.CustomClaim; +import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.ResponseResult; +import com.dji.sample.manage.model.dto.UserListDTO; import com.dji.sample.manage.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; @@ -20,10 +20,45 @@ public class UserController { @Autowired private IUserService userService; + /** + * Query the information of the current user. + * @param request + * @return + */ @GetMapping("/current") public ResponseResult getCurrentUserInfo(HttpServletRequest request) { CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); return userService.getUserByUsername(customClaim.getUsername(), customClaim.getWorkspaceId()); } + /** + * Paging to query all users in a workspace. + * @param page current page + * @param pageSize + * @param workspaceId + * @return + */ + @GetMapping("/{workspace_id}/users") + public ResponseResult> getUsers(@RequestParam(defaultValue = "1") Long page, + @RequestParam(value = "page_size", defaultValue = "50") Long pageSize, + @PathVariable("workspace_id") String workspaceId) { + PaginationData paginationData = userService.getUsersByWorkspaceId(page, pageSize, workspaceId); + return ResponseResult.success(paginationData); + } + + /** + * Modify user information. Only mqtt account information is included, nothing else can be modified. + * @param user + * @param workspaceId + * @param userId + * @return + */ + @PutMapping("/{workspace_id}/users/{user_id}") + public ResponseResult updateUser(@RequestBody UserListDTO user, + @PathVariable("workspace_id") String workspaceId, + @PathVariable("user_id") String userId) { + + userService.updateUser(workspaceId, userId, user); + return ResponseResult.success(); + } } diff --git a/src/main/java/com/dji/sample/manage/dao/IDeviceHmsMapper.java b/src/main/java/com/dji/sample/manage/dao/IDeviceHmsMapper.java new file mode 100644 index 0000000..c7eb8bb --- /dev/null +++ b/src/main/java/com/dji/sample/manage/dao/IDeviceHmsMapper.java @@ -0,0 +1,12 @@ +package com.dji.sample.manage.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.dji.sample.manage.model.entity.DeviceHmsEntity; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/6 + */ +public interface IDeviceHmsMapper extends BaseMapper { +} diff --git a/src/main/java/com/dji/sample/manage/model/common/HmsJsonUtil.java b/src/main/java/com/dji/sample/manage/model/common/HmsJsonUtil.java new file mode 100644 index 0000000..030bf5f --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/common/HmsJsonUtil.java @@ -0,0 +1,49 @@ +package com.dji.sample.manage.model.common; + +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.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +@Slf4j +@Component +public class HmsJsonUtil { + + private static ObjectMapper mapper; + + @Autowired + public void setMapper(ObjectMapper mapper) { + HmsJsonUtil.mapper = mapper; + } + + private static JsonNode nodes; + + private HmsJsonUtil(){ + + } + + @PostConstruct + private void loadJsonFile() { + try (InputStream inputStream = new ClassPathResource("hms.json").getInputStream()){ + nodes = mapper.readTree(inputStream); + } catch (IOException e) { + log.error("hms.json failed to load."); + e.printStackTrace(); + } + } + + public static HmsMessage get(String key) { + return mapper.convertValue(nodes.get(key), HmsMessage.class); + } +} diff --git a/src/main/java/com/dji/sample/manage/model/common/HmsMessage.java b/src/main/java/com/dji/sample/manage/model/common/HmsMessage.java new file mode 100644 index 0000000..328b45e --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/common/HmsMessage.java @@ -0,0 +1,16 @@ +package com.dji.sample.manage.model.common; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +@Data +public class HmsMessage { + + private String zh; + + private String en; +} diff --git a/src/main/java/com/dji/sample/manage/model/dto/CapacityCameraDTO.java b/src/main/java/com/dji/sample/manage/model/dto/CapacityCameraDTO.java index dbc6481..d44fddd 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/CapacityCameraDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/CapacityCameraDTO.java @@ -18,14 +18,12 @@ import java.util.List; @NoArgsConstructor public class CapacityCameraDTO { - private Integer id; + private String id; private String deviceSn; private String name; - private String description; - private String index; private String type; 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 1d9dc3a..9366472 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 @@ -16,7 +16,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor public class CapacityVideoDTO { - private Integer id; + private String id; private String index; diff --git a/src/main/java/com/dji/sample/manage/model/dto/DeviceDTO.java b/src/main/java/com/dji/sample/manage/model/dto/DeviceDTO.java index 2571dff..89d19d6 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/DeviceDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/DeviceDTO.java @@ -1,12 +1,11 @@ package com.dji.sample.manage.model.dto; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDateTime; import java.util.List; /** @@ -18,7 +17,6 @@ import java.util.List; @AllArgsConstructor @NoArgsConstructor @Builder -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class DeviceDTO { private String deviceSn; @@ -44,4 +42,22 @@ public class DeviceDTO { private List payloadsList; private IconUrlDTO iconUrl; + + private Boolean status; + + private Boolean boundStatus; + + private LocalDateTime loginTime; + + private LocalDateTime boundTime; + + private String nickname; + + private String userId; + + private String firmwareVersion; + + private String workspaceName; + + private DeviceDTO children; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/dto/DeviceHmsDTO.java b/src/main/java/com/dji/sample/manage/model/dto/DeviceHmsDTO.java new file mode 100644 index 0000000..7dc1c1a --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/dto/DeviceHmsDTO.java @@ -0,0 +1,57 @@ +package com.dji.sample.manage.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/8 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DeviceHmsDTO implements Cloneable { + + private String hmsId; + + private String tid; + + private String bid; + + private String sn; + + private Integer level; + + private Integer module; + + private String key; + + private String messageZh; + + private String messageEn; + + private LocalDateTime createTime; + + private LocalDateTime updateTime; + + @Override + public DeviceHmsDTO clone() { + try { + return (DeviceHmsDTO) super.clone(); + } catch (CloneNotSupportedException e) { + return DeviceHmsDTO.builder() + .sn(this.sn) + .bid(this.bid) + .tid(this.tid) + .createTime(this.createTime) + .updateTime(this.updateTime) + .build(); + } + } +} diff --git a/src/main/java/com/dji/sample/manage/model/dto/TelemetryDTO.java b/src/main/java/com/dji/sample/manage/model/dto/TelemetryDTO.java index 8f7f9d5..26fe624 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/TelemetryDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/TelemetryDTO.java @@ -14,9 +14,9 @@ import lombok.NoArgsConstructor; @Builder @AllArgsConstructor @NoArgsConstructor -public class TelemetryDTO { +public class TelemetryDTO { - private TelemetryDeviceDTO host; + private T host; private String sn; } diff --git a/src/main/java/com/dji/sample/manage/model/dto/TopologyDeviceDTO.java b/src/main/java/com/dji/sample/manage/model/dto/TopologyDeviceDTO.java index c36e88d..ced5277 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/TopologyDeviceDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/TopologyDeviceDTO.java @@ -22,8 +22,7 @@ public class TopologyDeviceDTO { private DeviceModelDTO deviceModel; - @Builder.Default - private Boolean onlineStatus = true; + private Boolean onlineStatus; private String deviceCallsign; @@ -32,4 +31,12 @@ public class TopologyDeviceDTO { private String userCallsign; private IconUrlDTO iconUrls; + + private String model; + + private Boolean boundStatus; + + private String gatewaySn; + + private String domain; } diff --git a/src/main/java/com/dji/sample/manage/model/dto/UserListDTO.java b/src/main/java/com/dji/sample/manage/model/dto/UserListDTO.java new file mode 100644 index 0000000..7bfb3a6 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/dto/UserListDTO.java @@ -0,0 +1,34 @@ +package com.dji.sample.manage.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/18 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserListDTO { + + private String userId; + + private String username; + + private String workspaceName; + + private String userType; + + private String mqttUsername; + + private String mqttPassword; + + private LocalDateTime createTime; +} diff --git a/src/main/java/com/dji/sample/manage/model/dto/UserLoginDTO.java b/src/main/java/com/dji/sample/manage/model/dto/UserLoginDTO.java index b82ca44..7c0d8f4 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/UserLoginDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/UserLoginDTO.java @@ -11,4 +11,7 @@ public class UserLoginDTO { @NonNull private String password; + + @NonNull + private Integer flag; } diff --git a/src/main/java/com/dji/sample/manage/model/dto/WorkspaceDTO.java b/src/main/java/com/dji/sample/manage/model/dto/WorkspaceDTO.java index 0ce7d15..3714974 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/WorkspaceDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/WorkspaceDTO.java @@ -19,8 +19,6 @@ import lombok.NoArgsConstructor; @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class WorkspaceDTO { - public static final String DEFAULT_WORKSPACE_ID = "e3dea0f5-37f2-4d79-ae58-490af3228069"; - private Integer id; private String workspaceId; @@ -30,4 +28,6 @@ public class WorkspaceDTO { private String workspaceDesc; private String platformName; + + private String bindCode; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/entity/DeviceEntity.java b/src/main/java/com/dji/sample/manage/model/entity/DeviceEntity.java index f019a01..29326da 100644 --- a/src/main/java/com/dji/sample/manage/model/entity/DeviceEntity.java +++ b/src/main/java/com/dji/sample/manage/model/entity/DeviceEntity.java @@ -66,4 +66,23 @@ public class DeviceEntity implements Serializable { @TableField(value = "url_select") private String urlSelect; + + @TableField(value = "user_id") + private String userId; + + @TableField(value = "nickname") + private String nickname; + + @TableField(value = "firmware_version") + private String firmwareVersion; + + @TableField(value = "bound_status") + private Boolean boundStatus; + + @TableField(value = "bound_time") + private Long boundTime; + + @TableField(value = "login_time") + private Long loginTime; + } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/entity/DeviceHmsEntity.java b/src/main/java/com/dji/sample/manage/model/entity/DeviceHmsEntity.java new file mode 100644 index 0000000..bf14f5a --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/entity/DeviceHmsEntity.java @@ -0,0 +1,76 @@ +package com.dji.sample.manage.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/6 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName(value = "manage_device_hms") +public class DeviceHmsEntity implements Serializable, Cloneable { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @TableField("hms_id") + private String hmsId; + + @TableField("tid") + private String tid; + + @TableField("bid") + private String bid; + + @TableField("sn") + private String sn; + + @TableField("level") + private Integer level; + + @TableField("module") + private Integer module; + + @TableField("hms_key") + private String hmsKey; + + @TableField("message_zh") + private String messageZh; + + @TableField("message_en") + private String messageEn; + + @TableField("create_time") + private Long createTime; + + @TableField("update_time") + private Long updateTime; + + @Override + public DeviceHmsEntity clone() { + try { + return (DeviceHmsEntity) super.clone(); + } catch (CloneNotSupportedException e) { + return DeviceHmsEntity.builder() + .bid(this.getBid()) + .tid(this.getTid()) + .createTime(this.getCreateTime()) + .updateTime(this.getUpdateTime()) + .sn(this.getSn()) + .build(); + } + } +} diff --git a/src/main/java/com/dji/sample/manage/model/entity/DevicePayloadEntity.java b/src/main/java/com/dji/sample/manage/model/entity/DevicePayloadEntity.java index 184fb0f..a54ff0d 100644 --- a/src/main/java/com/dji/sample/manage/model/entity/DevicePayloadEntity.java +++ b/src/main/java/com/dji/sample/manage/model/entity/DevicePayloadEntity.java @@ -35,8 +35,8 @@ public class DevicePayloadEntity implements Serializable { @TableField(value = "sub_type") private Integer subType; - @TableField(value = "version") - private Integer version; + @TableField(value = "firmware_version") + private String firmwareVersion; @TableField(value = "payload_index") private Integer payloadIndex; diff --git a/src/main/java/com/dji/sample/manage/model/entity/UserEntity.java b/src/main/java/com/dji/sample/manage/model/entity/UserEntity.java index 67edecf..6c095b9 100644 --- a/src/main/java/com/dji/sample/manage/model/entity/UserEntity.java +++ b/src/main/java/com/dji/sample/manage/model/entity/UserEntity.java @@ -22,7 +22,7 @@ public class UserEntity implements Serializable { private String password; @TableField(value = "workspace_id") - private Integer workspaceId; + private String workspaceId; @TableField(value = "user_type") private Integer userType; diff --git a/src/main/java/com/dji/sample/manage/model/entity/WorkspaceEntity.java b/src/main/java/com/dji/sample/manage/model/entity/WorkspaceEntity.java index 8a84ff0..90a5348 100644 --- a/src/main/java/com/dji/sample/manage/model/entity/WorkspaceEntity.java +++ b/src/main/java/com/dji/sample/manage/model/entity/WorkspaceEntity.java @@ -30,4 +30,6 @@ public class WorkspaceEntity implements Serializable { @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) private Long updateTime; + @TableField(value = "bind_code") + private String bindCode; } diff --git a/src/main/java/com/dji/sample/manage/model/enums/DeviceDomainEnum.java b/src/main/java/com/dji/sample/manage/model/enums/DeviceDomainEnum.java index cd8fd39..76b9c4d 100644 --- a/src/main/java/com/dji/sample/manage/model/enums/DeviceDomainEnum.java +++ b/src/main/java/com/dji/sample/manage/model/enums/DeviceDomainEnum.java @@ -14,6 +14,8 @@ public enum DeviceDomainEnum { PAYLOAD(1, "payload"), + DOCK (3, "dock"), + UNKNOWN(-1, "unknown"); private int val; @@ -29,6 +31,10 @@ public enum DeviceDomainEnum { return val; } + public String getDesc() { + return desc; + } + public static String getDesc(int val) { if (SUB_DEVICE.val == val) { return SUB_DEVICE.desc; @@ -41,6 +47,10 @@ public enum DeviceDomainEnum { if (PAYLOAD.val == val) { return PAYLOAD.desc; } + + if (DOCK.val == val) { + return DOCK.desc; + } return UNKNOWN.desc; } @@ -56,6 +66,10 @@ public enum DeviceDomainEnum { if (PAYLOAD.desc.equals(desc)) { return PAYLOAD.val; } + + if (DOCK.desc.equals(desc)) { + return DOCK.val; + } return UNKNOWN.val; } diff --git a/src/main/java/com/dji/sample/manage/model/enums/HmsEnum.java b/src/main/java/com/dji/sample/manage/model/enums/HmsEnum.java new file mode 100644 index 0000000..331a985 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/enums/HmsEnum.java @@ -0,0 +1,155 @@ +package com.dji.sample.manage.model.enums; + +import lombok.Getter; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +@Getter +public enum HmsEnum { + + IN_THE_SKY("_in_the_sky", 1); + + private int val; + + private String text; + + HmsEnum(String text, int val) { + this.text = text; + this.val = val; + } + + @Getter + public enum MessageLanguage { + + EN("en"), + + ZH("zh"); + + String language; + + MessageLanguage(String language) { + this.language = language; + } + } + + @Getter + public enum DomainType { + DRONE_NEST("drone_nest"), + + DRONE("drone"); + + private String domain; + + DomainType(String domain) { + this.domain = domain; + } + + } + + @Getter + public enum HmsFaqIdEnum { + + FPV_TIP("fpv_tip_"); + + private String text; + + HmsFaqIdEnum(String text) { + this.text = text; + } + + } + + @Getter + public enum HmsBatteryIndexEnum { + LEFT(0, "left", "左"), + + RIGHT(1, "right", "右"), + + UNKNOWN(-1, "unknown", "未知"); + + private int val; + + private String en; + + private String zh; + + HmsBatteryIndexEnum(int val, String en, String zh) { + this.val = val; + this.en = en; + this.zh = zh; + } + + public static HmsBatteryIndexEnum find(int val) { + return Arrays.stream(HmsBatteryIndexEnum.values()) + .filter(battery -> battery.val == val) + .findAny() + .orElse(UNKNOWN); + } + } + + @Getter + public enum HmsDockCoverIndexEnum { + LEFT(0, "left", "左"), + + RIGHT(1, "right", "右"), + + UNKNOWN(-1, "unknown", "未知"); + + private int val; + + private String en; + + private String zh; + + HmsDockCoverIndexEnum(int val, String en, String zh) { + this.val = val; + this.en = en; + this.zh = zh; + } + + public static HmsDockCoverIndexEnum find(int val) { + return Arrays.stream(HmsDockCoverIndexEnum.values()) + .filter(dockCover -> dockCover.val == val) + .findAny() + .orElse(UNKNOWN); + } + } + + @Getter + public enum HmsChargingRodIndexEnum { + + FRONT(0, "front", "前"), + + BACK(1, "back", "后"), + + LEFT(2, "left", "左"), + + RIGHT(3, "right", "右"), + + UNKNOWN(-1, "unknown", "未知"); + + private int val; + + private String en; + + private String zh; + + HmsChargingRodIndexEnum(int val, String en, String zh) { + this.val = val; + this.en = en; + this.zh = zh; + } + + public static HmsChargingRodIndexEnum find(int val) { + return Arrays.stream(HmsChargingRodIndexEnum.values()) + .filter(rod -> rod.val == val) + .findAny() + .orElse(UNKNOWN); + } + } +} diff --git a/src/main/java/com/dji/sample/manage/model/enums/PayloadModelEnum.java b/src/main/java/com/dji/sample/manage/model/enums/PayloadModelEnum.java new file mode 100644 index 0000000..5633b03 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/enums/PayloadModelEnum.java @@ -0,0 +1,61 @@ +package com.dji.sample.manage.model.enums; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/29 + */ +public enum PayloadModelEnum { + + Z30("Z30", "20-0"), + + XT2("XT2", "26-0"), + + XTS("XTS", "41-0"), + + H20("H20", "42-0"), + + H20T("H20T", "43-0"), + + P1("P1", "50-65535"), + + M30("M30", "52-0"), + + M30T("M30T", "53-0"), + + H20N("H20N", "61-0"), + + DOCK("DOCK", "165-0"), + + L1("L1", "90742-0"); + + private String model; + + private String index; + + PayloadModelEnum(String model, String index) { + this.model = model; + this.index = index; + } + + public String getModel() { + return model; + } + + public String getIndex() { + return index; + } + + public static List getAllModel() { + return Arrays.stream(PayloadModelEnum.values()).map(PayloadModelEnum::getModel).collect(Collectors.toList()); + } + + public static List getAllIndex() { + return Arrays.stream(PayloadModelEnum.values()).map(PayloadModelEnum::getIndex).collect(Collectors.toList()); + } + +} diff --git a/src/main/java/com/dji/sample/manage/model/enums/UserTypeEnum.java b/src/main/java/com/dji/sample/manage/model/enums/UserTypeEnum.java index 311669e..a90e526 100644 --- a/src/main/java/com/dji/sample/manage/model/enums/UserTypeEnum.java +++ b/src/main/java/com/dji/sample/manage/model/enums/UserTypeEnum.java @@ -7,18 +7,36 @@ package com.dji.sample.manage.model.enums; */ public enum UserTypeEnum { - WEB(1), + WEB(1, "Web"), - PILOT(2); + PILOT(2, "Pilot"), + + UNKNOWN(-1, "Unknown"); private int val; + private String desc; - UserTypeEnum(int val) { + UserTypeEnum(int val, String desc) { this.val = val; + this.desc = desc; } public int getVal() { - return val; + return this.val; + } + + public String getDesc() { + return this.desc; + } + + public static UserTypeEnum find(int val) { + if (val == WEB.val) { + return WEB; + } + if (val == PILOT.val) { + return PILOT; + } + return UNKNOWN; } } diff --git a/src/main/java/com/dji/sample/manage/model/param/DeviceHmsQueryParam.java b/src/main/java/com/dji/sample/manage/model/param/DeviceHmsQueryParam.java new file mode 100644 index 0000000..cfce9fe --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/param/DeviceHmsQueryParam.java @@ -0,0 +1,45 @@ +package com.dji.sample.manage.model.param; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Set; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/8 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class DeviceHmsQueryParam implements Serializable { + + @JsonProperty("device_sn") + private Set deviceSn; + + @JsonProperty("begin_time") + private Long beginTime; + + @JsonProperty("end_time") + private Long endTime; + + private String language; + + private String message; + + private Long page; + + @JsonProperty("page_size") + private Long pageSize; + + private Integer level; + + @JsonProperty("update_time") + private Long updateTime; +} diff --git a/src/main/java/com/dji/sample/manage/model/param/DeviceQueryParam.java b/src/main/java/com/dji/sample/manage/model/param/DeviceQueryParam.java index 9233121..b9fc67e 100644 --- a/src/main/java/com/dji/sample/manage/model/param/DeviceQueryParam.java +++ b/src/main/java/com/dji/sample/manage/model/param/DeviceQueryParam.java @@ -3,6 +3,8 @@ package com.dji.sample.manage.model.param; import lombok.Builder; import lombok.Data; +import java.util.List; + /** * The object of the device query field. * @@ -22,10 +24,12 @@ public class DeviceQueryParam { private Integer subType; - private Integer domain; + private List domains; private String childSn; + private Boolean boundStatus; + private boolean orderBy; private boolean isAsc; 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 new file mode 100644 index 0000000..bd69415 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java @@ -0,0 +1,18 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +@Data +public class AlternateLandPointReceiver { + + private Double latitude; + + private Double longitude; + + private Double safeLandHeight; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/BindDeviceReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/BindDeviceReceiver.java new file mode 100644 index 0000000..7f13246 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/BindDeviceReceiver.java @@ -0,0 +1,22 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/13 + */ +@Data +public class BindDeviceReceiver { + + private String deviceBindingCode; + + private String organizationId; + + private String deviceCallsign; + + private String sn; + + private String deviceModelKey; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/BindStatusReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/BindStatusReceiver.java new file mode 100644 index 0000000..61c8477 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/BindStatusReceiver.java @@ -0,0 +1,30 @@ +package com.dji.sample.manage.model.receiver; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/14 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude +public class BindStatusReceiver { + + private String sn; + + private Boolean isDeviceBindOrganization; + + private String organizationId; + + private String organizationName; + + private String deviceCallsign; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DeviceBasicReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DeviceBasicReceiver.java index 1ae46d8..fa22b73 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/DeviceBasicReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/DeviceBasicReceiver.java @@ -21,18 +21,12 @@ public class DeviceBasicReceiver { private String deviceSn; - private Integer flightMode; - - private Integer flightStatus; - private Double homeLatitude; private Double homeLongitude; private Integer lowBatteryWarningThreshold; - private Integer positionMode; - private Integer seriousLowBatteryWarningThreshold; private List payloads; diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DeviceHmsReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DeviceHmsReceiver.java new file mode 100644 index 0000000..f2557a5 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/DeviceHmsReceiver.java @@ -0,0 +1,28 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/6 + */ +@Data +public class DeviceHmsReceiver { + + private String code; + + private String deviceType; + + private String domainType; + + private Integer imminent; + + private Integer inTheSky; + + private Integer level; + + private Integer module; + + private HmsArgsReceiver args; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DevicePayloadReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DevicePayloadReceiver.java index 2b3f998..830c9d6 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/DevicePayloadReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/DevicePayloadReceiver.java @@ -23,7 +23,4 @@ public class DevicePayloadReceiver { private String sn; - private Integer version; - - private Integer workMode; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DockMediaFileDetailReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DockMediaFileDetailReceiver.java new file mode 100644 index 0000000..bd7745a --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/DockMediaFileDetailReceiver.java @@ -0,0 +1,14 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/17 + */ +@Data +public class DockMediaFileDetailReceiver { + + private Integer remainUpload; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DockSdrReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DockSdrReceiver.java new file mode 100644 index 0000000..4067717 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/DockSdrReceiver.java @@ -0,0 +1,18 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/17 + */ +@Data +public class DockSdrReceiver { + + private Integer downQuality; + + private Double frequencyBand; + + private Integer upQuality; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DockSubDeviceReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DockSubDeviceReceiver.java new file mode 100644 index 0000000..4708ea7 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/DockSubDeviceReceiver.java @@ -0,0 +1,20 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +@Data +public class DockSubDeviceReceiver { + + private String deviceSn; + + private Integer deviceOnlineStatus; + + private Integer devicePaired; + + private String deviceModelKey; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DroneChargeStateReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DroneChargeStateReceiver.java new file mode 100644 index 0000000..6fe4f49 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/DroneChargeStateReceiver.java @@ -0,0 +1,16 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +@Data +public class DroneChargeStateReceiver { + + private Integer state; + + private Integer capacityPercent; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/FirmwareVersionReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/FirmwareVersionReceiver.java new file mode 100644 index 0000000..a3c2634 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/FirmwareVersionReceiver.java @@ -0,0 +1,29 @@ +package com.dji.sample.manage.model.receiver; + +import com.dji.sample.manage.model.enums.DeviceDomainEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/28 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FirmwareVersionReceiver { + + private String firmwareVersion; + + private Integer compatibleStatus; + + private Integer firmwareUpgradeStatus; + + private String sn; + + private DeviceDomainEnum domain; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/HmsArgsReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/HmsArgsReceiver.java new file mode 100644 index 0000000..2d35a1e --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/HmsArgsReceiver.java @@ -0,0 +1,18 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/6 + */ +@Data +public class HmsArgsReceiver { + + private Long componentIndex; + + private Integer sensorIndex; + + private Integer alarmId; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/LiveCapacityReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/LiveCapacityReceiver.java new file mode 100644 index 0000000..af471b7 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/LiveCapacityReceiver.java @@ -0,0 +1,20 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +import java.util.List; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/24 + */ +@Data +public class LiveCapacityReceiver { + + private Integer availableVideoNumber; + + private Integer coexistVideoNumberMax; + + private List deviceList; +} 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 new file mode 100644 index 0000000..84698cc --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java @@ -0,0 +1,18 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +@Data +public class NetworkStateReceiver { + + private Integer type; + + private Integer quality; + + private float rate; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/OrganizationGetReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OrganizationGetReceiver.java new file mode 100644 index 0000000..3b92484 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/OrganizationGetReceiver.java @@ -0,0 +1,16 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/13 + */ +@Data +public class OrganizationGetReceiver { + + private String deviceBindingCode; + + private String organizationId; +} 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 new file mode 100644 index 0000000..ebd8b34 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java @@ -0,0 +1,67 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +@Data +public class OsdDockReceiver { + + private NetworkStateReceiver networkState; + + private Integer droneInDock; + + private DroneChargeStateReceiver droneChargeState; + + private Integer rainfall; + + private Float windSpeed; + + private Float environmentTemperature; + + private Integer environmentHumidity; + + private Float temperature; + + private Integer humidity; + + private Double latitude; + + private Double longitude; + + private Double height; + + private AlternateLandPointReceiver alternateLandPoint; + + private Integer jobNumber; + + private Integer accTime; + + private Long firstPowerOn; + + private PositionStateReceiver positionState; + + private StorageReceiver storage; + + private Integer electricSupplyVoltage; + + private Integer workingVoltage; + + private Integer workingCurrent; + + private Integer backupBatteryVoltage; + + private Integer modeCode; + + private Integer coverState; + + private Integer supplementLightState; + + private Integer putterState; + + private DockSubDeviceReceiver subDevice; + +} 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 new file mode 100644 index 0000000..cdd9abf --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/OsdDockTransmissionReceiver.java @@ -0,0 +1,18 @@ +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/OsdPayloadReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OsdPayloadReceiver.java new file mode 100644 index 0000000..e4125c6 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/OsdPayloadReceiver.java @@ -0,0 +1,33 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/6 + */ +@Data +public class OsdPayloadReceiver { + + private String payloadIndex; + + private Double gimbalPitch; + + private Double gimbalRoll; + + private Double gimbalYaw; + + private Double measureTargetAltitude; + + private Double measureTargetDistance; + + private Double measureTargetLatitude; + + private Double measureTargetLongitude; + + private Integer measureTargetErrorState; + + private Integer version; + +} 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 24d101e..2cb54df 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 @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; import lombok.Data; +import java.util.List; + /** * @author sean.zhou * @version 0.1 @@ -49,7 +51,11 @@ public class OsdSubDeviceReceiver { private Double windDirection; - private Double windSpeed; + private Float windSpeed; private PositionStateReceiver positionState; + + private List payloads; + + private StorageReceiver storage; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/receiver/PositionStateReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/PositionStateReceiver.java index 5c6787f..ad7baec 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/PositionStateReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/PositionStateReceiver.java @@ -13,6 +13,8 @@ import lombok.Data; @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class PositionStateReceiver { + private Integer isCalibration; + private Integer gpsNumber; private Integer isFixed; diff --git a/src/main/java/com/dji/sample/manage/model/receiver/StatusSubDeviceReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/StatusSubDeviceReceiver.java index 4f0d47a..428f16c 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/StatusSubDeviceReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/StatusSubDeviceReceiver.java @@ -16,6 +16,8 @@ public class StatusSubDeviceReceiver { private String sn; + private Integer domain; + private Integer type; @JsonProperty(value = "sub_type") diff --git a/src/main/java/com/dji/sample/manage/model/receiver/StorageReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/StorageReceiver.java new file mode 100644 index 0000000..6cb4b75 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/receiver/StorageReceiver.java @@ -0,0 +1,16 @@ +package com.dji.sample.manage.model.receiver; + +import lombok.Data; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +@Data +public class StorageReceiver { + + private Long total; + + private Long used; +} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/WirelessLinkStateReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/WirelessLinkStateReceiver.java index 5dc8bd5..1efbe3e 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/WirelessLinkStateReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/WirelessLinkStateReceiver.java @@ -15,7 +15,7 @@ public class WirelessLinkStateReceiver { private Integer downloadQuality; - private Integer frequencyBand; + private Double frequencyBand; private Integer upwardQuality; diff --git a/src/main/java/com/dji/sample/manage/service/ICameraVideoService.java b/src/main/java/com/dji/sample/manage/service/ICameraVideoService.java index 808d6dd..df2f1ec 100644 --- a/src/main/java/com/dji/sample/manage/service/ICameraVideoService.java +++ b/src/main/java/com/dji/sample/manage/service/ICameraVideoService.java @@ -3,8 +3,6 @@ package com.dji.sample.manage.service; import com.dji.sample.manage.model.dto.CapacityVideoDTO; import com.dji.sample.manage.model.receiver.CapacityVideoReceiver; -import java.util.List; - /** * @author sean.zhou * @date 2021/11/19 @@ -13,25 +11,9 @@ import java.util.List; public interface ICameraVideoService { /** - * Queries all lens data contained in the camera based on camera id. - * @param cameraId - * @return - */ - List getCameraVideosByCameraId(Integer cameraId); - - /** - * Deletes all the data of this lens, according to the lens id. - * @param ids A collection of lens ids. - * @return true + * Convert the received lens capability object into lens data transfer object. + * @param receiver + * @return data transfer object */ - Boolean deleteCameraVideosById(List ids); - - /** - * Save the live capability of all lenses of this camera. - * @param capacityVideoReceivers live capability of lens - * @param cameraId - * @return - */ - Boolean saveCameraVideoDTOList(List capacityVideoReceivers, Integer cameraId); - + CapacityVideoDTO receiver2Dto(CapacityVideoReceiver receiver); } diff --git a/src/main/java/com/dji/sample/manage/service/ICapacityCameraService.java b/src/main/java/com/dji/sample/manage/service/ICapacityCameraService.java index 02ab494..df4d3ee 100644 --- a/src/main/java/com/dji/sample/manage/service/ICapacityCameraService.java +++ b/src/main/java/com/dji/sample/manage/service/ICapacityCameraService.java @@ -19,14 +19,6 @@ public interface ICapacityCameraService { */ List getCapacityCameraByDeviceSn(String deviceSn); - /** - * Query whether this camera data has been saved based on the device sn and camera location. - * @param deviceSn - * @param cameraIndex - * @return - */ - Boolean checkExist(String deviceSn, String cameraIndex); - /** * Delete all live capability data for this device based on the device sn. * @param deviceSn @@ -38,8 +30,13 @@ public interface ICapacityCameraService { * Save the live capability data of the device. * @param capacityCameraReceivers * @param deviceSn - * @return */ - Boolean saveCapacityCameraReceiverList(List capacityCameraReceivers, String deviceSn); + void saveCapacityCameraReceiverList(List capacityCameraReceivers, String deviceSn); + /** + * Convert the received camera capability object into camera data transfer object. + * @param receiver + * @return + */ + CapacityCameraDTO receiver2Dto(CapacityCameraReceiver receiver); } 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 65324ac..201f82c 100644 --- a/src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java +++ b/src/main/java/com/dji/sample/manage/service/IDeviceDictionaryService.java @@ -13,12 +13,10 @@ public interface IDeviceDictionaryService { /** * Query the type data of the device based on domain, device type and sub type. - * @param domain * @param deviceType * @param subType * @return */ - Optional getOneDictionaryInfoByDomainTypeSubType( - Integer domain, Integer deviceType, Integer subType); + Optional getOneDictionaryInfoByTypeSubType(Integer deviceType, Integer subType); } diff --git a/src/main/java/com/dji/sample/manage/service/IDeviceHmsService.java b/src/main/java/com/dji/sample/manage/service/IDeviceHmsService.java new file mode 100644 index 0000000..c3085ac --- /dev/null +++ b/src/main/java/com/dji/sample/manage/service/IDeviceHmsService.java @@ -0,0 +1,21 @@ +package com.dji.sample.manage.service; + +import com.dji.sample.common.model.PaginationData; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.manage.model.dto.DeviceHmsDTO; +import com.dji.sample.manage.model.param.DeviceHmsQueryParam; +import org.springframework.messaging.MessageHeaders; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/6 + */ +public interface IDeviceHmsService { + + void handleHms(CommonTopicReceiver receiver, MessageHeaders headers); + + PaginationData getDeviceHmsByParam(DeviceHmsQueryParam param); + + void updateUnreadHms(String deviceSn); +} diff --git a/src/main/java/com/dji/sample/manage/service/IDevicePayloadService.java b/src/main/java/com/dji/sample/manage/service/IDevicePayloadService.java index 2eb5c0e..93397c7 100644 --- a/src/main/java/com/dji/sample/manage/service/IDevicePayloadService.java +++ b/src/main/java/com/dji/sample/manage/service/IDevicePayloadService.java @@ -2,6 +2,7 @@ package com.dji.sample.manage.service; import com.dji.sample.manage.model.dto.DevicePayloadDTO; import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; +import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; import java.util.List; @@ -45,4 +46,10 @@ public interface IDevicePayloadService { * @param deviceSns */ void deletePayloadsByDeviceSn(List deviceSns); + + /** + * Update the firmware version information of the payload. + * @param receiver + */ + void updateFirmwareVersion(FirmwareVersionReceiver receiver); } \ No newline at end of file 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 444d49d..911edb7 100644 --- a/src/main/java/com/dji/sample/manage/service/IDeviceService.java +++ b/src/main/java/com/dji/sample/manage/service/IDeviceService.java @@ -1,11 +1,15 @@ package com.dji.sample.manage.service; +import com.dji.sample.common.model.PaginationData; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.component.mqtt.model.CommonTopicResponse; import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.TopologyDeviceDTO; import com.dji.sample.manage.model.param.DeviceQueryParam; +import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; +import org.springframework.messaging.MessageHeaders; import java.util.Collection; import java.util.List; @@ -90,7 +94,7 @@ public interface IDeviceService { * @param sessions The collection of connection objects on the pilot side. * @param sn */ - void pushDeviceOnlineTopo(Collection sessions, String sn); + void pushDeviceOnlineTopo(Collection sessions, String sn, String gatewaySn); /** * Query the information of the device according to the sn of the device. @@ -111,9 +115,9 @@ public interface IDeviceService { * it also broadcasts a push of device online, offline and topology update to PILOT via websocket, * and PILOT will get the device topology list again after receiving the push. * @param workspaceId - * @param gatewaySn + * @param sn */ - void pushDeviceOfflineTopo(String workspaceId, String gatewaySn); + void pushDeviceOfflineTopo(String workspaceId, String sn); /** * When the server receives the request of any device online, offline and topology update in the same workspace, @@ -123,7 +127,7 @@ public interface IDeviceService { * @param deviceSn * @param gatewaySn */ - void pushDeviceOnlineTopo(String workspaceId, String deviceSn, String gatewaySn); + void pushDeviceOnlineTopo(String workspaceId, String gatewaySn, String deviceSn); /** * Handle messages from the osd topic. @@ -132,4 +136,63 @@ public interface IDeviceService { */ void handleOSD(String topic, byte[] payload); + /** + * Update the device information. + * @param deviceDTO + * @return + */ + Boolean updateDevice(DeviceDTO deviceDTO); + + /** + * Bind devices to organizations and people. + * @param device + */ + Boolean bindDevice(DeviceDTO device); + + /** + * Handle dock binding status requests. + * Note: If your business does not need to bind the dock to the organization, + * you can directly reply to the successful message without implementing business logic. + * @param receiver + * @param headers + */ + void bindStatus(CommonTopicReceiver receiver, MessageHeaders headers); + + /** + * Handle dock binding requests. + * Note: If your business does not need to bind the dock to the organization, + * you can directly reply to the successful message without implementing business logic. + * @param receiver + * @param headers + */ + void bindDevice(CommonTopicReceiver receiver, MessageHeaders headers); + + /** + * Get the binding devices list in one workspace. + * @param workspaceId + * @param page + * @param pageSize + * @param domain + * @return + */ + PaginationData getBoundDevicesWithDomain(String workspaceId, Long page, Long pageSize, String domain); + + /** + * Unbind device base on device's sn. + * @param deviceSn + */ + void unbindDevice(String deviceSn); + + /** + * Get device information based on device's sn. + * @param sn device's sn + * @return device + */ + Optional getDeviceBySn(String sn); + + /** + * Update the firmware version information of the device or payload. + * @param receiver + */ + void updateFirmwareVersion(FirmwareVersionReceiver receiver); } \ 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 09cba9e..2d1ac98 100644 --- a/src/main/java/com/dji/sample/manage/service/ILiveStreamService.java +++ b/src/main/java/com/dji/sample/manage/service/ILiveStreamService.java @@ -3,7 +3,7 @@ package com.dji.sample.manage.service; import com.dji.sample.common.model.ResponseResult; import com.dji.sample.manage.model.dto.CapacityDeviceDTO; import com.dji.sample.manage.model.dto.LiveTypeDTO; -import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; +import com.dji.sample.manage.model.receiver.LiveCapacityReceiver; import java.util.List; @@ -22,11 +22,10 @@ public interface ILiveStreamService { List getLiveCapacity(String workspaceId); /** - * Save live capability data from drone. - * @param capacityDeviceReceiver - * @return + * Save live capability data. + * @param liveCapacityReceiver */ - Boolean saveLiveCapacity(CapacityDeviceReceiver capacityDeviceReceiver); + void saveLiveCapacity(LiveCapacityReceiver liveCapacityReceiver); /** * Initiate a live streaming by publishing mqtt message. diff --git a/src/main/java/com/dji/sample/manage/service/ITSAService.java b/src/main/java/com/dji/sample/manage/service/ITSAService.java index a444f92..e11a903 100644 --- a/src/main/java/com/dji/sample/manage/service/ITSAService.java +++ b/src/main/java/com/dji/sample/manage/service/ITSAService.java @@ -1,5 +1,13 @@ package com.dji.sample.manage.service; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; +import com.dji.sample.component.websocket.model.CustomWebSocketMessage; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.model.dto.TelemetryDTO; + +import java.util.Collection; + /** * @author sean * @version 0.3 @@ -14,4 +22,14 @@ public interface ITSAService { * @param sn */ void pushTelemetryData(String workspaceId, Object osdData, String sn); + + /** + * Handle device's osd data. + * @param receiver + * @param webSessions + * @param wsMessage + */ + void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, + Collection webSessions, CustomWebSocketMessage wsMessage); + } diff --git a/src/main/java/com/dji/sample/manage/service/IUserService.java b/src/main/java/com/dji/sample/manage/service/IUserService.java index 2ceff96..133c4c7 100644 --- a/src/main/java/com/dji/sample/manage/service/IUserService.java +++ b/src/main/java/com/dji/sample/manage/service/IUserService.java @@ -1,7 +1,9 @@ package com.dji.sample.manage.service; +import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.ResponseResult; import com.dji.sample.manage.model.dto.UserDTO; +import com.dji.sample.manage.model.dto.UserListDTO; import java.util.Optional; @@ -19,9 +21,10 @@ public interface IUserService { * Verify the username and password to log in. * @param username * @param password + * @param flag * @return */ - ResponseResult userLogin(String username, String password); + ResponseResult userLogin(String username, String password, Integer flag); /** * Create a user object containing a new token. @@ -29,4 +32,13 @@ public interface IUserService { * @return */ Optional refreshToken(String token); + + /** + * Query information about all users in a workspace. + * @param workspaceId uuid + * @return + */ + PaginationData getUsersByWorkspaceId(long page, long pageSize, String workspaceId); + + Boolean updateUser(String workspaceId, String userId, UserListDTO user); } diff --git a/src/main/java/com/dji/sample/manage/service/IWorkspaceService.java b/src/main/java/com/dji/sample/manage/service/IWorkspaceService.java index 5defa10..2d80777 100644 --- a/src/main/java/com/dji/sample/manage/service/IWorkspaceService.java +++ b/src/main/java/com/dji/sample/manage/service/IWorkspaceService.java @@ -1,23 +1,33 @@ package com.dji.sample.manage.service; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.manage.model.dto.WorkspaceDTO; +import org.springframework.messaging.MessageHeaders; import java.util.Optional; public interface IWorkspaceService { /** - * Query the workspace information based on the primary key id of the database. - * @param id primary key id + * Query the information of a workspace based on its workspace id. + * @param workspaceId * @return */ - Optional getWorkspaceById(int id); + Optional getWorkspaceByWorkspaceId(String workspaceId); /** - * Query the information of a workspace based on its workspace id. - * @param workspaceId + * Query the workspace of a workspace based on bind code. + * @param bindCode * @return */ - Optional getWorkspaceByWorkspaceId(String workspaceId); + Optional getWorkspaceNameByBindCode(String bindCode); + + /** + * Handle the request for obtaining the organization information corresponding to the device binding. + * Note: If your business does not need to bind the dock to the organization, + * you can directly reply to the successful message without implementing business logic. + * @param receiver + */ + void replyOrganizationGet(CommonTopicReceiver receiver, MessageHeaders headers); } diff --git a/src/main/java/com/dji/sample/manage/service/impl/AbstractTSAService.java b/src/main/java/com/dji/sample/manage/service/impl/AbstractTSAService.java index 35597fa..56b8217 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/AbstractTSAService.java +++ b/src/main/java/com/dji/sample/manage/service/impl/AbstractTSAService.java @@ -1,16 +1,17 @@ package com.dji.sample.manage.service.impl; -import com.dji.sample.component.mqtt.model.TopicStateReceiver; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; import com.dji.sample.component.websocket.model.BizCodeEnum; import com.dji.sample.component.websocket.model.CustomWebSocketMessage; -import com.dji.sample.component.websocket.model.WebSocketManager; import com.dji.sample.component.websocket.service.ISendMessageService; +import com.dji.sample.component.websocket.service.IWebSocketManageService; +import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.TelemetryDTO; import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.service.ITSAService; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import java.util.Collection; @@ -24,6 +25,15 @@ public abstract class AbstractTSAService implements ITSAService { protected AbstractTSAService tsaService; + @Autowired + protected ObjectMapper mapper; + + @Autowired + protected RedisOpsUtils redisOps; + + @Autowired + private IWebSocketManageService webSocketManageService; + public AbstractTSAService(AbstractTSAService tsaService) { this.tsaService = tsaService; } @@ -34,9 +44,8 @@ public abstract class AbstractTSAService implements ITSAService { @Override public void pushTelemetryData(String workspaceId, Object osdData, String sn) { // All connected accounts on the pilot side of this workspace. - Collection pilotSessions = WebSocketManager - .getValueWithWorkspaceAndUserType( - workspaceId, UserTypeEnum.PILOT.getVal()); + Collection pilotSessions = webSocketManageService + .getValueWithWorkspaceAndUserType(workspaceId, UserTypeEnum.PILOT.getVal()); TelemetryDTO telemetry = TelemetryDTO.builder() .sn(sn) @@ -53,7 +62,6 @@ public abstract class AbstractTSAService implements ITSAService { public abstract void pushTelemetryData(Collection sessions, CustomWebSocketMessage message, Object Object); - protected abstract void handleOSD(TopicStateReceiver receiver, String sn, String workspaceId, JsonNode hostNode, - Collection webSessions, CustomWebSocketMessage wsMessage) - throws JsonProcessingException; + public abstract void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, + Collection webSessions, CustomWebSocketMessage wsMessage); } 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 a2196e4..ec769b3 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 @@ -1,17 +1,11 @@ package com.dji.sample.manage.service.impl; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.dji.sample.manage.dao.ICameraVideoMapper; import com.dji.sample.manage.model.dto.CapacityVideoDTO; -import com.dji.sample.manage.model.entity.CameraVideoEntity; import com.dji.sample.manage.model.receiver.CapacityVideoReceiver; import com.dji.sample.manage.service.ICameraVideoService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; +import java.util.UUID; /** * @author sean.zhou @@ -19,80 +13,17 @@ import java.util.stream.Collectors; * @date 2021/11/19 */ @Service -@Transactional +//@Transactional public class CameraVideoServiceImpl implements ICameraVideoService { - @Autowired - private ICameraVideoMapper mapper; - @Override - public List getCameraVideosByCameraId(Integer cameraId) { - return mapper.selectList( - new LambdaQueryWrapper() - .eq(CameraVideoEntity::getCameraId, cameraId)) - .stream() - .map(this::entityConvertToDto) - .collect(Collectors.toList()); - } - - @Override - public Boolean deleteCameraVideosById(List ids) { - if (ids.isEmpty()) { - return true; - } - return mapper.deleteBatchIds(ids) > 0; - } - - @Override - public Boolean saveCameraVideoDTOList(List capacityVideoReceivers, Integer cameraId) { - for (CapacityVideoReceiver videoDTO : capacityVideoReceivers) { - CameraVideoEntity videoEntity = videoDTOConvertToEntity(videoDTO); - videoEntity.setCameraId(cameraId); - int saveId = this.saveOneCameraVideoEntity(videoEntity); - if (saveId <= 0) { - return false; - } - } - return true; - } - - /** - * Save the live capability of the lens of this camera. - * @param entity lens data - * @return - */ - private Integer saveOneCameraVideoEntity(CameraVideoEntity entity) { - return mapper.insert(entity) > 0 ? entity.getId() : 0; - } - - /** - * Convert the received lens capability object into a database entity object. - * @param dto received lens object - * @return entity - */ - private CameraVideoEntity videoDTOConvertToEntity(CapacityVideoReceiver dto) { - CameraVideoEntity.CameraVideoEntityBuilder builder = CameraVideoEntity.builder(); - if (dto != null) { - builder - .videoIndex(dto.getVideoIndex()) - .videoType(dto.getVideoType()); - } - return builder.build(); - } - - /** - * Convert database entity objects into lens data transfer object. - * @param entity - * @return data transfer object - */ - private CapacityVideoDTO entityConvertToDto(CameraVideoEntity entity) { + public CapacityVideoDTO receiver2Dto(CapacityVideoReceiver receiver) { CapacityVideoDTO.CapacityVideoDTOBuilder builder = CapacityVideoDTO.builder(); - if (entity != null) { - builder - .id(entity.getId()) - .index(entity.getVideoIndex()) - .type(entity.getVideoType()); + if (receiver != null) { + builder.id(UUID.randomUUID().toString()) + .index(receiver.getVideoIndex()) + .type(receiver.getVideoType()); } 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 7ac4fc4..a524648 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 @@ -1,23 +1,20 @@ package com.dji.sample.manage.service.impl; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.dji.sample.manage.dao.ICapacityCameraMapper; +import com.dji.sample.component.mqtt.model.StateDataEnum; +import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.manage.model.dto.CapacityCameraDTO; -import com.dji.sample.manage.model.dto.CapacityVideoDTO; import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; -import com.dji.sample.manage.model.entity.CapacityCameraEntity; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.receiver.CapacityCameraReceiver; import com.dji.sample.manage.service.ICameraVideoService; import com.dji.sample.manage.service.ICapacityCameraService; import com.dji.sample.manage.service.IDeviceDictionaryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; /** @@ -26,107 +23,38 @@ import java.util.stream.Collectors; * @version 0.1 */ @Service -@Transactional +//@Transactional public class CapacityCameraServiceImpl implements ICapacityCameraService { - @Autowired - private ICapacityCameraMapper mapper; - @Autowired private ICameraVideoService cameraVideoService; @Autowired private IDeviceDictionaryService dictionaryService; - @Override - public List getCapacityCameraByDeviceSn(String deviceSn) { - List capacityCamerasList = mapper.selectList( - new LambdaQueryWrapper() - .eq(CapacityCameraEntity::getDeviceSn, deviceSn)) - .stream() - .map(this::entityConvertToDto) - .collect(Collectors.toList()); - capacityCamerasList.forEach(capacityCamera -> { - // Set the lens data for this camera. - capacityCamera.setVideosList( - cameraVideoService.getCameraVideosByCameraId(capacityCamera.getId())); - }); - - return capacityCamerasList; - } + @Autowired + private RedisOpsUtils redisOps; @Override - public Boolean checkExist(String deviceSn, String cameraIndex) { - return mapper.selectOne( - new LambdaQueryWrapper() - .eq(CapacityCameraEntity::getDeviceSn, deviceSn) - .eq(CapacityCameraEntity::getCameraIndex, cameraIndex) - .last(" limit 1")) != null; + public List getCapacityCameraByDeviceSn(String deviceSn) { + return (List) redisOps.hashGet(StateDataEnum.LIVE_CAPACITY.getDesc(), deviceSn); } @Override public Boolean deleteCapacityCameraByDeviceSn(String deviceSn) { - - List capacityCamerasList = this.getCapacityCameraByDeviceSn(deviceSn); - // Return directly if no data exists in the database. - if (capacityCamerasList.isEmpty()) { - return true; - } - - List cameraIds = capacityCamerasList - .stream() - .map(CapacityCameraDTO::getId) - .collect(Collectors.toList()); - - List videoIds = capacityCamerasList - .stream() - .flatMap(camera -> camera.getVideosList().stream()) - .map(CapacityVideoDTO::getId) - .collect(Collectors.toList()); - - return mapper.deleteBatchIds(cameraIds) > 0 && cameraVideoService.deleteCameraVideosById(videoIds); + return redisOps.hashDel(StateDataEnum.LIVE_CAPACITY.getDesc(), new String[]{deviceSn}); } @Override - public Boolean saveCapacityCameraReceiverList(List capacityCameraReceivers, String deviceSn) { - for (CapacityCameraReceiver cameraDTO : capacityCameraReceivers) { - CapacityCameraEntity cameraEntity = receiverConvertToEntity(cameraDTO); - cameraEntity.setDeviceSn(deviceSn); - int cameraId = this.saveOneCapacityCameraEntity(cameraEntity); - if (cameraId <= 0) { - continue; - } - boolean saveVideo = cameraVideoService.saveCameraVideoDTOList( - cameraDTO.getVideosList(), cameraId); - - if (!saveVideo) { - return false; - } - } - return true; + public void saveCapacityCameraReceiverList(List capacityCameraReceivers, String deviceSn) { + List capacity = capacityCameraReceivers.stream() + .map(this::receiver2Dto).collect(Collectors.toList()); + redisOps.hashSet(StateDataEnum.LIVE_CAPACITY.getDesc(), deviceSn, capacity); } - /** - * Save the camera live capability data of the device. - * @param cameraEntity - * @return - */ - private Integer saveOneCapacityCameraEntity(CapacityCameraEntity cameraEntity) { - boolean exist = checkExist( - cameraEntity.getDeviceSn(), cameraEntity.getCameraIndex()); - if (exist) { - return -1; - } - return mapper.insert(cameraEntity) > 0 ? cameraEntity.getId() : 0; - } - - /** - * Convert the received camera capability object into a database entity object. - * @param receiver - * @return - */ - private CapacityCameraEntity receiverConvertToEntity(CapacityCameraReceiver receiver) { - CapacityCameraEntity.CapacityCameraEntityBuilder builder = CapacityCameraEntity.builder(); + @Override + public CapacityCameraDTO receiver2Dto(CapacityCameraReceiver receiver) { + CapacityCameraDTO.CapacityCameraDTOBuilder builder = CapacityCameraDTO.builder(); if (receiver == null) { return builder.build(); } @@ -138,35 +66,17 @@ public class CapacityCameraServiceImpl implements ICapacityCameraService { // type-subType-index if (indexArr.length == 3) { Optional dictionaryOpt = dictionaryService - .getOneDictionaryInfoByDomainTypeSubType( - DeviceDomainEnum.PAYLOAD.getVal(), indexArr[0], indexArr[1]); + .getOneDictionaryInfoByTypeSubType(indexArr[0], indexArr[1]); dictionaryOpt.ifPresent(dictionary -> builder.name(dictionary.getDeviceName())); } return builder - .availableVideoNumber(receiver.getAvailableVideoNumber()) - .coexistVideoNumberMax(receiver.getCoexistVideoNumberMax()) - .cameraIndex(receiver.getCameraIndex()) + .id(UUID.randomUUID().toString()) + .videosList(receiver.getVideosList() + .stream() + .map(cameraVideoService::receiver2Dto) + .collect(Collectors.toList())) + .index(receiver.getCameraIndex()) .build(); } - - /** - * Convert database entity objects into camera data transfer object. - * @param entity - * @return - */ - private CapacityCameraDTO entityConvertToDto(CapacityCameraEntity entity) { - CapacityCameraDTO.CapacityCameraDTOBuilder builder = CapacityCameraDTO.builder(); - - if (entity != null) { - builder - .id(entity.getId()) - .name(entity.getName()) - .description(entity.getDescription()) - .deviceSn(entity.getDeviceSn()) - .index(entity.getCameraIndex()) - .build(); - } - return builder.build(); - } } \ No newline at end of file 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 60da3d5..f0dbad0 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,14 @@ public class DeviceDictionaryServiceImpl implements IDeviceDictionaryService { private IDeviceDictionaryMapper mapper; @Override - public Optional getOneDictionaryInfoByDomainTypeSubType( - Integer domain, Integer deviceType, Integer subType) { - if (domain == null || deviceType == null || subType == null) { + public Optional getOneDictionaryInfoByTypeSubType(Integer deviceType, Integer subType) { + if (deviceType == null || subType == null) { return Optional.empty(); } return Optional.ofNullable( entityConvertToDTO( mapper.selectOne( new LambdaQueryWrapper() - .eq(DeviceDictionaryEntity::getDomain, domain) .eq(DeviceDictionaryEntity::getDeviceType, deviceType) .eq(DeviceDictionaryEntity::getSubType, subType)))); } diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceHmsServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceHmsServiceImpl.java new file mode 100644 index 0000000..61c13ee --- /dev/null +++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceHmsServiceImpl.java @@ -0,0 +1,133 @@ +package com.dji.sample.manage.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.dji.sample.common.model.Pagination; +import com.dji.sample.common.model.PaginationData; +import com.dji.sample.component.mqtt.model.ChannelName; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.mqtt.model.MapKeyConst; +import com.dji.sample.component.mqtt.model.TopicConst; +import com.dji.sample.component.redis.RedisConst; +import com.dji.sample.component.redis.RedisOpsUtils; +import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; +import com.dji.sample.component.websocket.model.BizCodeEnum; +import com.dji.sample.component.websocket.model.CustomWebSocketMessage; +import com.dji.sample.component.websocket.service.impl.SendMessageServiceImpl; +import com.dji.sample.component.websocket.service.impl.WebSocketManageServiceImpl; +import com.dji.sample.manage.dao.IDeviceHmsMapper; +import com.dji.sample.manage.model.common.HmsJsonUtil; +import com.dji.sample.manage.model.common.HmsMessage; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.model.dto.DeviceHmsDTO; +import com.dji.sample.manage.model.dto.TelemetryDTO; +import com.dji.sample.manage.model.entity.DeviceHmsEntity; +import com.dji.sample.manage.model.enums.HmsEnum; +import com.dji.sample.manage.model.enums.UserTypeEnum; +import com.dji.sample.manage.model.param.DeviceHmsQueryParam; +import com.dji.sample.manage.model.receiver.DeviceHmsReceiver; +import com.dji.sample.manage.model.receiver.HmsArgsReceiver; +import com.dji.sample.manage.service.IDeviceHmsService; +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.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/6 + */ +@Service +@Transactional +public class DeviceHmsServiceImpl implements IDeviceHmsService { + + @Autowired + private IDeviceHmsMapper mapper; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private RedisOpsUtils redisOps; + + @Autowired + private SendMessageServiceImpl sendMessageService; + + @Autowired + private WebSocketManageServiceImpl webSocketManageService; + + @Override + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HMS) + public void handleHms(CommonTopicReceiver receiver, MessageHeaders headers) { + } + + @Override + public PaginationData getDeviceHmsByParam(DeviceHmsQueryParam param) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .and(wrapper -> param.getDeviceSn().forEach(sn -> wrapper.eq(DeviceHmsEntity::getSn, sn).or())) + .between(param.getBeginTime() != null && param.getEndTime() != null, + DeviceHmsEntity::getCreateTime, param.getBeginTime(), param.getEndTime()) + .eq(param.getUpdateTime() != null, DeviceHmsEntity::getUpdateTime, param.getUpdateTime()) + .eq(param.getLevel() != null, DeviceHmsEntity::getLevel, param.getLevel()) + .like(StringUtils.hasText(param.getMessage()) && + HmsEnum.MessageLanguage.ZH.getLanguage().equals(param.getLanguage()), + DeviceHmsEntity::getMessageZh, param.getMessage()) + .like(StringUtils.hasText(param.getMessage()) && + HmsEnum.MessageLanguage.EN.getLanguage().equals(param.getLanguage()), + DeviceHmsEntity::getMessageEn, param.getMessage()) + .orderByDesc(DeviceHmsEntity::getCreateTime); + if (param.getPage() == null || param.getPageSize() == null) { + param.setPage(1L); + param.setPageSize(Long.valueOf(mapper.selectCount(queryWrapper))); + } + + Page pagination = mapper.selectPage(new Page<>(param.getPage(), param.getPageSize()), queryWrapper); + + List deviceHmsList = pagination.getRecords().stream().map(this::entity2Dto).collect(Collectors.toList()); + + return new PaginationData(deviceHmsList, new Pagination(pagination)); + } + + @Override + public void updateUnreadHms(String deviceSn) { + mapper.update(DeviceHmsEntity.builder().updateTime(System.currentTimeMillis()).build(), + new LambdaUpdateWrapper() + .eq(DeviceHmsEntity::getSn, deviceSn) + .eq(DeviceHmsEntity::getUpdateTime, 0L)); + redisOps.del(RedisConst.HMS_PREFIX + deviceSn); + } + + private DeviceHmsDTO entity2Dto(DeviceHmsEntity entity) { + if (entity == null) { + return null; + } + return DeviceHmsDTO.builder() + .bid(entity.getBid()) + .tid(entity.getTid()) + .createTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault())) + .updateTime(entity.getUpdateTime().intValue() == 0 ? + null : LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getUpdateTime()), ZoneId.systemDefault())) + .sn(entity.getSn()) + .hmsId(entity.getHmsId()) + .key(entity.getHmsKey()) + .level(entity.getLevel()) + .module(entity.getModule()) + .messageEn(entity.getMessageEn()) + .messageZh(entity.getMessageZh()) + .build(); + } + +} 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 606343c..9fc690d 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,22 +1,25 @@ package com.dji.sample.manage.service.impl; -import com.dji.sample.component.mqtt.model.TopicStateReceiver; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; import com.dji.sample.component.websocket.model.BizCodeEnum; import com.dji.sample.component.websocket.model.CustomWebSocketMessage; -import com.dji.sample.manage.model.DeviceStatusManager; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.model.dto.DevicePayloadDTO; import com.dji.sample.manage.model.dto.TelemetryDTO; import com.dji.sample.manage.model.dto.TelemetryDeviceDTO; import com.dji.sample.manage.model.enums.DeviceDomainEnum; +import com.dji.sample.manage.model.receiver.OsdPayloadReceiver; import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; -import java.time.LocalDateTime; import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * @author sean @@ -24,10 +27,11 @@ import java.util.Collection; * @date 2022/2/21 */ @Service +@Slf4j public class DeviceOSDServiceImpl extends AbstractTSAService { - protected DeviceOSDServiceImpl() { - super(null); + protected DeviceOSDServiceImpl(@Autowired @Qualifier("dockOSDServiceImpl") AbstractTSAService tsaService) { + super(tsaService); } @Override @@ -50,20 +54,32 @@ public class DeviceOSDServiceImpl extends AbstractTSAService { } } @Override - protected void handleOSD(TopicStateReceiver receiver, String sn, String workspaceId, JsonNode hostNode, - Collection webSessions, CustomWebSocketMessage wsMessage) throws JsonProcessingException { - // Real-time update of device status in memory - DeviceStatusManager.STATUS_MANAGER.put( - DeviceDomainEnum.SUB_DEVICE.getVal() + "/" + sn, LocalDateTime.now()); - wsMessage.setBizCode(BizCodeEnum.DEVICE_OSD.getCode()); + public void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, + Collection webSessions, + CustomWebSocketMessage wsMessage) { + if (DeviceDomainEnum.SUB_DEVICE.getDesc().equals(device.getDomain())) { + wsMessage.setBizCode(BizCodeEnum.DEVICE_OSD.getCode()); - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - OsdSubDeviceReceiver data = mapper.treeToValue(hostNode, OsdSubDeviceReceiver.class); + OsdSubDeviceReceiver data = mapper.convertValue(receiver.getData(), OsdSubDeviceReceiver.class); + List payloadsList = device.getPayloadsList(); + try { + Map receiverData = (Map) receiver.getData(); + data.setPayloads(payloadsList.stream() + .map(payload -> mapper.convertValue( + receiverData.getOrDefault(payload.getPayloadName(), Map.of()), + OsdPayloadReceiver.class)) + .collect(Collectors.toList())); - wsMessage.setData(data); + } catch (NullPointerException e) { + log.warn("Please remount the payload, or restart the drone. Otherwise the data of the payload will not be received."); + } - sendMessageService.sendBatch(webSessions, wsMessage); - this.pushTelemetryData(workspaceId, data, sn); + + wsMessage.getData().setHost(data); + + sendMessageService.sendBatch(webSessions, wsMessage); + this.pushTelemetryData(device.getWorkspaceId(), data, device.getDeviceSn()); + } + tsaService.handleOSD(receiver, device, 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 a88504f..7f05b54 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 @@ -1,12 +1,16 @@ package com.dji.sample.manage.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.dji.sample.component.redis.RedisConst; +import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.manage.dao.IDevicePayloadMapper; +import com.dji.sample.manage.model.dto.DeviceDTO; 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; import com.dji.sample.manage.service.IDeviceDictionaryService; import com.dji.sample.manage.service.IDevicePayloadService; @@ -14,6 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -37,6 +42,9 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { @Autowired private ICapacityCameraService capacityCameraService; + @Autowired + private RedisOpsUtils redisOps; + @Override public Integer checkPayloadExist(String payloadSn) { DevicePayloadEntity devicePayload = mapper.selectOne( @@ -57,12 +65,28 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { @Override public Boolean savePayloadDTOs(List payloadReceiverList) { + if (payloadReceiverList.isEmpty()) { + return true; + } + + String deviceSn = payloadReceiverList.get(0).getDeviceSn(); + String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; + DeviceDTO device = (DeviceDTO) redisOps.get(key); + List payloads = new ArrayList<>(); + for (DevicePayloadReceiver payloadReceiver : payloadReceiverList) { int payloadId = this.saveOnePayloadDTO(payloadReceiver); if (payloadId <= 0) { return false; } + payloads.add(this.receiver2Dto(payloadReceiver)); + } + + if (payloads.isEmpty()) { + payloads = this.getDevicePayloadEntitiesByDeviceSn(deviceSn); } + device.setPayloadsList(payloads); + redisOps.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(), device, RedisConst.DEVICE_ALIVE_SECOND); return true; } @@ -91,6 +115,15 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { }); } + @Override + public void updateFirmwareVersion(FirmwareVersionReceiver receiver) { + mapper.update(DevicePayloadEntity.builder() + .firmwareVersion(receiver.getFirmwareVersion()) + .build() + , new LambdaUpdateWrapper() + .eq(DevicePayloadEntity::getDeviceSn, receiver.getSn())); + } + /** * Convert database entity objects into payload data transfer object. * @param entity @@ -129,8 +162,7 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { if (arr.length == 3) { Optional dictionaryOpt = dictionaryService - .getOneDictionaryInfoByDomainTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), - arr[0], arr[1]); + .getOneDictionaryInfoByTypeSubType(arr[0], arr[1]); dictionaryOpt.ifPresent(dictionary -> builder.payloadName(dictionary.getDeviceName()) .payloadDesc(dictionary.getDeviceDesc())); @@ -147,10 +179,17 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { return builder .payloadSn(dto.getSn()) - .version(dto.getVersion()) - .deviceSn(dto.getSn() - .substring(0, - dto.getSn().indexOf("-"))) + .deviceSn(dto.getDeviceSn()) + .build(); + } + + private DevicePayloadDTO receiver2Dto(DevicePayloadReceiver receiver) { + DevicePayloadDTO.DevicePayloadDTOBuilder builder = DevicePayloadDTO.builder(); + if (receiver == null) { + return builder.build(); + } + return builder.payloadSn(receiver.getSn()) + .payloadName(receiver.getPayloadIndex()) .build(); } 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 80e34df..dbfbf77 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 @@ -1,15 +1,21 @@ package com.dji.sample.manage.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.dji.sample.component.mqtt.model.CommonTopicResponse; -import com.dji.sample.component.mqtt.model.TopicStateReceiver; +import com.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.Pagination; +import com.dji.sample.common.model.PaginationData; +import com.dji.sample.component.mqtt.model.*; import com.dji.sample.component.mqtt.service.IMessageSenderService; import com.dji.sample.component.mqtt.service.IMqttTopicService; +import com.dji.sample.component.redis.RedisConst; +import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; import com.dji.sample.component.websocket.model.BizCodeEnum; import com.dji.sample.component.websocket.model.CustomWebSocketMessage; -import com.dji.sample.component.websocket.model.WebSocketManager; import com.dji.sample.component.websocket.service.ISendMessageService; +import com.dji.sample.component.websocket.service.IWebSocketManageService; import com.dji.sample.manage.dao.IDeviceMapper; import com.dji.sample.manage.model.dto.*; import com.dji.sample.manage.model.entity.DeviceEntity; @@ -17,28 +23,26 @@ import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.enums.IconUrlEnum; import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.model.param.DeviceQueryParam; -import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; -import com.dji.sample.manage.model.receiver.StatusSubDeviceReceiver; -import com.dji.sample.manage.service.IDeviceDictionaryService; -import com.dji.sample.manage.service.IDevicePayloadService; -import com.dji.sample.manage.service.IDeviceService; -import com.dji.sample.manage.service.IWorkspaceService; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; +import com.dji.sample.manage.model.receiver.*; +import com.dji.sample.manage.service.*; +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.beans.factory.annotation.Qualifier; +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.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -76,24 +80,62 @@ public class DeviceServiceImpl implements IDeviceService { @Autowired private ISendMessageService sendMessageService; + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private RedisOpsUtils redisOps; + + @Autowired + private IWebSocketManageService webSocketManageService; + @Autowired @Qualifier("gatewayOSDServiceImpl") - private AbstractTSAService tsaService; + private ITSAService tsaService; @Override public Boolean deviceOffline(String gatewaySn) { - List gatewaysList = this.getDevicesByParams( - DeviceQueryParam.builder() - .deviceSn(gatewaySn) - .build()); + this.subscribeTopicOnline(gatewaySn); + + // Only the remote controller is logged in and the aircraft is not connected. + String key = RedisConst.DEVICE_ONLINE_PREFIX + gatewaySn; + + boolean exist = redisOps.checkExist(key); + if (!exist) { + Optional 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; + } - // If no information about this gateway device exists in the database, the drone is considered to be offline. - if (gatewaysList.isEmpty()) { - log.debug("The drone is already offline."); + long expire = redisOps.getExpire(key); + // If the key about the device in redis has expired, the remote control is considered to be offline. + if (expire <= 0) { + log.debug("The remote control is already offline."); return true; } - // Handle the drone connected to the gateway device offline. - return this.subDeviceOffline(gatewaysList.get(0).getChildDeviceSn()); + + String deviceSn = ((DeviceDTO)(redisOps.get(key))).getChildDeviceSn(); + if (deviceSn.equals(gatewaySn)) { + return true; + } + + return subDeviceOffline(deviceSn); } @Override @@ -101,82 +143,117 @@ public class DeviceServiceImpl implements IDeviceService { // Cancel drone-related subscriptions. this.unsubscribeTopicOffline(deviceSn); - List devicesList = this.getDevicesByParams( - DeviceQueryParam.builder() - .deviceSn(deviceSn) - .build()); - - // If no information about this drone exists in the database, the drone is considered to be offline. - if (devicesList.isEmpty()) { - log.debug("{} is already offline.", deviceSn); + 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. + 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); + // Publish the latest device topology information in the current workspace. + this.pushDeviceOfflineTopo(device.getWorkspaceId(), deviceSn); - List ids = devicesList.stream() - .map(DeviceDTO::getDeviceSn) - .collect(Collectors.toList()); - - // Delete all data related to the drone. - boolean isDel = this.delDeviceByDeviceSns(ids); - payloadService.deletePayloadsByDeviceSn(ids); - - log.debug("{} offline status: {}.", deviceSn, isDel); - return isDel; + redisOps.del(key); + log.debug("{} offline.", deviceSn); + return true; } @Override public Boolean deviceOnline(StatusGatewayReceiver deviceGateway) { String deviceSn = deviceGateway.getSubDevices().get(0).getSn(); - - List devicesList = this.getDevicesByParams( - DeviceQueryParam.builder() - .deviceSn(deviceSn) - .build()); - // If the information about this drone exists in the database, the drone is considered to be online. - if (!devicesList.isEmpty()) { + String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; + // change log: Use redis instead of + long time = redisOps.getExpire(key); + long now = System.currentTimeMillis(); + + if (time > 0) { + redisOps.expireKey(RedisConst.DEVICE_ONLINE_PREFIX + deviceGateway.getSn(), RedisConst.DEVICE_ALIVE_SECOND); + redisOps.expireKey(key, RedisConst.DEVICE_ALIVE_SECOND); + DeviceDTO device = DeviceDTO.builder().loginTime(LocalDateTime.now()).deviceSn(deviceSn).build(); + DeviceDTO gateway = DeviceDTO.builder() + .loginTime(LocalDateTime.now()) + .deviceSn(deviceGateway.getSn()) + .childDeviceSn(deviceSn).build(); + this.updateDevice(gateway); + this.updateDevice(device); + String workspaceId = ((DeviceDTO)(redisOps.get(key))).getWorkspaceId(); + if (StringUtils.hasText(workspaceId)) { + this.subscribeTopicOnline(deviceSn); + this.subscribeTopicOnline(deviceGateway.getSn()); + } log.warn("{} is already online.", deviceSn); - // Subscribe to topic related to drone and gateway devices. - this.subscribeTopicOnline(deviceGateway.getSn()); - this.subscribeTopicOnline(deviceSn); return true; } - // Delete the gateway device information that was previously bound to the drone. - this.delDeviceByDeviceSns( - this.getDevicesByParams( - DeviceQueryParam.builder() - .childSn(deviceSn) - .build()) - .stream() - .map(DeviceDTO::getDeviceSn) - .collect(Collectors.toList())); + List gatewaysList = this.getDevicesByParams( + DeviceQueryParam.builder() + .childSn(deviceSn) + .build()); + gatewaysList.stream().filter( + gateway -> !gateway.getDeviceSn().equals(deviceGateway.getSn())) + .findAny() + .ifPresent(gateway -> { + gateway.setChildDeviceSn(""); + this.updateDevice(gateway); + }); + DeviceEntity gateway = deviceGatewayConvertToDeviceEntity(deviceGateway); - gateway.setWorkspaceId(WorkspaceDTO.DEFAULT_WORKSPACE_ID); + 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)); - subDevice.setWorkspaceId(WorkspaceDTO.DEFAULT_WORKSPACE_ID); - // Set the icon of the drone device displayed in the pilot's map, required in the TSA module. - subDevice.setUrlSelect(IconUrlEnum.SELECT_EQUIPMENT.getUrl()); // Set the icon of the drone device displayed in the pilot's map when it is selected, required in the TSA module. subDevice.setUrlNormal(IconUrlEnum.NORMAL_EQUIPMENT.getUrl()); + // 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); + + // dock go online + if (deviceGateway.getDomain() != null && DeviceDomainEnum.DOCK.getVal() == deviceGateway.getDomain()) { + Optional deviceOpt = this.getDeviceBySn(deviceGateway.getSn()); + if (deviceOpt.isEmpty()) { + log.info("The dock is not bound and cannot go online."); + return false; + } + gateway.setNickname(null); + subDevice.setNickname(null); + } - gateway.setChildSn(subDevice.getDeviceSn()); - - boolean isSave = this.saveDevice(gateway) > 0 && this.saveDevice(subDevice) > 0; - - log.debug(subDevice.getDeviceSn() + " online status: {}", isSave); - if (isSave) { - // Subscribe to topic related to drone and gateway devices. - this.subscribeTopicOnline(subDevice.getDeviceSn()); - this.subscribeTopicOnline(gateway.getDeviceSn()); + Optional gatewayOpt = this.saveDevice(gateway); + String workspaceId = this.saveDevice(subDevice).orElse(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); } - return isSave; + // Subscribe to topic related to drone devices. + this.subscribeTopicOnline(deviceSn); + this.subscribeTopicOnline(deviceGateway.getSn()); + return true; } @Override @@ -191,6 +268,8 @@ public class DeviceServiceImpl implements IDeviceService { 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); } @Override @@ -198,6 +277,8 @@ public class DeviceServiceImpl implements IDeviceService { 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); } @Override @@ -232,17 +313,21 @@ public class DeviceServiceImpl implements IDeviceService { return mapper.selectList( new LambdaQueryWrapper() .eq(StringUtils.hasText(param.getDeviceSn()), - DeviceEntity::getDeviceSn, param.getDeviceSn()) + DeviceEntity::getDeviceSn, param.getDeviceSn()) .eq(param.getDeviceType() != null, DeviceEntity::getDeviceType, param.getDeviceType()) .eq(param.getSubType() != null, DeviceEntity::getSubType, param.getSubType()) .eq(StringUtils.hasText(param.getChildSn()), DeviceEntity::getChildSn, param.getChildSn()) - .eq(param.getDomain() != null, - DeviceEntity::getDomain, param.getDomain()) + .and(!CollectionUtils.isEmpty(param.getDomains()), wrapper -> { + for (Integer domain : param.getDomains()) { + wrapper.eq(DeviceEntity::getDomain, domain).or(); + } + }) .eq(StringUtils.hasText(param.getWorkspaceId()), DeviceEntity::getWorkspaceId, param.getWorkspaceId()) + .eq(param.getBoundStatus() != null, DeviceEntity::getBoundStatus, param.getBoundStatus()) .orderBy(param.isOrderBy(), param.isAsc(), DeviceEntity::getId)) .stream() @@ -255,13 +340,13 @@ public class DeviceServiceImpl implements IDeviceService { List devicesList = this.getDevicesByParams( DeviceQueryParam.builder() .workspaceId(workspaceId) - .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) + .domains(List.of(DeviceDomainEnum.SUB_DEVICE.getVal())) .build()); devicesList.forEach(device -> { this.spliceDeviceTopo(device); device.setWorkspaceId(workspaceId); - + device.setStatus(redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())); }); return devicesList; } @@ -301,7 +386,7 @@ public class DeviceServiceImpl implements IDeviceService { } @Override - public void pushDeviceOnlineTopo(Collection sessions, String sn) { + public void pushDeviceOnlineTopo(Collection sessions, String sn, String gatewaySn) { CustomWebSocketMessage pilotMessage = CustomWebSocketMessage.builder() @@ -312,6 +397,9 @@ public class DeviceServiceImpl implements IDeviceService { this.getDeviceTopoForPilot(sn) .ifPresent(pilotMessage::setData); + boolean exist = redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + sn); + pilotMessage.getData().setOnlineStatus(exist); + pilotMessage.getData().setGatewaySn(gatewaySn.equals(sn) ? "" : gatewaySn); sendMessageService.sendBatch(sessions, pilotMessage); } @@ -321,91 +409,82 @@ public class DeviceServiceImpl implements IDeviceService { TopologyDeviceDTO.TopologyDeviceDTOBuilder builder = TopologyDeviceDTO.builder(); if (device != null) { - String domain = String.valueOf(DeviceDomainEnum.getVal(device.getDomain())); + int domain = DeviceDomainEnum.getVal(device.getDomain()); String subType = String.valueOf(device.getSubType()); String type = String.valueOf(device.getType()); builder.sn(device.getDeviceSn()) - .deviceCallsign(device.getDeviceName()) + .deviceCallsign(device.getNickname()) .deviceModel(DeviceModelDTO.builder() - .domain(domain) + .domain(String.valueOf(domain)) .subType(subType) .type(type) .key(domain + "-" + type + "-" + subType) .build()) .iconUrls(device.getIconUrl()) + .onlineStatus(redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())) + .boundStatus(device.getBoundStatus()) + .model(device.getDeviceName()) + .userId(device.getUserId()) + .domain(DeviceDomainEnum.getDesc(domain)) .build(); } return builder.build(); } @Override - public void pushDeviceOnlineTopo(String workspaceId, String deviceSn, String gatewaySn) { + public void pushDeviceOnlineTopo(String workspaceId, String gatewaySn, String deviceSn) { - // All connected accounts on the pilot side of this workspace. - Collection pilotSessions = WebSocketManager - .getValueWithWorkspaceAndUserType( - workspaceId, UserTypeEnum.PILOT.getVal()); + // All connected accounts in this workspace. + Collection allSessions = webSocketManageService.getValueWithWorkspace(workspaceId); - this.pushDeviceOnlineTopo(pilotSessions, deviceSn); - this.pushDeviceOnlineTopo(pilotSessions, gatewaySn); - this.pushDeviceUpdateTopo(pilotSessions, deviceSn); - this.pushDeviceUpdateTopo(pilotSessions, gatewaySn); + if (!gatewaySn.equals(deviceSn)) { + this.pushDeviceOnlineTopo(allSessions, deviceSn, gatewaySn); + this.pushDeviceUpdateTopo(allSessions, deviceSn); + } + this.pushDeviceOnlineTopo(allSessions, gatewaySn, gatewaySn); + this.pushDeviceUpdateTopo(allSessions, gatewaySn); } @Override - public void pushDeviceOfflineTopo(String workspaceId, String gatewaySn) { - // All connected accounts on the pilot side of this workspace. - Collection pilotSessions = WebSocketManager - .getValueWithWorkspaceAndUserType( - workspaceId, UserTypeEnum.PILOT.getVal()); - - - List gatewaysList = this.getDevicesByParams( - DeviceQueryParam.builder() - .deviceSn(gatewaySn) - .build()); - - if (!gatewaysList.isEmpty()) { - String deviceSn = gatewaysList.get(0).getChildDeviceSn(); - this.pushDeviceOfflineTopo(pilotSessions, deviceSn); - this.pushDeviceUpdateTopo(pilotSessions, deviceSn); - } + public void pushDeviceOfflineTopo(String workspaceId, String sn) { + // All connected accounts of this workspace. + Collection allSessions = webSocketManageService + .getValueWithWorkspace(workspaceId); - this.pushDeviceOfflineTopo(pilotSessions, gatewaySn); - this.pushDeviceUpdateTopo(pilotSessions, gatewaySn); + this.pushDeviceOfflineTopo(allSessions, sn); + this.pushDeviceUpdateTopo(allSessions, sn); } @Override public void handleOSD(String topic, byte[] payload) { - TopicStateReceiver receiver; + CommonTopicReceiver receiver; try { String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), topic.indexOf(OSD_SUF)); - List deviceList = this.getDevicesByParams( - DeviceQueryParam.builder().deviceSn(from).build()); - if (deviceList.isEmpty()) { - return; - } + // Real-time update of device status in memory + redisOps.expireKey(RedisConst.DEVICE_ONLINE_PREFIX + from, RedisConst.DEVICE_ALIVE_SECOND); - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + from); - receiver = mapper.readValue(payload, TopicStateReceiver.class); + if (device == null || !StringUtils.hasText(device.getWorkspaceId())) { + return; + } - CustomWebSocketMessage wsMessage = CustomWebSocketMessage.builder() - .timestamp(System.currentTimeMillis()).build(); + receiver = objectMapper.readValue(payload, CommonTopicReceiver.class); - JsonNode hostNode = mapper.readTree(payload).findPath("data"); + CustomWebSocketMessage wsMessage = CustomWebSocketMessage.builder() + .timestamp(System.currentTimeMillis()) + .data(TelemetryDTO.builder().sn(from).build()) + .build(); - String workspaceId = deviceList.get(0).getWorkspaceId(); - Collection webSessions = WebSocketManager + Collection webSessions = webSocketManageService .getValueWithWorkspaceAndUserType( - workspaceId, UserTypeEnum.WEB.getVal()); + device.getWorkspaceId(), UserTypeEnum.WEB.getVal()); - tsaService.handleOSD(receiver, from, workspaceId, hostNode, webSessions, wsMessage); + tsaService.handleOSD(receiver, device, webSessions, wsMessage); } catch (IOException e) { e.printStackTrace(); @@ -451,7 +530,7 @@ public class DeviceServiceImpl implements IDeviceService { * @param entity * @return */ - private Integer saveDevice(DeviceEntity entity) { + private Optional saveDevice(DeviceEntity entity) { DeviceEntity deviceEntity = mapper.selectOne( new LambdaQueryWrapper() .eq(DeviceEntity::getDeviceSn, entity.getDeviceSn())); @@ -459,9 +538,9 @@ public class DeviceServiceImpl implements IDeviceService { if (deviceEntity != null) { entity.setId(deviceEntity.getId()); mapper.updateById(entity); - return deviceEntity.getId(); + return Optional.of(deviceEntity); } - return mapper.insert(entity) > 0 ? entity.getId() : 0; + return mapper.insert(entity) > 0 ? Optional.of(entity) : Optional.empty(); } /** @@ -477,19 +556,20 @@ public class DeviceServiceImpl implements IDeviceService { // Query the model information of this gateway device. Optional dictionaryOpt = dictionaryService - .getOneDictionaryInfoByDomainTypeSubType(gateway.getDomain(), - gateway.getType(), gateway.getSubType()); + .getOneDictionaryInfoByTypeSubType(gateway.getType(), gateway.getSubType()); dictionaryOpt.ifPresent(entity -> builder.deviceName(entity.getDeviceName()) + .nickname(entity.getDeviceName()) .deviceDesc(entity.getDeviceDesc())); return builder .deviceSn(gateway.getSn()) - .domain(gateway.getDomain()) .subType(gateway.getSubType()) .deviceType(gateway.getType()) .version(gateway.getVersion()) + .domain(gateway.getDomain() != null ? + gateway.getDomain() : DeviceDomainEnum.GATEWAY.getVal()) .build(); } @@ -506,48 +586,330 @@ public class DeviceServiceImpl implements IDeviceService { // Query the model information of this drone device. Optional dictionaryOpt = dictionaryService - .getOneDictionaryInfoByDomainTypeSubType(DeviceDomainEnum.SUB_DEVICE.getVal(), - device.getType(), device.getSubType()); + .getOneDictionaryInfoByTypeSubType(device.getType(), device.getSubType()); dictionaryOpt.ifPresent(dictionary -> builder.deviceName(dictionary.getDeviceName()) + .nickname(dictionary.getDeviceName()) .deviceDesc(dictionary.getDeviceDesc())); return builder .deviceSn(device.getSn()) .deviceType(device.getType()) .subType(device.getSubType()) - .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) .version(device.getVersion()) .deviceIndex(device.getIndex()) + .domain(device.getDomain() != null ? + device.getDomain() : DeviceDomainEnum.SUB_DEVICE.getVal()) .build(); } /** - * Convert database entity objects into device data transfer object. + * Convert database entity object into device data transfer object. * @param entity * @return */ private DeviceDTO deviceEntityConvertToDTO(DeviceEntity entity) { - DeviceDTO.DeviceDTOBuilder builder = DeviceDTO.builder(); - - if (entity != null) { - builder.deviceSn(entity.getDeviceSn()) - .childDeviceSn(entity.getChildSn()) - .deviceName(entity.getDeviceName()) - .deviceDesc(entity.getDeviceDesc()) - .deviceIndex(entity.getDeviceIndex()) - .workspaceId(entity.getWorkspaceId()) - .type(entity.getDeviceType()) - .subType(entity.getSubType()) - .domain(DeviceDomainEnum.getDesc(entity.getDomain())) - .iconUrl(IconUrlDTO.builder() - .normalUrl(entity.getUrlNormal()) - .selectUrl(entity.getUrlSelect()) - .build()) - .build(); + if (entity == null) { + return null; } - return builder.build(); + return DeviceDTO.builder() + .deviceSn(entity.getDeviceSn()) + .childDeviceSn(entity.getChildSn()) + .deviceName(entity.getDeviceName()) + .deviceDesc(entity.getDeviceDesc()) + .deviceIndex(entity.getDeviceIndex()) + .workspaceId(entity.getWorkspaceId()) + .type(entity.getDeviceType()) + .subType(entity.getSubType()) + .domain(DeviceDomainEnum.getDesc(entity.getDomain())) + .iconUrl(IconUrlDTO.builder() + .normalUrl(entity.getUrlNormal()) + .selectUrl(entity.getUrlSelect()) + .build()) + .boundStatus(entity.getBoundStatus()) + .loginTime(entity.getLoginTime() != null ? + LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getLoginTime()), ZoneId.systemDefault()) + : null) + .boundTime(entity.getBoundTime() != null ? + LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getBoundTime()), ZoneId.systemDefault()) + : null) + .nickname(entity.getNickname()) + .firmwareVersion(entity.getFirmwareVersion()) + .workspaceName(entity.getWorkspaceId() != null ? + workspaceService.getWorkspaceByWorkspaceId(entity.getWorkspaceId()) + .map(WorkspaceDTO::getWorkspaceName).orElse("") : "") + .build(); + } + + @Override + public Boolean updateDevice(DeviceDTO deviceDTO) { + int update = mapper.update(this.deviceDTO2Entity(deviceDTO), + new LambdaUpdateWrapper().eq(DeviceEntity::getDeviceSn, deviceDTO.getDeviceSn())); + return update > 0; + } + + @Override + public Boolean bindDevice(DeviceDTO device) { + device.setBoundStatus(true); + 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); + redisDevice.setWorkspaceId(device.getWorkspaceId()); + redisOps.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND); + + if (DeviceDomainEnum.GATEWAY.getDesc().equals(redisDevice.getDomain())) { + this.pushDeviceOnlineTopo(webSocketManageService.getValueWithWorkspace(device.getWorkspaceId()), + device.getDeviceSn(), device.getDeviceSn()); + } + if (DeviceDomainEnum.SUB_DEVICE.getDesc().equals(redisDevice.getDomain())) { + DeviceDTO subDevice = this.getDevicesByParams(DeviceQueryParam.builder() + .childSn(device.getChildDeviceSn()) + .build()).get(0); + this.pushDeviceOnlineTopo(webSocketManageService.getValueWithWorkspace(device.getWorkspaceId()), + device.getDeviceSn(), subDevice.getDeviceSn()); + } + this.subscribeTopicOnline(device.getDeviceSn()); + + return true; + } + + @Override + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS, outputChannel = ChannelName.OUTBOUND) + public void bindStatus(CommonTopicReceiver receiver, MessageHeaders headers) { + List> data = ((Map>>) receiver.getData()).get(MapKeyConst.DEVICES); + String dockSn = data.get(0).get(MapKeyConst.SN); + String droneSn = data.size() > 1 ? data.get(1).get(MapKeyConst.SN) : "null"; + + Optional dockOpt = this.getDeviceBySn(dockSn); + Optional droneOpt = this.getDeviceBySn(droneSn); + + List bindStatusResult = new ArrayList<>(); + bindStatusResult.add(dockOpt.isPresent() ? this.dto2BindStatus(dockOpt.get()) : + BindStatusReceiver.builder().sn(dockSn).isDeviceBindOrganization(false).build()); + if (data.size() > 1) { + bindStatusResult.add(droneOpt.isPresent() ? this.dto2BindStatus(droneOpt.get()) : + BindStatusReceiver.builder().sn(droneSn).isDeviceBindOrganization(false).build()); + } + + messageSender.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + _REPLY_SUF, + CommonTopicResponse.builder() + .tid(receiver.getTid()) + .bid(receiver.getBid()) + .timestamp(System.currentTimeMillis()) + .method(RequestsMethodEnum.AIRPORT_BIND_STATUS.getMethod()) + .data(RequestsReply.success(Map.of(MapKeyConst.BIND_STATUS, bindStatusResult))) + .build()); + + } + + @Override + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND) + public void bindDevice(CommonTopicReceiver receiver, MessageHeaders headers) { + Map> data = objectMapper.convertValue(receiver.getData(), + new TypeReference>>() {}); + List devices = data.get(MapKeyConst.BIND_DEVICES); + BindDeviceReceiver dock = null; + BindDeviceReceiver drone = null; + for (BindDeviceReceiver device : devices) { + int val = Integer.parseInt(device.getDeviceModelKey().split("-")[0]); + if (val == DeviceDomainEnum.DOCK.getVal()) { + dock = device; + } + if (val == DeviceDomainEnum.SUB_DEVICE.getVal()) { + drone = device; + } + } + + assert dock != null; + + Optional dockEntityOpt = this.bindDevice2Entity(dock); + Optional droneEntityOpt = this.bindDevice2Entity(drone); + + List bindResult = new ArrayList<>(); + + droneEntityOpt.ifPresent(droneEntity -> { + dockEntityOpt.get().setChildSn(droneEntity.getDeviceSn()); + Optional deviceEntityOpt = this.saveDevice(droneEntity); + bindResult.add( + deviceEntityOpt.isPresent() ? + ErrorInfoReply.success(droneEntity.getDeviceSn()) : + new ErrorInfoReply(droneEntity.getDeviceSn(), + CommonErrorEnum.DEVICE_BINDING_FAILED.getErrorCode()) + ); + }); + Optional dockOpt = this.saveDevice(dockEntityOpt.get()); + + bindResult.add(dockOpt.isPresent() ? + ErrorInfoReply.success(dock.getSn()) : + new ErrorInfoReply(dock.getSn(), + CommonErrorEnum.DEVICE_BINDING_FAILED.getErrorCode())); + + String topic = headers.get(MqttHeaders.RECEIVED_TOPIC) + _REPLY_SUF; + messageSender.publish(topic, + CommonTopicResponse.builder() + .tid(receiver.getTid()) + .bid(receiver.getBid()) + .method(RequestsMethodEnum.AIRPORT_ORGANIZATION_BIND.getMethod()) + .timestamp(System.currentTimeMillis()) + .data(RequestsReply.success(Map.of(MapKeyConst.ERR_INFOS, bindResult))) + .build()); + + } + + @Override + public PaginationData getBoundDevicesWithDomain(String workspaceId, Long page, + Long pageSize, String domain) { + + Page pagination = mapper.selectPage(new Page<>(page, pageSize), + new LambdaQueryWrapper() + .eq(DeviceEntity::getDomain, DeviceDomainEnum.getVal(domain)) + .eq(DeviceEntity::getWorkspaceId, workspaceId) + .eq(DeviceEntity::getBoundStatus, true)); + List devicesList = pagination.getRecords().stream().map(this::deviceEntityConvertToDTO) + .peek(device -> { + device.setStatus(redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())); + if (StringUtils.hasText(device.getChildDeviceSn())) { + Optional childOpt = this.getDeviceBySn(device.getChildDeviceSn()); + childOpt.ifPresent(child -> { + child.setStatus( + redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + child.getDeviceSn())); + child.setWorkspaceName(device.getWorkspaceName()); + device.setChildren(child); + }); + } + }) + .collect(Collectors.toList()); + return new PaginationData(devicesList, new Pagination(pagination)); + } + + @Override + public void unbindDevice(String deviceSn) { + String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; + DeviceDTO redisDevice = (DeviceDTO) redisOps.get(key); + redisDevice.setWorkspaceId(""); + redisOps.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND); + + DeviceDTO device = DeviceDTO.builder() + .deviceSn(deviceSn) + .workspaceId("") + .userId("") + .boundStatus(false) + .build(); + this.updateDevice(device); } + @Override + public Optional getDeviceBySn(String sn) { + List devicesList = this.getDevicesByParams(DeviceQueryParam.builder().deviceSn(sn).build()); + if (devicesList.isEmpty()) { + return Optional.empty(); + } + DeviceDTO device = devicesList.get(0); + device.setStatus(redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + sn)); + return Optional.of(device); + } + + @Override + public void updateFirmwareVersion(FirmwareVersionReceiver receiver) { + if (receiver.getDomain() == DeviceDomainEnum.SUB_DEVICE) { + this.updateDevice(DeviceDTO.builder() + .deviceSn(receiver.getSn()) + .firmwareVersion(receiver.getFirmwareVersion()) + .build()); + return; + } + payloadService.updateFirmwareVersion(receiver); + } + + /** + * Convert device data transfer object into database entity object. + * @param dto + * @return + */ + private DeviceEntity deviceDTO2Entity(DeviceDTO dto) { + DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder(); + if (dto == null) { + return builder.build(); + } + + return builder.deviceSn(dto.getDeviceSn()) + .userId(dto.getUserId()) + .nickname(dto.getNickname()) + .workspaceId(dto.getWorkspaceId()) + .boundStatus(dto.getBoundStatus()) + .loginTime(dto.getLoginTime() != null ? + dto.getLoginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null) + .boundTime(dto.getBoundTime() != null ? + dto.getBoundTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null) + .childSn(dto.getChildDeviceSn()) + .domain(StringUtils.hasText(dto.getDomain()) ? DeviceDomainEnum.getVal(dto.getDomain()) : null) + .firmwareVersion(dto.getFirmwareVersion()) + .build(); + } + + /** + * Convert device binding data object into database entity object. + * @param receiver + * @return + */ + private Optional bindDevice2Entity(BindDeviceReceiver receiver) { + if (receiver == null) { + return Optional.empty(); + } + int[] droneKey = Arrays.stream(receiver.getDeviceModelKey().split("-")).mapToInt(Integer::parseInt).toArray(); + Optional dictionaryOpt = dictionaryService.getOneDictionaryInfoByTypeSubType(droneKey[1], droneKey[2]); + DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder(); + + dictionaryOpt.ifPresent(entity -> + builder.deviceName(entity.getDeviceName()) + .nickname(entity.getDeviceName()) + .deviceDesc(entity.getDeviceDesc())); + + Optional workspace = workspaceService.getWorkspaceNameByBindCode(receiver.getDeviceBindingCode()); + + DeviceEntity entity = builder + .workspaceId(workspace.isPresent() ? workspace.get().getWorkspaceId() : receiver.getOrganizationId()) + .domain(droneKey[0]) + .deviceType(droneKey[1]) + .subType(droneKey[2]) + .deviceSn(receiver.getSn()) + .boundStatus(true) + .loginTime(System.currentTimeMillis()) + .boundTime(System.currentTimeMillis()) + .urlSelect(IconUrlEnum.SELECT_EQUIPMENT.getUrl()) + .urlNormal(IconUrlEnum.NORMAL_EQUIPMENT.getUrl()) + .build(); + if (StringUtils.hasText(receiver.getDeviceCallsign())) { + entity.setNickname(receiver.getDeviceCallsign()); + } + return Optional.of(entity); + } + + /** + * Convert device data transfer object into device binding status data object. + * @param device + * @return + */ + private BindStatusReceiver dto2BindStatus(DeviceDTO device) { + if (device == null) { + return null; + } + return BindStatusReceiver.builder() + .sn(device.getDeviceSn()) + .deviceCallsign(device.getNickname()) + .isDeviceBindOrganization(device.getBoundStatus()) + .organizationId(device.getWorkspaceId()) + .organizationName(device.getWorkspaceName()) + .build(); + } } \ 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 new file mode 100644 index 0000000..4c34cd5 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java @@ -0,0 +1,50 @@ +package com.dji.sample.manage.service.impl; + +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; +import com.dji.sample.component.websocket.model.BizCodeEnum; +import com.dji.sample.component.websocket.model.CustomWebSocketMessage; +import com.dji.sample.manage.model.dto.DeviceDTO; +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; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +@Service +public class DockOSDServiceImpl extends AbstractTSAService { + + public DockOSDServiceImpl() { + super(null); + } + + @Override + public void pushTelemetryData(Collection sessions, + CustomWebSocketMessage message, Object Object) { + + } + + @Override + public void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, + Collection webSessions, + CustomWebSocketMessage wsMessage) { + + if (DeviceDomainEnum.DOCK.getDesc().equals(device.getDomain())) { + 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/GatewayOSDServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/GatewayOSDServiceImpl.java index a69b9fe..397c80e 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/GatewayOSDServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/GatewayOSDServiceImpl.java @@ -1,23 +1,18 @@ package com.dji.sample.manage.service.impl; -import com.dji.sample.component.mqtt.model.TopicStateReceiver; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; import com.dji.sample.component.websocket.model.BizCodeEnum; import com.dji.sample.component.websocket.model.CustomWebSocketMessage; -import com.dji.sample.manage.model.DeviceStatusManager; +import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.TelemetryDTO; import com.dji.sample.manage.model.dto.TelemetryDeviceDTO; import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.receiver.OsdGatewayReceiver; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; -import java.time.LocalDateTime; import java.util.Collection; /** @@ -49,26 +44,21 @@ public class GatewayOSDServiceImpl extends AbstractTSAService { } @Override - protected void handleOSD(TopicStateReceiver receiver, String sn, String workspaceId, JsonNode hostNode, - Collection webSessions, CustomWebSocketMessage wsMessage) throws JsonProcessingException { - if (sn.equals(receiver.getGateway())) { - // Real-time update of device status in memory - DeviceStatusManager.STATUS_MANAGER.put(DeviceDomainEnum.GATEWAY.getVal() + "/" + sn, - LocalDateTime.now()); - - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + public void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, + Collection webSessions, + CustomWebSocketMessage wsMessage) { + if (DeviceDomainEnum.GATEWAY.getDesc().equals(device.getDomain())) { wsMessage.setBizCode(BizCodeEnum.GATEWAY_OSD.getCode()); - OsdGatewayReceiver data = mapper.treeToValue(hostNode, OsdGatewayReceiver.class); - wsMessage.setData(data); + OsdGatewayReceiver data = mapper.convertValue(receiver.getData(), OsdGatewayReceiver.class); + wsMessage.getData().setHost(data); this.sendMessageService.sendBatch(webSessions, wsMessage); - this.pushTelemetryData(workspaceId, data, sn); + this.pushTelemetryData(device.getWorkspaceId(), data, device.getDeviceSn()); return; } - tsaService.handleOSD(receiver, sn, workspaceId, hostNode, webSessions, wsMessage); + tsaService.handleOSD(receiver, device, 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 371ad97..9ef86b1 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 @@ -2,18 +2,19 @@ package com.dji.sample.manage.service.impl; import com.dji.sample.common.error.LiveErrorEnum; import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.component.mqtt.model.CommonTopicResponse; +import com.dji.sample.component.mqtt.model.ServiceReply; +import com.dji.sample.component.mqtt.model.ServicesMethodEnum; import com.dji.sample.component.mqtt.service.IMessageSenderService; -import com.dji.sample.manage.model.Chan; +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.LiveMethodEnum; import com.dji.sample.manage.model.enums.LiveUrlTypeEnum; import com.dji.sample.manage.model.enums.LiveVideoQualityEnum; import com.dji.sample.manage.model.param.DeviceQueryParam; import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; -import com.dji.sample.manage.model.receiver.ServiceReplyReceiver; +import com.dji.sample.manage.model.receiver.LiveCapacityReceiver; import com.dji.sample.manage.service.ICapacityCameraService; import com.dji.sample.manage.service.IDeviceService; import com.dji.sample.manage.service.ILiveStreamService; @@ -24,9 +25,12 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.lang.reflect.Field; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import static com.dji.sample.component.mqtt.model.TopicConst.*; @@ -51,32 +55,37 @@ public class LiveStreamServiceImpl implements ILiveStreamService { @Autowired private IMessageSenderService messageSender; + @Autowired + private RedisOpsUtils redisOps; + @Override public List getLiveCapacity(String workspaceId) { - // Query all drone data in this workspace. + // Query all devices in this workspace. List devicesList = deviceService.getDevicesByParams( DeviceQueryParam.builder() .workspaceId(workspaceId) - .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) + .domains(List.of(DeviceDomainEnum.SUB_DEVICE.getVal(), DeviceDomainEnum.DOCK.getVal())) .build()); - List capacityDevicesList = new ArrayList<>(); // Query the live capability of each drone. - devicesList.forEach(device -> capacityDevicesList.add(CapacityDeviceDTO.builder() - .name(device.getDeviceName()) - .sn(device.getDeviceSn()) - .camerasList(capacityCameraService.getCapacityCameraByDeviceSn(device.getDeviceSn())) - .build())); - - return capacityDevicesList; + return devicesList.stream() + .filter(device -> redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())) + .map(device -> CapacityDeviceDTO.builder() + .name(device.getDeviceName()) + .sn(device.getDeviceSn()) + .camerasList(capacityCameraService.getCapacityCameraByDeviceSn(device.getDeviceSn())) + .build()) + .collect(Collectors.toList()); } @Override - public Boolean saveLiveCapacity(CapacityDeviceReceiver capacityDeviceReceiver) { - return capacityCameraService.saveCapacityCameraReceiverList( - capacityDeviceReceiver.getCameraList(), - capacityDeviceReceiver.getSn()); + public void saveLiveCapacity(LiveCapacityReceiver liveCapacityReceiver) { + for (CapacityDeviceReceiver capacityDeviceReceiver : liveCapacityReceiver.getDeviceList()) { + capacityCameraService.saveCapacityCameraReceiverList( + capacityDeviceReceiver.getCameraList(), + capacityDeviceReceiver.getSn()); + } } @Override @@ -87,11 +96,11 @@ public class LiveStreamServiceImpl implements ILiveStreamService { return responseResult; } - List data = (List)responseResult.getData(); + DeviceDTO data = (DeviceDTO)responseResult.getData(); // target topic String respTopic = THING_MODEL_PRE + PRODUCT + - data.get(0).getDeviceSn() + SERVICES_SUF; - Optional receiveReplyOpt = this.publishLiveStart(respTopic, liveParam); + data.getDeviceSn() + SERVICES_SUF; + Optional receiveReplyOpt = this.publishLiveStart(respTopic, liveParam); if (receiveReplyOpt.isEmpty()) { return ResponseResult.error(LiveErrorEnum.NO_REPLY); @@ -119,7 +128,7 @@ public class LiveStreamServiceImpl implements ILiveStreamService { .toString()); break; case RTSP: - String url = receiveReplyOpt.get().getInfo(); + String url = receiveReplyOpt.get().getInfo().toString(); this.resolveUrlUser(url, live); break; case UNKNOWN: @@ -131,14 +140,14 @@ public class LiveStreamServiceImpl implements ILiveStreamService { @Override public ResponseResult liveStop(String videoId) { - ResponseResult> responseResult = this.checkBeforeLive(videoId); + ResponseResult responseResult = this.checkBeforeLive(videoId); if (responseResult.getCode() != 0) { return responseResult; } - String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().get(0).getDeviceSn() + SERVICES_SUF; + String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; - Optional receiveReplyOpt = this.publishLiveStop(respTopic, videoId); + Optional receiveReplyOpt = this.publishLiveStop(respTopic, videoId); if (receiveReplyOpt.isEmpty()) { return ResponseResult.error(LiveErrorEnum.NO_REPLY); } @@ -156,15 +165,15 @@ public class LiveStreamServiceImpl implements ILiveStreamService { return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS); } - ResponseResult> responseResult = this.checkBeforeLive(liveParam.getVideoId()); + ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId()); if (responseResult.getCode() != 0) { return responseResult; } - String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().get(0).getDeviceSn() + SERVICES_SUF; + String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; - Optional receiveReplyOpt = this.publishLiveSetQuality(respTopic, liveParam); + Optional receiveReplyOpt = this.publishLiveSetQuality(respTopic, liveParam); if (receiveReplyOpt.isEmpty()) { return ResponseResult.error(LiveErrorEnum.NO_REPLY); } @@ -180,22 +189,31 @@ public class LiveStreamServiceImpl implements ILiveStreamService { * @param videoId * @return */ - private ResponseResult checkBeforeLive(String videoId) { + private ResponseResult checkBeforeLive(String videoId) { String[] videoIdArr = videoId.split("/"); // drone sn / enumeration value of the location where the payload is mounted / payload lens if (videoIdArr.length != 3) { return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS); } + Optional deviceOpt = deviceService.getDeviceBySn(videoIdArr[0]); + // Check if the gateway device connected to this drone exists + if (deviceOpt.isEmpty()) { + return ResponseResult.error(LiveErrorEnum.NO_AIRCRAFT); + } + + if (deviceOpt.get().getDomain().equals(DeviceDomainEnum.DOCK.getDesc())) { + return ResponseResult.success(deviceOpt.get()); + } List gatewayList = deviceService.getDevicesByParams( DeviceQueryParam.builder() .childSn(videoIdArr[0]) .build()); - // Check if the gateway device connected to this drone exists if (gatewayList.isEmpty()) { return ResponseResult.error(LiveErrorEnum.NO_FLIGHT_CONTROL); } - return ResponseResult.success(gatewayList); + + return ResponseResult.success(gatewayList.get(0)); } /** @@ -250,14 +268,14 @@ public class LiveStreamServiceImpl implements ILiveStreamService { * @param liveParam * @return */ - private Optional publishLiveStart(String topic, LiveTypeDTO liveParam) { + private Optional publishLiveStart(String topic, LiveTypeDTO liveParam) { CommonTopicResponse response = new CommonTopicResponse<>(); response.setTid(UUID.randomUUID().toString()); response.setBid(UUID.randomUUID().toString()); response.setData(liveParam); - response.setMethod(LiveMethodEnum.LIVE_START_PUSH.getMethod()); + response.setMethod(ServicesMethodEnum.LIVE_START_PUSH.getMethod()); - return this.publishLive(topic, response); + return messageSender.publishWithReply(topic, response); } /** @@ -266,17 +284,17 @@ public class LiveStreamServiceImpl implements ILiveStreamService { * @param liveParam * @return */ - private Optional publishLiveSetQuality(String respTopic, LiveTypeDTO liveParam) { + private Optional publishLiveSetQuality(String respTopic, LiveTypeDTO liveParam) { Map data = new ConcurrentHashMap<>(Map.of( "video_id", liveParam.getVideoId(), "video_quality", liveParam.getVideoQuality())); CommonTopicResponse> response = new CommonTopicResponse<>(); response.setTid(UUID.randomUUID().toString()); response.setBid(UUID.randomUUID().toString()); - response.setMethod(LiveMethodEnum.LIVE_SET_QUALITY.getMethod()); + response.setMethod(ServicesMethodEnum.LIVE_SET_QUALITY.getMethod()); response.setData(data); - return this.publishLive(respTopic, response); + return messageSender.publishWithReply(respTopic, response); } /** @@ -285,42 +303,15 @@ public class LiveStreamServiceImpl implements ILiveStreamService { * @param videoId * @return */ - private Optional publishLiveStop(String topic, String videoId) { + private Optional publishLiveStop(String topic, String videoId) { Map data = new ConcurrentHashMap<>(Map.of("video_id", videoId)); CommonTopicResponse> response = new CommonTopicResponse<>(); response.setTid(UUID.randomUUID().toString()); response.setBid(UUID.randomUUID().toString()); response.setData(data); - response.setMethod(LiveMethodEnum.LIVE_STOP_PUSH.getMethod()); + response.setMethod(ServicesMethodEnum.LIVE_STOP_PUSH.getMethod()); - return this.publishLive(topic, response); - } - - /** - * Send live streaming start message and receive a response at the same time - * @param topic - * @param response notification of whether the start is successful. - * @return - */ - private Optional publishLive(String topic, CommonTopicResponse response) { - AtomicInteger time = new AtomicInteger(0); - // Retry three times - while (time.getAndIncrement() < 3) { - messageSender.publish(topic, response); - - Chan> chan = Chan.getInstance(); - // If the message is not received in 0.5 seconds then resend it again. - CommonTopicReceiver receiver = chan.get(response.getMethod()); - if (receiver == null) { - continue; - } - // Need to match tid and bid. - if (receiver.getTid().equals(response.getTid()) && - receiver.getBid().equals(response.getBid())) { - return Optional.ofNullable(receiver.getData()); - } - } - return Optional.empty(); + return messageSender.publishWithReply(topic, response); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/service/impl/TopologyServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/TopologyServiceImpl.java index aefc059..64d7d07 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/TopologyServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/TopologyServiceImpl.java @@ -31,7 +31,7 @@ public class TopologyServiceImpl implements ITopologyService { List gatewayList = deviceService.getDevicesByParams( DeviceQueryParam.builder() .workspaceId(workspaceId) - .domain(DeviceDomainEnum.GATEWAY.getVal()) + .domains(List.of(DeviceDomainEnum.GATEWAY.getVal())) .build()); List topologyList = new ArrayList<>(); 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 c3c9a1b..8f3df63 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,15 +1,22 @@ package com.dji.sample.manage.service.impl; import com.auth0.jwt.JWT; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 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.common.model.ResponseResult; import com.dji.sample.common.util.JwtUtil; import com.dji.sample.component.mqtt.config.MqttConfiguration; import com.dji.sample.manage.dao.IUserMapper; import com.dji.sample.manage.model.dto.UserDTO; +import com.dji.sample.manage.model.dto.UserListDTO; import com.dji.sample.manage.model.dto.WorkspaceDTO; import com.dji.sample.manage.model.entity.UserEntity; +import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.service.IUserService; import com.dji.sample.manage.service.IWorkspaceService; import org.springframework.beans.factory.annotation.Autowired; @@ -18,7 +25,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Service @Transactional @@ -51,7 +63,7 @@ public class UserServiceImpl implements IUserService { } @Override - public ResponseResult userLogin(String username, String password) { + public ResponseResult userLogin(String username, String password, Integer flag) { // check user UserEntity userEntity = this.getUserByUsername(username); if (userEntity == null) { @@ -60,6 +72,9 @@ public class UserServiceImpl implements IUserService { .message("invalid username") .build(); } + if (flag.intValue() != userEntity.getUserType().intValue()) { + return ResponseResult.error("The account type does not match."); + } if (!password.equals(userEntity.getPassword())) { return ResponseResult.builder() .code(HttpStatus.UNAUTHORIZED.value()) @@ -67,7 +82,7 @@ public class UserServiceImpl implements IUserService { .build(); } - Optional workspaceOpt = workspaceService.getWorkspaceById(userEntity.getWorkspaceId()); + Optional workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(userEntity.getWorkspaceId()); if (workspaceOpt.isEmpty()) { return ResponseResult.builder() .code(HttpStatus.UNAUTHORIZED.value()) @@ -109,6 +124,60 @@ public class UserServiceImpl implements IUserService { return Optional.of(user); } + @Override + public PaginationData getUsersByWorkspaceId(long page, long pageSize, String workspaceId) { + Page userEntityPage = mapper.selectPage( + new Page<>(page, pageSize), + new LambdaQueryWrapper().eq(UserEntity::getWorkspaceId, workspaceId)); + + List usersList = userEntityPage.getRecords() + .stream() + .map(this::entity2UserListDTO) + .collect(Collectors.toList()); + return new PaginationData<>(usersList, new Pagination(userEntityPage)); + } + + @Override + public Boolean updateUser(String workspaceId, String userId, UserListDTO user) { + UserEntity userEntity = mapper.selectOne( + new LambdaQueryWrapper() + .eq(UserEntity::getUserId, userId) + .eq(UserEntity::getWorkspaceId, workspaceId)); + if (userEntity == null) { + return false; + } + userEntity.setMqttUsername(user.getMqttUsername()); + userEntity.setMqttPassword(user.getMqttPassword()); + userEntity.setUpdateTime(System.currentTimeMillis()); + int id = mapper.update(userEntity, new LambdaUpdateWrapper() + .eq(UserEntity::getUserId, userId) + .eq(UserEntity::getWorkspaceId, workspaceId)); + + return id > 0; + } + + /** + * Convert database entity objects into user data transfer object. + * @param entity + * @return + */ + private UserListDTO entity2UserListDTO(UserEntity entity) { + UserListDTO.UserListDTOBuilder builder = UserListDTO.builder(); + if (entity != null) { + builder.userId(entity.getUserId()) + .username(entity.getUsername()) + .mqttUsername(entity.getMqttUsername()) + .mqttPassword(entity.getMqttPassword()) + .userType(UserTypeEnum.find(entity.getUserType()).getDesc()) + .createTime(LocalDateTime.ofInstant( + Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault())); + Optional workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(entity.getWorkspaceId()); + workspaceOpt.ifPresent(workspace -> builder.workspaceName(workspace.getWorkspaceName())); + } + + return builder.build(); + } + /** * Query a user by username. * @param username @@ -119,6 +188,11 @@ public class UserServiceImpl implements IUserService { .eq("username", username)); } + /** + * Convert database entity objects into user data transfer object. + * @param entity + * @return + */ private UserDTO entityConvertToDTO(UserEntity entity) { if (entity == null) { return new UserDTO(); diff --git a/src/main/java/com/dji/sample/manage/service/impl/WorkspaceServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/WorkspaceServiceImpl.java index d6ed3ab..b90d262 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/WorkspaceServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/WorkspaceServiceImpl.java @@ -1,14 +1,24 @@ package com.dji.sample.manage.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.dji.sample.common.error.CommonErrorEnum; +import com.dji.sample.component.mqtt.model.*; +import com.dji.sample.component.mqtt.service.IMessageSenderService; import com.dji.sample.manage.dao.IWorkspaceMapper; import com.dji.sample.manage.model.dto.WorkspaceDTO; import com.dji.sample.manage.model.entity.WorkspaceEntity; +import com.dji.sample.manage.model.receiver.OrganizationGetReceiver; import com.dji.sample.manage.service.IWorkspaceService; +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 org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; +import java.util.Map; import java.util.Optional; @Service @@ -18,10 +28,11 @@ public class WorkspaceServiceImpl implements IWorkspaceService { @Autowired private IWorkspaceMapper mapper; - @Override - public Optional getWorkspaceById(int id) { - return Optional.ofNullable(entityConvertToDto(mapper.selectById(id))); - } + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private IMessageSenderService messageSenderService; @Override public Optional getWorkspaceByWorkspaceId(String workspaceId) { @@ -31,22 +42,57 @@ public class WorkspaceServiceImpl implements IWorkspaceService { .eq(WorkspaceEntity::getWorkspaceId, workspaceId)))); } + @Override + public Optional getWorkspaceNameByBindCode(String bindCode) { + return Optional.ofNullable(entityConvertToDto( + mapper.selectOne(new LambdaQueryWrapper().eq(WorkspaceEntity::getBindCode, bindCode)))); + } + + @Override + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET, outputChannel = ChannelName.OUTBOUND) + public void replyOrganizationGet(CommonTopicReceiver receiver, MessageHeaders headers) { + OrganizationGetReceiver organizationGet = objectMapper.convertValue(receiver.getData(), OrganizationGetReceiver.class); + CommonTopicResponse.CommonTopicResponseBuilder builder = CommonTopicResponse.builder() + .tid(receiver.getTid()) + .bid(receiver.getBid()) + .method(RequestsMethodEnum.AIRPORT_ORGANIZATION_GET.getMethod()) + .timestamp(System.currentTimeMillis()); + + String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString() + TopicConst._REPLY_SUF; + + if (!StringUtils.hasText(organizationGet.getDeviceBindingCode())) { + builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); + messageSenderService.publish(topic, builder.build()); + return; + } + + Optional workspace = this.getWorkspaceNameByBindCode(organizationGet.getDeviceBindingCode()); + if (workspace.isEmpty()) { + builder.data(RequestsReply.error(CommonErrorEnum.GET_ORGANIZATION_FAILED)); + messageSenderService.publish(topic, builder.build()); + return; + } + + builder.data(RequestsReply.success(Map.of(MapKeyConst.ORGANIZATION_NAME, workspace.get().getWorkspaceName()))); + messageSenderService.publish(topic, builder.build()); + } + /** * Convert database entity objects into workspace data transfer object. * @param entity * @return */ private WorkspaceDTO entityConvertToDto(WorkspaceEntity entity) { - WorkspaceDTO.WorkspaceDTOBuilder builder = WorkspaceDTO.builder(); if (entity == null) { - return builder.build(); + return null; } - return builder + return WorkspaceDTO.builder() .id(entity.getId()) .workspaceId(entity.getWorkspaceId()) .platformName(entity.getPlatformName()) .workspaceDesc(entity.getWorkspaceDesc()) .workspaceName(entity.getWorkspaceName()) + .bindCode(entity.getBindCode()) .build(); } } diff --git a/src/main/java/com/dji/sample/map/controller/WorkspaceElementController.java b/src/main/java/com/dji/sample/map/controller/WorkspaceElementController.java index 1ad52b8..8afaf16 100644 --- a/src/main/java/com/dji/sample/map/controller/WorkspaceElementController.java +++ b/src/main/java/com/dji/sample/map/controller/WorkspaceElementController.java @@ -4,8 +4,8 @@ import com.dji.sample.common.model.CustomClaim; import com.dji.sample.common.model.ResponseResult; import com.dji.sample.component.websocket.model.BizCodeEnum; import com.dji.sample.component.websocket.model.CustomWebSocketMessage; -import com.dji.sample.component.websocket.model.WebSocketManager; import com.dji.sample.component.websocket.service.ISendMessageService; +import com.dji.sample.component.websocket.service.IWebSocketManageService; import com.dji.sample.map.model.dto.*; import com.dji.sample.map.service.IWorkspaceElementService; import org.springframework.beans.factory.annotation.Autowired; @@ -34,6 +34,9 @@ public class WorkspaceElementController { @Autowired private ISendMessageService sendMessageService; + @Autowired + private IWebSocketManageService webSocketManageService; + /** * In the first connection, pilot will send out this http request to obtain the group element list. * Also, if pilot receives a group refresh instruction from WebSocket, @@ -78,7 +81,7 @@ public class WorkspaceElementController { elementService.getElementByElementId(elementCreate.getId()) .ifPresent(groupElement -> sendMessageService.sendBatch( - WebSocketManager.getValueWithWorkspace(workspaceId), + webSocketManageService.getValueWithWorkspace(workspaceId), CustomWebSocketMessage.builder() .timestamp(System.currentTimeMillis()) .bizCode(BizCodeEnum.MAP_ELEMENT_CREATE.getCode()) @@ -114,7 +117,7 @@ public class WorkspaceElementController { elementService.getElementByElementId(elementId) .ifPresent(groupElement -> sendMessageService.sendBatch( - WebSocketManager.getValueWithWorkspace(workspaceId), + webSocketManageService.getValueWithWorkspace(workspaceId), CustomWebSocketMessage.builder() .timestamp(System.currentTimeMillis()) .bizCode(BizCodeEnum.MAP_ELEMENT_UPDATE.getCode()) @@ -142,7 +145,7 @@ public class WorkspaceElementController { if (ResponseResult.CODE_SUCCESS == response.getCode()) { elementOpt.ifPresent(element -> sendMessageService.sendBatch( - WebSocketManager.getValueWithWorkspace(workspaceId), + webSocketManageService.getValueWithWorkspace(workspaceId), CustomWebSocketMessage.builder() .timestamp(System.currentTimeMillis()) .bizCode(BizCodeEnum.MAP_ELEMENT_DELETE.getCode()) @@ -171,7 +174,7 @@ public class WorkspaceElementController { if (ResponseResult.CODE_SUCCESS == response.getCode()) { sendMessageService.sendBatch( - WebSocketManager.getValueWithWorkspace(workspaceId), + webSocketManageService.getValueWithWorkspace(workspaceId), CustomWebSocketMessage.builder() .timestamp(System.currentTimeMillis()) .bizCode(BizCodeEnum.MAP_GROUP_REFRESH.getCode()) diff --git a/src/main/java/com/dji/sample/media/controller/FileController.java b/src/main/java/com/dji/sample/media/controller/FileController.java index 25a6015..8fa8451 100644 --- a/src/main/java/com/dji/sample/media/controller/FileController.java +++ b/src/main/java/com/dji/sample/media/controller/FileController.java @@ -1,15 +1,15 @@ package com.dji.sample.media.controller; +import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.ResponseResult; import com.dji.sample.media.model.MediaFileDTO; import com.dji.sample.media.service.IFileService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; -import java.util.List; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URL; /** * @author sean @@ -29,8 +29,29 @@ public class FileController { * @return */ @GetMapping("/{workspace_id}/files") - public ResponseResult> getFilesList(@PathVariable(name = "workspace_id") String workspaceId) { - List filesList = fileService.getAllFilesByWorkspaceId(workspaceId); + public ResponseResult> getFilesList(@RequestParam(defaultValue = "1") Long page, + @RequestParam(name = "page_size", defaultValue = "10") Long pageSize, + @PathVariable(name = "workspace_id") String workspaceId) { + PaginationData filesList = fileService.getJobsPaginationByWorkspaceId(workspaceId, page, pageSize); return ResponseResult.success(filesList); } + + /** + * Query the download address of the file according to the media file fingerprint, + * and redirect to this address directly for download. + * @param workspaceId + * @param fingerprint + * @param response + */ + @GetMapping("/{workspace_id}/file/{fingerprint}/url") + public void getFileUrl(@PathVariable(name = "workspace_id") String workspaceId, + @PathVariable String fingerprint, HttpServletResponse response) { + + try { + URL url = fileService.getObjectUrl(workspaceId, fingerprint); + response.sendRedirect(url.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/com/dji/sample/media/controller/MediaController.java b/src/main/java/com/dji/sample/media/controller/MediaController.java index 0e73743..b02f8ad 100644 --- a/src/main/java/com/dji/sample/media/controller/MediaController.java +++ b/src/main/java/com/dji/sample/media/controller/MediaController.java @@ -1,8 +1,10 @@ package com.dji.sample.media.controller; import com.dji.sample.common.model.ResponseResult; +import com.dji.sample.component.mqtt.model.MapKeyConst; import com.dji.sample.media.model.FileUploadDTO; import com.dji.sample.media.service.IMediaService; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -10,7 +12,6 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; /** * @author sean @@ -36,7 +37,7 @@ public class MediaController { boolean isExist = mediaService.fastUpload(workspaceId, file.getFingerprint()); - return isExist ? ResponseResult.success() : ResponseResult.error(file.getFingerprint() + "already exists."); + return isExist ? ResponseResult.success() : ResponseResult.error(file.getFingerprint() + "don't exist."); } /** @@ -56,18 +57,17 @@ public class MediaController { /** * Query the files that already exist in this workspace based on the workspace id and the collection of tiny fingerprints. * @param workspaceId - * @param tinyFingerprints + * @param tinyFingerprints There is only one tiny_fingerprint parameter in the body. + * But it is not recommended to use Map to receive the parameter. * @return */ - @GetMapping("/{workspace_id}/files/tiny-fingerprints") - public ResponseResult>> uploadCallback(@PathVariable(name = "workspace_id") String workspaceId, - @RequestParam(value = "tiny_fingerprint") List tinyFingerprints) { - List tinyFingerprintList = mediaService.getAllTinyFingerprintsByWorkspaceId(workspaceId); - List existingList = tinyFingerprints - .stream() - .filter(tinyFingerprintList::contains) - .collect(Collectors.toList()); - return ResponseResult.success(new ConcurrentHashMap<>(Map.of("tiny_fingerprints", existingList))); + @PostMapping("/{workspace_id}/files/tiny-fingerprints") + public ResponseResult>> uploadCallback( + @PathVariable(name = "workspace_id") String workspaceId, + @RequestBody Map> tinyFingerprints) throws JsonProcessingException { + + List existingList = mediaService.getExistTinyFingerprints(workspaceId, tinyFingerprints.get(MapKeyConst.TINY_FINGERPRINTS)); + return ResponseResult.success(new ConcurrentHashMap<>(Map.of(MapKeyConst.TINY_FINGERPRINTS, existingList))); } } \ No newline at end of file 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 2662254..be146bd 100644 --- a/src/main/java/com/dji/sample/media/model/CredentialsDTO.java +++ b/src/main/java/com/dji/sample/media/model/CredentialsDTO.java @@ -2,7 +2,9 @@ package com.dji.sample.media.model; import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse; import io.minio.credentials.Credentials; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; /** @@ -11,6 +13,8 @@ import lombok.Data; * @date 2021/12/7 */ @Data +@NoArgsConstructor +@AllArgsConstructor public class CredentialsDTO { private String accessKeyId; @@ -35,6 +39,10 @@ public class CredentialsDTO { this.expire = Math.toIntExact(expire); } - public CredentialsDTO() { + public CredentialsDTO(com.amazonaws.services.securitytoken.model.Credentials credentials) { + this.accessKeyId = credentials.getAccessKeyId(); + this.accessKeySecret = credentials.getSecretAccessKey(); + this.securityToken = credentials.getSessionToken(); + this.expire = Math.toIntExact((credentials.getExpiration().getTime() - System.currentTimeMillis()) / 1000); } } diff --git a/src/main/java/com/dji/sample/media/model/FileExtensionDTO.java b/src/main/java/com/dji/sample/media/model/FileExtensionDTO.java index 5a33bc6..c1f96b7 100644 --- a/src/main/java/com/dji/sample/media/model/FileExtensionDTO.java +++ b/src/main/java/com/dji/sample/media/model/FileExtensionDTO.java @@ -23,4 +23,6 @@ public class FileExtensionDTO { private String sn; + private String flightId; + } diff --git a/src/main/java/com/dji/sample/media/model/FileUploadCallback.java b/src/main/java/com/dji/sample/media/model/FileUploadCallback.java new file mode 100644 index 0000000..62bbcc7 --- /dev/null +++ b/src/main/java/com/dji/sample/media/model/FileUploadCallback.java @@ -0,0 +1,18 @@ +package com.dji.sample.media.model; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +@Data +public class FileUploadCallback { + + private Integer result; + + private Integer progress; + + private FileUploadDTO file; +} diff --git a/src/main/java/com/dji/sample/media/model/MediaFileDTO.java b/src/main/java/com/dji/sample/media/model/MediaFileDTO.java index b42acf8..8b34e17 100644 --- a/src/main/java/com/dji/sample/media/model/MediaFileDTO.java +++ b/src/main/java/com/dji/sample/media/model/MediaFileDTO.java @@ -5,6 +5,8 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDateTime; + /** * @author sean * @version 0.2 @@ -31,4 +33,8 @@ public class MediaFileDTO { private String payload; private String tinnyFingerprint; + + private String fingerprint; + + private LocalDateTime createTime; } diff --git a/src/main/java/com/dji/sample/media/model/MediaFileEntity.java b/src/main/java/com/dji/sample/media/model/MediaFileEntity.java index a3dd2aa..ab17263 100644 --- a/src/main/java/com/dji/sample/media/model/MediaFileEntity.java +++ b/src/main/java/com/dji/sample/media/model/MediaFileEntity.java @@ -53,6 +53,9 @@ public class MediaFileEntity implements Serializable { @TableField("payload") private String payload; + @TableField("job_id") + private String jobId; + @TableField(value = "create_time", fill = FieldFill.INSERT) private Long createTime; diff --git a/src/main/java/com/dji/sample/media/service/IFileService.java b/src/main/java/com/dji/sample/media/service/IFileService.java index 9347ccf..804f419 100644 --- a/src/main/java/com/dji/sample/media/service/IFileService.java +++ b/src/main/java/com/dji/sample/media/service/IFileService.java @@ -1,8 +1,10 @@ package com.dji.sample.media.service; +import com.dji.sample.common.model.PaginationData; import com.dji.sample.media.model.FileUploadDTO; import com.dji.sample.media.model.MediaFileDTO; +import java.net.URL; import java.util.List; /** @@ -34,4 +36,21 @@ public interface IFileService { * @return */ List getAllFilesByWorkspaceId(String workspaceId); + + /** + * Paginate through all media files in this workspace. + * @param workspaceId + * @param page + * @param pageSize + * @return + */ + PaginationData getJobsPaginationByWorkspaceId(String workspaceId, long page, long pageSize); + + /** + * Get the download address of the file. + * @param workspaceId + * @param fingerprint + * @return + */ + URL getObjectUrl(String workspaceId, String fingerprint); } diff --git a/src/main/java/com/dji/sample/media/service/IMediaService.java b/src/main/java/com/dji/sample/media/service/IMediaService.java index f8fc42d..75085df 100644 --- a/src/main/java/com/dji/sample/media/service/IMediaService.java +++ b/src/main/java/com/dji/sample/media/service/IMediaService.java @@ -1,5 +1,6 @@ package com.dji.sample.media.service; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.media.model.FileUploadDTO; import java.util.List; @@ -33,4 +34,19 @@ public interface IMediaService { * @return */ List getAllTinyFingerprintsByWorkspaceId(String workspaceId); + + /** + * Query the fingerprints that already exist in it based on the incoming tiny fingerprints data. + * @param workspaceId + * @param tinyFingerprints + * @return + */ + List getExistTinyFingerprints(String workspaceId, List tinyFingerprints); + + /** + * Handle media files messages reported by dock. + * @param receiver + * @return + */ + void handleFileUploadCallBack(CommonTopicReceiver receiver); } 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 744d9d7..2037099 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 @@ -1,6 +1,11 @@ package com.dji.sample.media.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.dji.sample.common.model.Pagination; +import com.dji.sample.common.model.PaginationData; +import com.dji.sample.component.oss.model.OssConfiguration; +import com.dji.sample.component.oss.service.impl.OssServiceContext; import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; import com.dji.sample.manage.service.IDeviceDictionaryService; import com.dji.sample.media.dao.IFileMapper; @@ -12,6 +17,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.net.URL; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -26,19 +35,28 @@ import java.util.stream.Collectors; @Transactional public class FileServiceImpl implements IFileService { - @Autowired private IFileMapper mapper; @Autowired private IDeviceDictionaryService deviceDictionaryService; - @Override - public Boolean checkExist(String workspaceId, String fingerprint) { + @Autowired + private OssServiceContext ossService; + + @Autowired + private OssConfiguration configuration; + + private Optional getMediaByFingerprint(String workspaceId, String fingerprint) { MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper() .eq(MediaFileEntity::getWorkspaceId, workspaceId) .eq(MediaFileEntity::getFingerprint, fingerprint)); - return fileEntity != null; + return Optional.ofNullable(fileEntity); + } + + @Override + public Boolean checkExist(String workspaceId, String fingerprint) { + return this.getMediaByFingerprint(workspaceId, fingerprint).isPresent(); } @Override @@ -57,6 +75,30 @@ public class FileServiceImpl implements IFileService { .collect(Collectors.toList()); } + @Override + public PaginationData getJobsPaginationByWorkspaceId(String workspaceId, long page, long pageSize) { + Page pageData = mapper.selectPage( + new Page(page, pageSize), + new LambdaQueryWrapper() + .eq(MediaFileEntity::getWorkspaceId, workspaceId)); + List records = pageData.getRecords() + .stream() + .map(this::entityConvertToDto) + .collect(Collectors.toList()); + + return new PaginationData(records, new Pagination(pageData)); + } + + @Override + public URL getObjectUrl(String workspaceId, String fingerprint) { + Optional mediaFileOpt = getMediaByFingerprint(workspaceId, fingerprint); + if (mediaFileOpt.isEmpty()) { + throw new IllegalArgumentException("{} doesn't exist."); + } + + return ossService.getObjectUrl(configuration.getBucket(), mediaFileOpt.get().getObjectKey()); + } + /** * Convert the received file object into a database entity object. * @param file @@ -72,6 +114,7 @@ public class FileServiceImpl implements IFileService { .objectKey(file.getObjectKey()) .subFileType(file.getSubFileType()) .isOriginal(file.getExt().getIsOriginal()) + .jobId(file.getExt().getFlightId()) .drone(file.getExt().getSn()) .tinnyFingerprint(file.getExt().getTinnyFingerprint()); @@ -81,7 +124,7 @@ public class FileServiceImpl implements IFileService { .mapToInt(Integer::intValue) .toArray(); Optional payloadDict = deviceDictionaryService - .getOneDictionaryInfoByDomainTypeSubType(payloadModel[0], payloadModel[1], payloadModel[2]); + .getOneDictionaryInfoByTypeSubType(payloadModel[1], payloadModel[2]); payloadDict.ifPresent(payload -> builder.payload(payload.getDeviceName())); } return builder.build(); @@ -99,9 +142,12 @@ public class FileServiceImpl implements IFileService { builder.fileName(entity.getFileName()) .filePath(entity.getFilePath()) .isOriginal(entity.getIsOriginal()) + .fingerprint(entity.getFingerprint()) .objectKey(entity.getObjectKey()) .tinnyFingerprint(entity.getTinnyFingerprint()) .payload(entity.getPayload()) + .createTime(LocalDateTime.ofInstant( + Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault())) .drone(entity.getDrone()); } 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 f7758d8..0e92b0b 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 @@ -1,13 +1,22 @@ package com.dji.sample.media.service.impl; +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.media.model.FileUploadCallback; import com.dji.sample.media.model.FileUploadDTO; import com.dji.sample.media.model.MediaFileDTO; import com.dji.sample.media.service.IFileService; import com.dji.sample.media.service.IMediaService; +import com.dji.sample.wayline.model.dto.WaylineJobDTO; +import com.dji.sample.wayline.service.IWaylineJobService; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.annotation.ServiceActivator; import org.springframework.stereotype.Service; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -21,6 +30,15 @@ public class MediaServiceImpl implements IMediaService { @Autowired private IFileService fileService; + @Autowired + private IWaylineJobService waylineJobService; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private IMessageSenderService messageSenderService; + @Override public Boolean fastUpload(String workspaceId, String fingerprint) { return fileService.checkExist(workspaceId, fingerprint); @@ -38,4 +56,42 @@ public class MediaServiceImpl implements IMediaService { .map(MediaFileDTO::getTinnyFingerprint) .collect(Collectors.toList()); } + + @Override + public List getExistTinyFingerprints(String workspaceId, List tinyFingerprints) { + List tinyFingerprintList = this.getAllTinyFingerprintsByWorkspaceId(workspaceId); + return tinyFingerprints + .stream() + .filter(tinyFingerprintList::contains) + .collect(Collectors.toList()); + + } + + @Override + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, outputChannel = ChannelName.OUTBOUND) + public void handleFileUploadCallBack(CommonTopicReceiver receiver) { + FileUploadCallback callback = objectMapper.convertValue(receiver.getData(), FileUploadCallback.class); + + String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + receiver.getGateway() + + TopicConst.EVENTS_SUF + TopicConst._REPLY_SUF; + CommonTopicResponse data = CommonTopicResponse.builder() + .timestamp(System.currentTimeMillis()) + .method(EventsMethodEnum.FILE_UPLOAD_CALLBACK.getMethod()) + .data(ResponseResult.success()) + .tid(receiver.getTid()) + .bid(receiver.getBid()) + .build(); + if (callback.getResult() == ResponseResult.CODE_SUCCESS) { + String jobId = callback.getFile().getExt().getFlightId(); + Optional jobOpt = waylineJobService.getJobByJobId(jobId); + if (jobOpt.isPresent()) { + int id = fileService.saveFile(jobOpt.get().getWorkspaceId(), callback.getFile()); + if (id <= 0) { + data.setData(ResponseResult.error()); + } + } + } + + messageSenderService.publish(topic, data); + } } diff --git a/src/main/java/com/dji/sample/storage/controller/StorageController.java b/src/main/java/com/dji/sample/storage/controller/StorageController.java index 7c40607..01147e7 100644 --- a/src/main/java/com/dji/sample/storage/controller/StorageController.java +++ b/src/main/java/com/dji/sample/storage/controller/StorageController.java @@ -1,13 +1,8 @@ package com.dji.sample.storage.controller; import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.oss.model.AliyunOSSConfiguration; -import com.dji.sample.component.oss.model.MinIOConfiguration; import com.dji.sample.media.model.StsCredentialsDTO; import com.dji.sample.storage.service.IStorageService; -import com.dji.sample.storage.service.impl.AliyunStorageServiceImpl; -import com.dji.sample.storage.service.impl.MinIOStorageServiceImpl; -import 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; @@ -21,24 +16,11 @@ import org.springframework.web.bind.annotation.RestController; */ @RestController @RequestMapping("${url.storage.prefix}${url.storage.version}/workspaces/") -@Slf4j public class StorageController { + @Autowired private IStorageService storageService; - @Autowired - private void setOssService(@Autowired(required = false) AliyunStorageServiceImpl aliyunStorageService, - @Autowired(required = false) MinIOStorageServiceImpl minIOStorageService) { - if (AliyunOSSConfiguration.enable) { - this.storageService = aliyunStorageService; - return; - } - if (MinIOConfiguration.enable) { - this.storageService = minIOStorageService; - return; - } - log.error("storageService is null."); - } /** * Get temporary credentials for uploading the media and wayline in DJI Pilot. * @param workspaceId @@ -50,4 +32,5 @@ public class StorageController { StsCredentialsDTO stsCredentials = storageService.getSTSCredentials(); return ResponseResult.success(stsCredentials); } + } diff --git a/src/main/java/com/dji/sample/storage/service/IStorageService.java b/src/main/java/com/dji/sample/storage/service/IStorageService.java index 0dc98c9..9bca0ef 100644 --- a/src/main/java/com/dji/sample/storage/service/IStorageService.java +++ b/src/main/java/com/dji/sample/storage/service/IStorageService.java @@ -1,6 +1,8 @@ package com.dji.sample.storage.service; +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.media.model.StsCredentialsDTO; +import org.springframework.messaging.MessageHeaders; /** * @author sean @@ -14,4 +16,11 @@ public interface IStorageService { * @return temporary credentials object */ StsCredentialsDTO getSTSCredentials(); + + /** + * Handles requests from the dock to obtain temporary credentials. + * @param receiver + * @param headers + */ + void replyConfigGet(CommonTopicReceiver receiver, MessageHeaders headers); } diff --git a/src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java b/src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java new file mode 100644 index 0000000..e47db9c --- /dev/null +++ b/src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java @@ -0,0 +1,56 @@ +package com.dji.sample.storage.service.impl; + +import com.dji.sample.component.mqtt.model.*; +import com.dji.sample.component.mqtt.service.IMessageSenderService; +import com.dji.sample.component.oss.model.OssConfiguration; +import com.dji.sample.component.oss.service.impl.OssServiceContext; +import com.dji.sample.media.model.StsCredentialsDTO; +import com.dji.sample.storage.service.IStorageService; +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; + +/** + * @author sean + * @version 0.3 + * @date 2022/3/9 + */ +@Service +public class StorageServiceImpl implements IStorageService { + + @Autowired + private IMessageSenderService messageSender; + + @Autowired + private OssServiceContext ossService; + + @Autowired + private OssConfiguration configuration; + + @Override + public StsCredentialsDTO getSTSCredentials() { + return StsCredentialsDTO.builder() + .endpoint(configuration.getEndpoint()) + .bucket(configuration.getBucket()) + .credentials(ossService.getCredentials()) + .provider(configuration.getProvider()) + .objectKeyPrefix(configuration.getObjectDirPrefix()) + .region(configuration.getRegion()) + .build(); + } + + @Override + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET, outputChannel = ChannelName.OUTBOUND) + public void replyConfigGet(CommonTopicReceiver receiver, MessageHeaders headers) { + CommonTopicResponse response = CommonTopicResponse.builder() + .tid(receiver.getTid()) + .bid(receiver.getBid()) + .data(RequestsReply.success(this.getSTSCredentials())) + .timestamp(System.currentTimeMillis()) + .method(receiver.getMethod()) + .build(); + messageSender.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF, response); + } +} 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 5af35a3..93390e4 100644 --- a/src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java +++ b/src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java @@ -3,10 +3,9 @@ package com.dji.sample.wayline.controller; import com.dji.sample.common.model.CustomClaim; import com.dji.sample.common.model.PaginationData; import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.oss.model.AliyunOSSConfiguration; -import com.dji.sample.wayline.model.WaylineFileDTO; -import com.dji.sample.wayline.model.WaylineFileUploadDTO; -import com.dji.sample.wayline.model.WaylineQueryParam; +import com.dji.sample.wayline.model.dto.WaylineFileDTO; +import com.dji.sample.wayline.model.dto.WaylineFileUploadDTO; +import com.dji.sample.wayline.model.param.WaylineQueryParam; import com.dji.sample.wayline.service.IWaylineFileService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -15,6 +14,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URL; +import java.sql.SQLException; import java.util.List; import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; @@ -70,12 +70,10 @@ public class WaylineFileController { public void getFileUrl(@PathVariable(name = "workspace_id") String workspaceId, @PathVariable(name = "wayline_id") String waylineId, HttpServletResponse response) { - WaylineFileDTO wayline = waylineFileService.getWaylineByWaylineId(workspaceId, waylineId); - URL url = waylineFileService.getObjectUrl(AliyunOSSConfiguration.bucket, wayline.getObjectKey()); - try { + URL url = waylineFileService.getObjectUrl(workspaceId, waylineId); response.sendRedirect(url.toString()); - } catch (Exception e) { + } catch (IOException | SQLException e) { e.printStackTrace(); } } @@ -147,4 +145,17 @@ public class WaylineFileController { return ResponseResult.success(existNamesList); } + + /** + * Delete the wayline file in the workspace according to the wayline id. + * @param workspaceId + * @param waylineId + * @return + */ + @DeleteMapping("/{workspace_id}/waylines/{wayline_id}") + public ResponseResult deleteWayline(@PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "wayline_id") String waylineId) { + boolean isDel = waylineFileService.deleteByWaylineId(workspaceId, waylineId); + return isDel ? ResponseResult.success() : ResponseResult.error("Failed to delete wayline."); + } } diff --git a/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java b/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java new file mode 100644 index 0000000..f066f54 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java @@ -0,0 +1,74 @@ +package com.dji.sample.wayline.controller; + +import com.dji.sample.common.model.CustomClaim; +import com.dji.sample.common.model.PaginationData; +import com.dji.sample.common.model.ResponseResult; +import com.dji.sample.wayline.model.dto.WaylineJobDTO; +import com.dji.sample.wayline.model.param.CreateJobParam; +import com.dji.sample.wayline.service.IWaylineJobService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.sql.SQLException; + +import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@RequestMapping("${url.wayline.prefix}${url.wayline.version}/workspaces") +@RestController +public class WaylineJobController { + + @Autowired + private IWaylineJobService waylineJobService; + + /** + * Create a wayline task for the Dock. + * @param request + * @param param + * @param workspaceId + * @return + * @throws SQLException + */ + @PostMapping("/{workspace_id}/flight-tasks") + public ResponseResult createJob(HttpServletRequest request, @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(); + } + + /** + * Paginate through all jobs in this workspace. + * @param page + * @param pageSize + * @param workspaceId + * @return + */ + @GetMapping("/{workspace_id}/jobs") + public ResponseResult> getJobs(@RequestParam(defaultValue = "1") Long page, + @RequestParam(name = "page_size", defaultValue = "10") Long pageSize, + @PathVariable(name = "workspace_id") String workspaceId) { + PaginationData data = waylineJobService.getJobsByWorkspaceId(workspaceId, page, pageSize); + return ResponseResult.success(data); + } + + /** + * Issue wayline mission to the dock for execution. + * @param jobId + * @param workspaceId + * @return + * @throws SQLException + */ + @PostMapping("/{workspace_id}/jobs/{job_id}") + public ResponseResult publishJob(@PathVariable(name = "job_id") String jobId, + @PathVariable(name = "workspace_id") String workspaceId) throws SQLException { + waylineJobService.publishFlightTask(workspaceId, jobId); + return ResponseResult.success(); + } +} diff --git a/src/main/java/com/dji/sample/wayline/dao/IWaylineFileMapper.java b/src/main/java/com/dji/sample/wayline/dao/IWaylineFileMapper.java index 10a0520..5537e4f 100644 --- a/src/main/java/com/dji/sample/wayline/dao/IWaylineFileMapper.java +++ b/src/main/java/com/dji/sample/wayline/dao/IWaylineFileMapper.java @@ -1,7 +1,7 @@ package com.dji.sample.wayline.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.dji.sample.wayline.model.WaylineFileEntity; +import com.dji.sample.wayline.model.entity.WaylineFileEntity; /** * @author sean diff --git a/src/main/java/com/dji/sample/wayline/dao/IWaylineJobMapper.java b/src/main/java/com/dji/sample/wayline/dao/IWaylineJobMapper.java new file mode 100644 index 0000000..9a18c69 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/dao/IWaylineJobMapper.java @@ -0,0 +1,12 @@ +package com.dji.sample.wayline.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.dji.sample.wayline.model.entity.WaylineJobEntity; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +public interface IWaylineJobMapper extends BaseMapper { +} diff --git a/src/main/java/com/dji/sample/wayline/model/dto/FLightTaskProgress.java b/src/main/java/com/dji/sample/wayline/model/dto/FLightTaskProgress.java new file mode 100644 index 0000000..e8e8794 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/dto/FLightTaskProgress.java @@ -0,0 +1,16 @@ +package com.dji.sample.wayline.model.dto; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +@Data +public class FLightTaskProgress { + + private Integer currentStep; + + private Integer percent; +} 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 new file mode 100644 index 0000000..736a8c5 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java @@ -0,0 +1,24 @@ +package com.dji.sample.wayline.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FlightTaskCreateDTO { + + private String flightId; + + private String type; + + private FlightTaskFileDTO file; +} 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 new file mode 100644 index 0000000..ca0accb --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java @@ -0,0 +1,22 @@ +package com.dji.sample.wayline.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FlightTaskFileDTO { + + private String url; + + private String sign; +} diff --git a/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressExt.java b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressExt.java new file mode 100644 index 0000000..c75ef1e --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressExt.java @@ -0,0 +1,16 @@ +package com.dji.sample.wayline.model.dto; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +@Data +public class FlightTaskProgressExt { + + private Integer currentWaypointIndex; + + private Integer mediaCount; +} diff --git a/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressReceiver.java b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressReceiver.java new file mode 100644 index 0000000..d2f7c7c --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressReceiver.java @@ -0,0 +1,19 @@ +package com.dji.sample.wayline.model.dto; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +@Data +public class FlightTaskProgressReceiver { + + private FlightTaskProgressExt ext; + + private FLightTaskProgress progress; + + private String status; + +} diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineFileDTO.java b/src/main/java/com/dji/sample/wayline/model/dto/WaylineFileDTO.java new file mode 100644 index 0000000..8e6282a --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/dto/WaylineFileDTO.java @@ -0,0 +1,43 @@ +package com.dji.sample.wayline.model.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author sean + * @version 0.3 + * @date 2021/12/22 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class WaylineFileDTO { + + private String name; + + @JsonProperty("id") + private String waylineId; + + private String droneModelKey; + + private String sign; + + private List payloadModelKeys; + + private Boolean favorited; + + private List templateTypes; + + private String objectKey; + + @JsonProperty("user_name") + private String username; + + private Long updateTime; +} diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineFileUploadDTO.java b/src/main/java/com/dji/sample/wayline/model/dto/WaylineFileUploadDTO.java new file mode 100644 index 0000000..61f7d01 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/dto/WaylineFileUploadDTO.java @@ -0,0 +1,18 @@ +package com.dji.sample.wayline.model.dto; + +import lombok.Data; + +/** + * @author sean + * @version 0.3 + * @date 2021/12/23 + */ +@Data +public class WaylineFileUploadDTO { + + private String objectKey; + + private String name; + + private WaylineFileDTO metadata; +} 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 new file mode 100644 index 0000000..a8820a7 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java @@ -0,0 +1,43 @@ +package com.dji.sample.wayline.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class WaylineJobDTO { + + private String jobId; + + private String jobName; + + private String fileId; + + private String fileName; + + private String dockSn; + + private String dockName; + + private String workspaceId; + + private String bid; + + private String type; + + private String username; + + private LocalDateTime updateTime; + +} diff --git a/src/main/java/com/dji/sample/wayline/model/entity/WaylineFileEntity.java b/src/main/java/com/dji/sample/wayline/model/entity/WaylineFileEntity.java new file mode 100644 index 0000000..27be0bf --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/entity/WaylineFileEntity.java @@ -0,0 +1,62 @@ +package com.dji.sample.wayline.model.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author sean + * @version 0.3 + * @date 2021/12/22 + */ +@Data +@TableName("wayline_file") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class WaylineFileEntity implements Serializable { + + @TableId(type = IdType.AUTO) + private Integer id; + + @TableField("name") + private String name; + + @TableField("wayline_id") + private String waylineId; + + @TableField("drone_model_key") + private String droneModelKey; + + @TableField("payload_model_keys") + private String payloadModelKeys; + + @TableField("sign") + private String sign; + + @TableField("workspace_id") + private String workspaceId; + + @TableField("favorited") + private Boolean favorited; + + @TableField("template_types") + private String templateTypes; + + @TableField("object_key") + private String objectKey; + + @TableField("user_name") + private String username; + + @TableField(value = "create_time", fill = FieldFill.INSERT) + private Long createTime; + + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) + private Long updateTime; + +} 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 new file mode 100644 index 0000000..f2106f6 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/entity/WaylineJobEntity.java @@ -0,0 +1,56 @@ +package com.dji.sample.wayline.model.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("wayline_job") +public class WaylineJobEntity implements Serializable { + + @TableId(type = IdType.AUTO) + private Integer id; + + @TableField("job_id") + private String jobId; + + @TableField("name") + private String name; + + @TableField("file_id") + private String fileId; + + @TableField("dock_sn") + private String dockSn; + + @TableField("workspace_id") + private String workspaceId; + + @TableField("bid") + private String bid; + + @TableField("type") + private String type; + + @TableField("username") + private String username; + + @TableField(value = "create_time", fill = FieldFill.INSERT) + private Long createTime; + + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) + private Long updateTime; + +} 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 new file mode 100644 index 0000000..a186838 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java @@ -0,0 +1,22 @@ +package com.dji.sample.wayline.model.param; + +import lombok.Data; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Data +public class CreateJobParam { + + private String name; + + private String fileId; + + private String dockSn; + + private String type; + + private boolean immediate; +} diff --git a/src/main/java/com/dji/sample/wayline/model/param/WaylineQueryParam.java b/src/main/java/com/dji/sample/wayline/model/param/WaylineQueryParam.java new file mode 100644 index 0000000..e0c39ce --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/model/param/WaylineQueryParam.java @@ -0,0 +1,30 @@ +package com.dji.sample.wayline.model.param; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author sean + * @version 0.3 + * @date 2021/12/22 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class WaylineQueryParam { + + private boolean favorited; + + @Builder.Default + private int page = 1; + + @Builder.Default + private int pageSize = 10; + + private String orderBy; + + private Integer[] templateType; +} diff --git a/src/main/java/com/dji/sample/wayline/service/IFlightTaskService.java b/src/main/java/com/dji/sample/wayline/service/IFlightTaskService.java new file mode 100644 index 0000000..8bc6e63 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/service/IFlightTaskService.java @@ -0,0 +1,18 @@ +package com.dji.sample.wayline.service; + +import com.dji.sample.component.mqtt.model.CommonTopicReceiver; +import org.springframework.messaging.MessageHeaders; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +public interface IFlightTaskService { + + /** + * Handle the progress messages of the flight tasks reported by the dock. + * @param receiver + */ + void handleProgress(CommonTopicReceiver receiver, MessageHeaders headers); +} 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 941890d..83f1820 100644 --- a/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java +++ b/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java @@ -1,11 +1,13 @@ package com.dji.sample.wayline.service; import com.dji.sample.common.model.PaginationData; -import com.dji.sample.wayline.model.WaylineFileDTO; -import com.dji.sample.wayline.model.WaylineQueryParam; +import com.dji.sample.wayline.model.dto.WaylineFileDTO; +import com.dji.sample.wayline.model.param.WaylineQueryParam; import java.net.URL; +import java.sql.SQLException; import java.util.List; +import java.util.Optional; /** * @author sean @@ -28,15 +30,15 @@ public interface IWaylineFileService { * @param waylineId * @return */ - WaylineFileDTO getWaylineByWaylineId(String workspaceId, String waylineId); + Optional getWaylineByWaylineId(String workspaceId, String waylineId); /** * Get the download address of the file object. - * @param bucket bucket name - * @param objectKey object name + * @param workspaceId + * @param waylineId * @return */ - URL getObjectUrl(String bucket, String objectKey); + URL getObjectUrl(String workspaceId, String waylineId) throws SQLException; /** * Save the basic information of the wayline file. @@ -62,4 +64,11 @@ public interface IWaylineFileService { * @return */ List getDuplicateNames(String workspaceId, List names); + + /** + * Delete the wayline file based on the wayline id. + * @param workspaceId + * @param waylineId + */ + Boolean deleteByWaylineId(String workspaceId, String waylineId); } diff --git a/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java b/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java new file mode 100644 index 0000000..4dc78d9 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java @@ -0,0 +1,56 @@ +package com.dji.sample.wayline.service; + +import com.dji.sample.common.model.CustomClaim; +import com.dji.sample.common.model.PaginationData; +import com.dji.sample.wayline.model.dto.WaylineJobDTO; +import com.dji.sample.wayline.model.param.CreateJobParam; + +import java.sql.SQLException; +import java.util.Optional; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +public interface IWaylineJobService { + + /** + * Create a wayline mission for the dock. + * @param param + * @param customClaim user info + * @return + */ + Boolean createJob(CreateJobParam param, CustomClaim customClaim) throws SQLException; + + /** + * Issue wayline mission to the dock for execution. + * @param workspaceId + * @param jobId + * @return + */ + void publishFlightTask(String workspaceId, String jobId) throws SQLException; + + /** + * Query job information based on job id. + * @param jobId + * @return job information + */ + Optional getJobByJobId(String jobId); + + /** + * Update job data. + * @param dto + * @return + */ + Boolean updateJob(WaylineJobDTO dto); + + /** + * Paginate through all jobs in this workspace. + * @param workspaceId + * @param page + * @param pageSize + * @return + */ + PaginationData getJobsByWorkspaceId(String workspaceId, long page, long pageSize); +} 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 new file mode 100644 index 0000000..46774bb --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java @@ -0,0 +1,84 @@ +package com.dji.sample.wayline.service.impl; + +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.component.websocket.model.BizCodeEnum; +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.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.service.IFlightTaskService; +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; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +@Service +@Slf4j +public class FlightTaskServiceImpl implements IFlightTaskService { + + @Autowired + private IMessageSenderService messageSender; + + @Autowired + private ObjectMapper mapper; + + @Autowired + private ISendMessageService websocketMessageService; + + @Autowired + private IWebSocketManageService webSocketManageService; + + @Autowired + private RedisOpsUtils redisOps; + + @Override + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS, outputChannel = ChannelName.OUTBOUND) + public void handleProgress(CommonTopicReceiver receiver, MessageHeaders headers) { + EventsReceiver eventsReceiver = mapper.convertValue(receiver.getData(), + new TypeReference>(){}); + eventsReceiver.setBid(receiver.getBid()); + + log.info("Task progress: " + eventsReceiver.getOutput().getProgress().toString()); + + if (eventsReceiver.getResult() != ResponseResult.CODE_SUCCESS) { + log.error("Error code: " + eventsReceiver.getResult()); + } + + DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); + websocketMessageService.sendBatch( + webSocketManageService.getValueWithWorkspaceAndUserType( + device.getWorkspaceId(), UserTypeEnum.WEB.getVal()), + CustomWebSocketMessage.builder() + .data(eventsReceiver) + .timestamp(System.currentTimeMillis()) + .bizCode(BizCodeEnum.FLIGHT_TASK_PROGRESS.getCode()) + .build()); + + if (receiver.getNeedReply() == 1) { + String topic = headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF; + messageSender.publish(topic, + CommonTopicResponse.builder() + .tid(receiver.getTid()) + .bid(receiver.getBid()) + .method(EventsMethodEnum.FLIGHT_TASK_PROGRESS.getMethod()) + .timestamp(System.currentTimeMillis()) + .data(ResponseResult.success()) + .build()); + } + } +} 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 3d25fb6..f12979a 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 @@ -5,25 +5,24 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.dji.sample.common.model.Pagination; import com.dji.sample.common.model.PaginationData; -import com.dji.sample.component.oss.model.AliyunOSSConfiguration; -import com.dji.sample.component.oss.model.MinIOConfiguration; -import com.dji.sample.component.oss.service.IOssService; -import com.dji.sample.component.oss.service.impl.AliyunOssServiceImpl; -import com.dji.sample.component.oss.service.impl.MinIOServiceImpl; +import com.dji.sample.component.oss.model.OssConfiguration; +import com.dji.sample.component.oss.service.impl.OssServiceContext; import com.dji.sample.wayline.dao.IWaylineFileMapper; -import com.dji.sample.wayline.model.WaylineFileDTO; -import com.dji.sample.wayline.model.WaylineFileEntity; -import com.dji.sample.wayline.model.WaylineQueryParam; +import com.dji.sample.wayline.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 lombok.extern.slf4j.Slf4j; 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 java.net.URL; +import java.sql.SQLException; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -34,27 +33,16 @@ import java.util.stream.Collectors; */ @Service @Transactional -@Slf4j public class WaylineFileServiceImpl implements IWaylineFileService { @Autowired private IWaylineFileMapper mapper; - private IOssService ossService; + @Autowired + private OssServiceContext ossService; @Autowired - private void setOssService(@Autowired(required = false) AliyunOssServiceImpl aliyunOssService, - @Autowired(required = false) MinIOServiceImpl minIOService) { - if (AliyunOSSConfiguration.enable) { - this.ossService = aliyunOssService; - return; - } - if (MinIOConfiguration.enable) { - this.ossService = minIOService; - return; - } - log.error("ossService is null."); - } + private OssConfiguration configuration; @Override public PaginationData getWaylinesByParam(String workspaceId, WaylineQueryParam param) { @@ -82,17 +70,22 @@ public class WaylineFileServiceImpl implements IWaylineFileService { } @Override - public WaylineFileDTO getWaylineByWaylineId(String workspaceId, String waylineId) { - return this.entityConvertToDTO( - mapper.selectOne( - new LambdaQueryWrapper() - .eq(WaylineFileEntity::getWorkspaceId, workspaceId) - .eq(WaylineFileEntity::getWaylineId, waylineId))); + public Optional getWaylineByWaylineId(String workspaceId, String waylineId) { + return Optional.ofNullable( + this.entityConvertToDTO( + mapper.selectOne( + new LambdaQueryWrapper() + .eq(WaylineFileEntity::getWorkspaceId, workspaceId) + .eq(WaylineFileEntity::getWaylineId, waylineId)))); } @Override - public URL getObjectUrl(String bucket, String objectKey) { - return ossService.getObjectUrl(bucket, objectKey); + public URL getObjectUrl(String workspaceId, String waylineId) throws SQLException { + Optional waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId); + if (waylineOpt.isEmpty()) { + throw new SQLException(waylineId + " does not exist."); + } + return ossService.getObjectUrl(configuration.getBucket(), waylineOpt.get().getObjectKey()); } @Override @@ -101,6 +94,13 @@ public class WaylineFileServiceImpl implements IWaylineFileService { 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() + "]."); + } + + file.setSign(DigestUtils.md5DigestAsHex(object)); int insertId = mapper.insert(file); return insertId > 0 ? file.getId() : insertId; } @@ -129,30 +129,48 @@ public class WaylineFileServiceImpl implements IWaylineFileService { .collect(Collectors.toList()); } + @Override + public Boolean deleteByWaylineId(String workspaceId, String waylineId) { + Optional waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId); + if (waylineOpt.isEmpty()) { + return true; + } + WaylineFileDTO wayline = waylineOpt.get(); + boolean isDel = mapper.delete(new LambdaUpdateWrapper() + .eq(WaylineFileEntity::getWorkspaceId, workspaceId) + .eq(WaylineFileEntity::getWaylineId, waylineId)) + > 0; + if (!isDel) { + return false; + } + return ossService.deleteObject(configuration.getBucket(), wayline.getObjectKey()); + } + /** * Convert database entity objects into wayline data transfer object. * @param entity * @return */ private WaylineFileDTO entityConvertToDTO(WaylineFileEntity entity) { - WaylineFileDTO.WaylineFileDTOBuilder builder = WaylineFileDTO.builder(); - - if (entity != null) { - builder.droneModelKey(entity.getDroneModelKey()) - .favorited(entity.getFavorited()) - .name(entity.getName()) - .payloadModelKeys(entity.getPayloadModelKeys() != null ? - Arrays.asList(entity.getPayloadModelKeys().split(",")) : null) - .templateTypes(Arrays.stream(entity.getTemplateTypes().split(",")) - .map(Integer::parseInt) - .collect(Collectors.toList())) - .username(entity.getUsername()) - .objectKey(entity.getObjectKey()) - .updateTime(entity.getUpdateTime()) - .waylineId(entity.getWaylineId()); + if (entity == null) { + return null; } + return WaylineFileDTO.builder() + .droneModelKey(entity.getDroneModelKey()) + .favorited(entity.getFavorited()) + .name(entity.getName()) + .payloadModelKeys(entity.getPayloadModelKeys() != null ? + Arrays.asList(entity.getPayloadModelKeys().split(",")) : null) + .templateTypes(Arrays.stream(entity.getTemplateTypes().split(",")) + .map(Integer::parseInt) + .collect(Collectors.toList())) + .username(entity.getUsername()) + .objectKey(entity.getObjectKey()) + .sign(entity.getSign()) + .updateTime(entity.getUpdateTime()) + .waylineId(entity.getWaylineId()) + .build(); - return builder.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 new file mode 100644 index 0000000..9a2336c --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java @@ -0,0 +1,212 @@ +package com.dji.sample.wayline.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.dji.sample.common.model.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.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.entity.WaylineJobEntity; +import com.dji.sample.wayline.model.param.CreateJobParam; +import com.dji.sample.wayline.service.IWaylineFileService; +import com.dji.sample.wayline.service.IWaylineJobService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +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.stream.Collectors; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Service +@Transactional +@Slf4j +public class WaylineJobServiceImpl implements IWaylineJobService { + + @Autowired + private IWaylineJobMapper mapper; + + @Autowired + private IWaylineFileService waylineFileService; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IMessageSenderService messageSender; + + @Autowired + private RedisOpsUtils redisOps; + + @Override + public Boolean createJob(CreateJobParam param, CustomClaim customClaim) throws SQLException { + if (param == null) { + return false; + } + WaylineJobEntity jobEntity = WaylineJobEntity.builder() + .name(param.getName()) + .dockSn(param.getDockSn()) + .fileId(param.getFileId()) + .username(customClaim.getUsername()) + .workspaceId(customClaim.getWorkspaceId()) + .jobId(UUID.randomUUID().toString()) + .type(param.getType()) + .build(); + int id = mapper.insert(jobEntity); + if (id <= 0) { + return false; + } + if (param.isImmediate()) { + publishFlightTask(jobEntity.getWorkspaceId(), jobEntity.getJobId()); + } + return true; + } + + @Override + public void publishFlightTask(String workspaceId, String jobId) throws SQLException { + // get job + Optional 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) { + throw new RuntimeException("Dock is offline."); + } + + // get wayline file + Optional 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(); + + String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + + job.getDockSn() + TopicConst.SERVICES_SUF; + CommonTopicResponse response = CommonTopicResponse.builder() + .tid(UUID.randomUUID().toString()) + .bid(UUID.randomUUID().toString()) + .timestamp(System.currentTimeMillis()) + .data(flightTask) + .method(ServicesMethodEnum.FLIGHTTASK_CREATE.getMethod()) + .build(); + + Optional 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()); + } + + job.setBid(response.getBid()); + boolean isUpd = this.updateJob(job); + if (!isUpd) { + throw new SQLException("Failed to update data."); + } + } + + @Override + public Optional getJobByJobId(String jobId) { + WaylineJobEntity jobEntity = mapper.selectOne( + new LambdaQueryWrapper() + .eq(WaylineJobEntity::getJobId, jobId)); + return Optional.ofNullable(entity2Dto(jobEntity)); + } + + @Override + public Boolean updateJob(WaylineJobDTO dto) { + return mapper.update(this.dto2Entity(dto), + new LambdaUpdateWrapper() + .eq(WaylineJobEntity::getWorkspaceId, dto.getWorkspaceId()) + .eq(WaylineJobEntity::getJobId, dto.getJobId())) + > 0; + } + + @Override + public PaginationData getJobsByWorkspaceId(String workspaceId, long page, long pageSize) { + Page pageData = mapper.selectPage( + new Page(page, pageSize), + new LambdaQueryWrapper() + .eq(WaylineJobEntity::getWorkspaceId, workspaceId)); + List records = pageData.getRecords() + .stream() + .map(this::entity2Dto) + .collect(Collectors.toList()); + + return new PaginationData(records, new Pagination(pageData)); + } + + private WaylineJobEntity dto2Entity(WaylineJobDTO dto) { + WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder(); + if (dto == null) { + return builder.build(); + } + return builder.type(dto.getType()) + .bid(dto.getBid()) + .name(dto.getJobName()) + .build(); + } + + private WaylineJobDTO entity2Dto(WaylineJobEntity entity) { + if (entity == null) { + return null; + } + return WaylineJobDTO.builder() + .jobId(entity.getJobId()) + .bid(entity.getBid()) + .updateTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getUpdateTime()), ZoneId.systemDefault())) + .jobName(entity.getName()) + .fileId(entity.getFileId()) + .fileName(waylineFileService.getWaylineByWaylineId(entity.getWorkspaceId(), entity.getFileId()) + .orElse(WaylineFileDTO.builder().build()).getName()) + .dockSn(entity.getDockSn()) + .dockName(deviceService.getDeviceBySn(entity.getDockSn()) + .orElse(DeviceDTO.builder().build()).getNickname()) + .username(entity.getUsername()) + .workspaceId(entity.getWorkspaceId()) + .type(entity.getType()) + .build(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8c95358..5d93be2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,7 +7,7 @@ spring: druid: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3306/cloud_sample?useSSL=false&allowPublicKeyRetrieval=true + url: jdbc:mysql://cloud_api_sample_mysql:3306/cloud_sample?useSSL=false&allowPublicKeyRetrieval=true username: root password: root initial-size: 10 @@ -15,12 +15,18 @@ spring: max-active: 20 max-wait: 60000 - jackson: - property-naming-strategy: SNAKE_CASE - date-format: yyyy-MM-dd HH:mm:ss - default-property-inclusion: NON_NULL - deserialization: - FAIL_ON_UNKNOWN_PROPERTIES: false + redis: + host: cloud_api_sample_redis + port: 6379 + database: 0 + username: # if you enable + password: + lettuce: + pool: + max-active: 8 + max-idle: 8 + min-idle: 0 + jwt: issuer: DJI @@ -30,13 +36,13 @@ jwt: mqtt: protocol: tcp - host: Please enter the address of the emqx server. # Example: 192.168.1.1 + host: Please enter your ip. # 192.168.1.1 port: 1883 username: JavaServer password: 123456 client-id: 123456 # Topics that need to be subscribed when initially connecting to mqtt, multiple topics are divided by ",". - inbound-topic: sys/product/+/status + inbound-topic: sys/product/+/status,thing/product/+/requests url: manage: @@ -56,29 +62,43 @@ url: version: /api/v1 # Tutorial: https://help.aliyun.com/document_detail/100624.htm?spm=a2c4g.11186623.0.0.74075e34eIhK7T#concept-xzh-nzk-2gb -aliyun: - oss: - enable: false - endpoint: Please enter your endpoint. # Example: https://oss-cn-shenzhen.aliyuncs.com - access-key: Please enter your access key. - secret-key: Please enter your secret key. - expire: 3600 - region: Please enter oss region. # Example: cn-shenzhen - role-session-name: Please enter session name. # A custom role session name to distinguish the different tokens, for example it could be filled in as SessionTest. - role-arn: Please enter role ARN. # Example: acs:ram::123456789:role/oss - bucket: Please enter bucket name. - object-dir-prefix: Please enter object prefix. +oss: + enable: true + provider: ali # @see com.dji.sample.component.OssConfiguration.model.enums.OssTypeEnum + endpoint: https://oss-cn-hangzhou.aliyuncs.com + access-key: Please enter your access key. + secret-key: Please enter your secret key. + expire: 3600 + region: Please enter your oss region. # cn-hangzhou + role-session-name: cloudApi + role-arn: Please enter your role arn. # acs:ram::123456789:role/stsrole + bucket: Please enter your bucket name. + object-dir-prefix: Please enter a folder name. + +#oss: +# enable: true +# provider: aws +# endpoint: https://s3.us-east-1.amazonaws.com +# access-key: +# secret-key: +# expire: 3600 +# region: us-east-1 +# role-session-name: cloudApi +# role-arn: +# bucket: cloudapi-bucket +# object-dir-prefix: wayline # MinIO is temporarily unavailable. -minio: - enable: false - endpoint: Please enter your endpoint. #http://192.168.1.1:9000/ - access-key: minioadmin - secret-key: minioadmin - bucket: Please enter bucket name. - expire: 3600 - region: Please enter minio region. - object-dir-prefix: Please enter object prefix. +#oss: +# enable: false +# provider: minio +# endpoint: +# access-key: +# secret-key: +# bucket: +# expire: +# region: +# object-dir-prefix: logging: level: