From 12887aed11fadbba828c69ac3eef52d7149bca49 Mon Sep 17 00:00:00 2001 From: "sean.zhou" Date: Mon, 18 Sep 2023 21:06:10 +0800 Subject: [PATCH] What's new? CloudSDK V1.0.0 was released to adapt to multiple versions of device protocols, simplify access to mqtt and http protocols, and provide protocol verification and error code modules. --- pom.xml | 389 ++++--- sql/cloud_sample.sql | 6 +- .../dji/sample/CloudApiSampleApplication.java | 3 +- .../sample/common/error/CommonErrorEnum.java | 7 +- .../sample/common/error/LiveErrorEnum.java | 78 -- .../dji/sample/common/model/Pagination.java | 36 - .../sample/common/model/PaginationData.java | 27 - .../sample/common/model/ResponseResult.java | 69 -- .../com/dji/sample/common/util/JwtUtil.java | 2 +- .../common/util/SpringBeanUtilsTest.java | 30 + .../component/ApplicationBootInitial.java | 19 +- .../dji/sample/component/AuthInterceptor.java | 4 +- .../component/GlobalExceptionHandler.java | 14 +- .../component/GlobalScheduleService.java | 18 +- .../mqtt/config/MqttMessageChannel.java | 145 +-- .../config/MqttOutboundConfiguration.java | 47 - ...on.java => MqttPropertyConfiguration.java} | 22 +- .../handler/AbstractStateTopicHandler.java | 39 - .../component/mqtt/handler/EventsRouter.java | 68 -- .../mqtt/handler/PropertySetReplyHandler.java | 40 - .../mqtt/handler/RequestsRouter.java | 45 - .../mqtt/handler/ServicesReplyHandler.java | 53 - .../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 | 91 -- .../mqtt/model/CommonTopicReceiver.java | 34 - .../mqtt/model/CommonTopicResponse.java | 35 - .../component/mqtt/model/ConfigScopeEnum.java | 32 - .../component/mqtt/model/ErrorInfoReply.java | 23 - .../mqtt/model/EventsMethodEnum.java | 83 -- .../model/EventsOutputProgressReceiver.java | 18 - .../component/mqtt/model/EventsReceiver.java | 53 +- .../mqtt/model/EventsResultStatusEnum.java | 49 - .../component/mqtt/model/MapKeyConst.java | 22 - .../mqtt/model/MqttProtocolEnum.java | 2 +- .../mqtt/model/OutputProgressReceiver.java | 18 - .../mqtt/model/RequestsMethodEnum.java | 44 - .../component/mqtt/model/RequestsReply.java | 44 - .../component/mqtt/model/ServiceReply.java | 20 - .../sample/component/mqtt/model/SetReply.java | 14 - .../mqtt/model/SetReplyStatusResultEnum.java | 30 - .../component/mqtt/model/StateDataEnum.java | 26 - .../mqtt/service/IMessageSenderService.java | 90 -- .../impl/MessageSenderServiceImpl.java | 116 -- .../service/impl/MqttTopicServiceImpl.java | 44 - .../component/oss/model/OssConfiguration.java | 7 +- .../component/oss/service/IOssService.java | 7 +- .../service/impl/AliyunOssServiceImpl.java | 14 +- .../oss/service/impl/AmazonS3ServiceImpl.java | 14 +- .../oss/service/impl/MinIOServiceImpl.java | 16 +- .../oss/service/impl/OssServiceContext.java | 8 +- .../sample/component/redis/RedisConst.java | 12 +- .../config/MyConcurrentWebSocketSession.java | 25 + ...ltFactory.java => MyWebSocketFactory.java} | 8 +- ...ltHandler.java => MyWebSocketHandler.java} | 8 +- .../websocket/model/BizCodeEnum.java | 2 +- .../model/CustomWebSocketMessage.java | 30 - .../service/IWebSocketManageService.java | 8 +- ...ice.java => IWebSocketMessageService.java} | 10 +- .../impl/WebSocketManageServiceImpl.java | 14 +- ....java => WebSocketMessageServiceImpl.java} | 25 +- .../SpringBeanConfiguration.java | 1 - .../configuration/mvc/GetSnakeDataBinder.java | 53 - .../mvc/GlobalMVCConfigurer.java | 14 +- .../control/controller/DockController.java | 23 +- .../control/controller/DrcController.java | 18 +- .../control/model/dto/AirConditionerMode.java | 22 + .../sample/control/model/dto/AlarmState.java | 10 +- .../control/model/dto/BatteryStoreMode.java | 11 +- .../sample/control/model/dto/DrcModeDTO.java | 32 - .../model/dto/DrcModeReasonReceiver.java | 15 - .../model/dto/DrcStatusNotifyReceiver.java | 18 - .../model/dto/FlyToProgressReceiver.java | 22 - .../control/model/dto/LinkWorkMode.java | 22 +- .../control/model/dto/MqttBrokerDTO.java | 31 - .../sample/control/model/dto/PointDTO.java | 31 - .../model/dto/RemoteDebugOpenState.java | 6 +- .../model/dto/ReturnHomeCancelState.java | 28 + .../control/model/dto/ReturnHomeState.java | 19 +- .../model/dto/TakeoffProgressReceiver.java | 25 - .../model/enums/BatteryStoreModeEnum.java | 29 - .../control/model/enums/CameraStateEnum.java | 26 - .../control/model/enums/LinkWorkModeEnum.java | 29 - .../model/enums/PayloadCommandsEnum.java | 32 +- .../model/enums/RemoteDebugMethodEnum.java | 74 +- .../control/model/param/DrcModeParam.java | 2 + .../model/param/DronePayloadParam.java | 6 +- .../control/model/param/FlyToPointParam.java | 4 +- .../control/model/param/RemoteDebugParam.java | 3 + .../model/param/TakeoffToPointParam.java | 25 +- .../control/service/IControlService.java | 27 +- .../sample/control/service/IDrcService.java | 4 +- .../impl/CameraFocalLengthSetImpl.java | 7 +- .../service/impl/CameraModeSwitchImpl.java | 2 +- .../service/impl/CameraPhotoTakeImpl.java | 2 +- .../impl/CameraRecordingStartImpl.java | 4 +- .../service/impl/CameraRecordingStopImpl.java | 2 +- .../service/impl/ControlServiceImpl.java | 262 ++--- .../control/service/impl/DrcServiceImpl.java | 135 +-- .../service/impl/PayloadCommandsHandler.java | 20 +- .../service/impl/RemoteDebugHandler.java | 8 +- .../service/impl/SDKControlService.java | 118 ++ .../control/service/impl/SDKRemoteDebug.java | 63 + .../manage/controller/DeviceController.java | 106 +- .../controller/DeviceFirmwareController.java | 30 +- .../controller/DeviceHmsController.java | 18 +- .../controller/DeviceLogsController.java | 54 +- .../controller/LiveStreamController.java | 28 +- .../manage/controller/LoginController.java | 10 +- .../manage/controller/TopologyController.java | 27 +- .../manage/controller/UserController.java | 22 +- .../controller/WorkspaceController.java | 6 +- .../manage/model/dto/DeviceAuthorityDTO.java | 3 +- .../sample/manage/model/dto/DeviceDTO.java | 20 +- .../manage/model/dto/DeviceLogsDTO.java | 6 +- .../manage/model/dto/DeviceModelDTO.java | 27 - .../manage/model/dto/DevicePayloadDTO.java | 9 +- .../model/dto/DevicePayloadReceiver.java | 29 + .../sample/manage/model/dto/IconUrlDTO.java | 25 - .../sample/manage/model/dto/LiveTypeDTO.java | 9 +- .../LogsFileUploadDTO.java} | 10 +- .../LogsFileUploadListDTO.java} | 6 +- .../model/dto/LogsOutputProgressDTO.java | 3 +- .../model/dto/LogsUploadCredentialsDTO.java | 16 +- .../manage/model/dto/TelemetryDeviceDTO.java | 32 - .../sample/manage/model/dto/TopologyDTO.java | 24 - .../manage/model/dto/TopologyDeviceDTO.java | 150 ++- .../sample/manage/model/dto/WorkspaceDTO.java | 3 - .../manage/model/entity/DeviceEntity.java | 2 +- .../manage/model/enums/ControlSourceEnum.java | 26 - .../model/enums/CustomizeConfigScopeEnum.java | 33 + .../manage/model/enums/DeviceDomainEnum.java | 35 - .../model/enums/DeviceFirmwareStatusEnum.java | 9 +- .../model/enums/DeviceLogsStatusEnum.java | 17 +- .../model/enums/DeviceSetPropertyEnum.java | 41 - .../manage/model/enums/DockDrcStateEnum.java | 36 - .../model/enums/DroneRcLostActionEnum.java | 26 - ...a => ExitWaylineWhenRcLostActionEnum.java} | 8 +- .../sample/manage/model/enums/HmsEnum.java | 177 --- .../model/enums/LogsFileMethodEnum.java | 26 - .../model/enums/PropertySetFieldEnum.java | 68 ++ .../model/param/DeviceFirmwareQueryParam.java | 4 + .../param/DeviceFirmwareUploadParam.java | 3 + .../model/param/DeviceHmsQueryParam.java | 6 + .../model/param/DeviceLogsCreateParam.java | 4 +- .../model/param/DeviceLogsGetParam.java | 19 + .../model/param/DeviceLogsQueryParam.java | 5 + .../model/param/DeviceOtaCreateParam.java | 32 - .../receiver/AlternateLandPointReceiver.java | 20 - .../model/receiver/BackupBatteryReceiver.java | 20 - .../model/receiver/BasicDeviceProperty.java | 4 +- .../model/receiver/BatteryReceiver.java | 27 - .../model/receiver/BatteryStateReceiver.java | 34 - .../model/receiver/BindDeviceReceiver.java | 22 - .../model/receiver/BindStatusReceiver.java | 30 - .../receiver/CapacityCameraReceiver.java | 10 +- .../receiver/CapacityDeviceReceiver.java | 4 +- .../model/receiver/CapacityVideoReceiver.java | 8 +- .../model/receiver/DeviceBasicReceiver.java | 12 +- .../model/receiver/DeviceHmsReceiver.java | 26 - .../model/receiver/DevicePayloadReceiver.java | 26 - .../receiver/DistanceLimitStatusReceiver.java | 34 +- .../receiver/DockMediaFileDetailReceiver.java | 14 - .../model/receiver/DockSubDeviceReceiver.java | 20 - .../receiver/DockWirelessLinkReceiver.java | 38 - .../DroneBatteryMaintenanceInfoReceiver.java | 16 - .../receiver/DroneChargeStateReceiver.java | 16 - .../receiver/FirmwareProgressExtReceiver.java | 14 - .../receiver/FirmwareVersionReceiver.java | 2 +- .../model/receiver/HeightLimitReceiver.java | 10 +- .../model/receiver/HmsArgsReceiver.java | 18 - .../model/receiver/LiveStatusReceiver.java | 23 - .../receiver/LiveviewWorldRegionReceiver.java | 20 - .../model/receiver/LogsExtFileReceiver.java | 27 - .../manage/model/receiver/LogsFile.java | 26 - .../model/receiver/LogsProgressReceiver.java | 26 - .../model/receiver/NetworkStateReceiver.java | 18 - .../receiver/NightLightsStateReceiver.java | 26 +- .../receiver/ObstacleAvoidanceReceiver.java | 47 +- .../receiver/OrganizationGetReceiver.java | 16 - .../model/receiver/OsdCameraReceiver.java | 34 - .../model/receiver/OsdDockReceiver.java | 92 -- .../model/receiver/OsdGatewayReceiver.java | 31 - .../model/receiver/OsdPayloadReceiver.java | 33 - .../model/receiver/OsdSubDeviceReceiver.java | 84 -- .../receiver/OutOfControlActionReceiver.java | 25 +- .../model/receiver/OutputLogsExtReceiver.java | 16 - .../receiver/OutputLogsProgressReceiver.java | 3 +- .../model/receiver/PositionStateReceiver.java | 25 - .../model/receiver/RequestConfigReceiver.java | 16 - .../model/receiver/RthAltitudeReceiver.java | 9 +- .../model/receiver/StorageReceiver.java | 16 - .../receiver/WirelessLinkStateReceiver.java | 3 - .../service/ICapacityCameraService.java | 3 +- .../service/IDeviceFirmwareService.java | 6 +- .../manage/service/IDeviceHmsService.java | 11 +- .../manage/service/IDeviceLogsService.java | 23 +- .../manage/service/IDevicePayloadService.java | 11 +- .../manage/service/IDeviceRedisService.java | 25 +- .../sample/manage/service/IDeviceService.java | 110 +- .../manage/service/ILiveStreamService.java | 18 +- .../manage/service/ILogsFileIndexService.java | 10 +- .../manage/service/ILogsFileService.java | 11 +- .../sample/manage/service/ITSAService.java | 35 - .../manage/service/ITopologyService.java | 6 +- .../sample/manage/service/IUserService.java | 8 +- .../manage/service/IWorkspaceService.java | 7 - .../service/impl/AbstractTSAService.java | 63 - .../service/impl/CameraVideoServiceImpl.java | 20 +- .../impl/CapacityCameraServiceImpl.java | 29 +- .../impl/DeviceFirmwareServiceImpl.java | 89 +- .../service/impl/DeviceHmsServiceImpl.java | 125 +- .../service/impl/DeviceLogsServiceImpl.java | 171 ++- .../service/impl/DeviceOSDServiceImpl.java | 87 -- .../impl/DevicePayloadServiceImpl.java | 150 +-- .../service/impl/DeviceRedisServiceImpl.java | 39 +- .../service/impl/DeviceServiceImpl.java | 1034 +++++------------ .../service/impl/DockOSDServiceImpl.java | 62 - .../service/impl/GatewayOSDServiceImpl.java | 64 - .../service/impl/LiveStreamServiceImpl.java | 229 ++-- .../impl/LogsFileIndexServiceImpl.java | 40 +- .../service/impl/LogsFileServiceImpl.java | 25 +- .../service/impl/RequestConfigContext.java | 60 +- .../manage/service/impl/SDKDeviceService.java | 497 ++++++++ .../service/impl/SDKLivestreamService.java | 49 + .../manage/service/impl/SDKLogService.java | 14 + .../service/impl/SDKOrganizationService.java | 177 +++ .../service/impl/SDKPropertySetService.java | 14 + .../service/impl/TopologyServiceImpl.java | 22 +- .../manage/service/impl/UserServiceImpl.java | 52 +- .../service/impl/WorkspaceServiceImpl.java | 41 - .../WorkspaceElementController.java | 131 +-- .../map/model/dto/ContentPropertyDTO.java | 25 - .../map/model/dto/ElementCoordinateDTO.java | 25 - .../map/model/dto/ElementCreateDTO.java | 18 - .../map/model/dto/ElementLineStringDTO.java | 47 - .../sample/map/model/dto/ElementPointDTO.java | 46 - .../map/model/dto/ElementPolygonDTO.java | 48 - .../map/model/dto/ElementResourceDTO.java | 26 - .../dji/sample/map/model/dto/ElementType.java | 43 - .../map/model/dto/ElementUpdateDTO.java | 17 - .../dji/sample/map/model/dto/GroupDTO.java | 38 - .../sample/map/model/dto/GroupElementDTO.java | 4 +- .../map/model/dto/ResourceContentDTO.java | 25 - .../map/model/dto/WebSocketElementDelDTO.java | 25 - .../map/model/enums/ElementTypeEnum.java | 52 +- .../service/IElementCoordinateService.java | 6 +- .../map/service/IGroupElementService.java | 11 +- .../dji/sample/map/service/IGroupService.java | 4 +- .../map/service/IWorkspaceElementService.java | 28 +- .../impl/ElementCoordinateServiceImpl.java | 25 +- .../service/impl/GroupElementServiceImpl.java | 107 +- .../map/service/impl/GroupServiceImpl.java | 24 +- .../impl/WorkspaceElementServiceImpl.java | 112 +- .../media/controller/FileController.java | 12 +- .../media/controller/MediaController.java | 56 +- .../sample/media/model/CredentialsDTO.java | 50 - .../sample/media/model/FileExtensionDTO.java | 28 - .../sample/media/model/FileMetadataDTO.java | 31 - .../media/model/FileUploadCallback.java | 18 - .../dji/sample/media/model/FileUploadDTO.java | 26 - .../sample/media/model/MediaFileCountDTO.java | 2 + .../dji/sample/media/model/PositionDTO.java | 16 - .../sample/media/model/StsCredentialsDTO.java | 33 - .../sample/media/service/IFileService.java | 6 +- .../media/service/IMediaRedisService.java | 26 + .../sample/media/service/IMediaService.java | 4 +- .../media/service/impl/FileServiceImpl.java | 31 +- .../service/impl/MediaRedisServiceImpl.java | 54 + .../media/service/impl/MediaServiceImpl.java | 210 ++-- .../storage/controller/StorageController.java | 24 +- .../storage/service/IStorageService.java | 12 +- .../service/impl/StorageServiceImpl.java | 45 +- .../controller/WaylineFileController.java | 190 ++- .../controller/WaylineJobController.java | 48 +- ...Key.java => ConditionalWaylineJobKey.java} | 17 +- ...essExt.java => FlighttaskProgressExt.java} | 2 +- ...s.java => FlighttaskProgressProgress.java} | 2 +- .../model/dto/WaylineFileUploadDTO.java | 18 - .../wayline/model/dto/WaylineJobDTO.java | 11 +- .../model/dto/WaylineTaskConditionDTO.java | 6 +- .../model/dto/WaylineTaskCreateDTO.java | 6 +- .../dto/WaylineTaskProgressReceiver.java | 4 +- .../model/enums/WaylineErrorCodeEnum.java | 6 +- .../model/enums/WaylineTaskTypeEnum.java | 36 - .../wayline/model/param/CreateJobParam.java | 12 +- .../wayline/service/IFlightTaskService.java | 67 ++ .../wayline/service/IWaylineFileService.java | 9 +- .../wayline/service/IWaylineJobService.java | 70 +- .../wayline/service/IWaylineRedisService.java | 42 +- .../service/impl/FlightTaskServiceImpl.java | 602 +++++++--- .../service/impl/SDKWaylineService.java | 159 +++ .../service/impl/WaylineFileServiceImpl.java | 66 +- .../service/impl/WaylineJobServiceImpl.java | 449 +------ .../service/impl/WaylineRedisServiceImpl.java | 35 +- src/main/java/com/dji/sdk/README.md | 48 + .../dji/sdk/annotations/CloudSDKVersion.java | 26 + .../sdk/cloudapi/config/ConfigScopeEnum.java | 34 + .../sdk/cloudapi/config/ConfigTypeEnum.java | 34 + .../config/ProductConfigResponse.java | 73 ++ .../config/RequestsConfigRequest.java | 42 + .../config/api/AbstractConfigService.java | 30 + .../cloudapi/control/CameraAimRequest.java | 99 ++ .../control/CameraFocalLengthSetRequest.java | 66 ++ .../control/CameraModeSwitchRequest.java | 50 + .../control/CameraPhotoTakeRequest.java | 36 + .../control/CameraRecordingStartRequest.java | 36 + .../control/CameraRecordingStopRequest.java | 36 + .../cloudapi/control}/CameraTypeEnum.java | 12 +- .../control/CommanderFlightModeEnum.java | 37 + .../control/CommanderModeLostActionEnum.java | 36 + .../control/ControlErrorCodeEnum.java | 91 ++ .../cloudapi/control/ControlMethodEnum.java | 54 + .../sdk/cloudapi/control/DelayInfoPush.java | 44 + .../cloudapi/control/DrcModeEnterRequest.java | 75 ++ .../cloudapi/control/DrcModeMqttBroker.java | 102 ++ .../cloudapi/control}/DrcStatusErrorEnum.java | 20 +- .../sdk/cloudapi/control/DrcStatusNotify.java | 44 + .../cloudapi/control/DroneControlRequest.java | 121 ++ .../control/DroneControlResponse.java | 31 + .../cloudapi/control/FlyToPointProgress.java | 68 ++ .../cloudapi/control/FlyToPointRequest.java | 71 ++ .../cloudapi/control}/FlyToStatusEnum.java | 18 +- .../cloudapi/control/GimbalResetModeEnum.java | 40 + .../cloudapi/control/GimbalResetRequest.java | 49 + .../cloudapi/control/HeartBeatRequest.java | 50 + .../dji/sdk/cloudapi/control/HsiInfoPush.java | 248 ++++ .../control/JoystickInvalidNotify.java | 30 + .../control/JoystickInvalidReasonEnum.java} | 24 +- .../sdk/cloudapi/control/LiveviewDelay.java | 35 + .../dji/sdk/cloudapi/control/OsdInfoPush.java | 138 +++ .../control/PayloadAuthorityGrabRequest.java | 36 + .../control/PayloadControlMethodEnum.java | 51 + .../com/dji/sdk/cloudapi/control/Point.java | 71 ++ .../cloudapi/control}/TakeoffStatusEnum.java | 12 +- .../control/TakeoffToPointProgress.java | 80 ++ .../control/TakeoffToPointRequest.java | 218 ++++ .../cloudapi/control/ZoomCameraTypeEnum.java | 36 + .../control/api/AbstractControlService.java | 399 +++++++ .../AirConditionerModeSwitchActionEnum.java | 40 + .../AirConditionerModeSwitchRequest.java | 35 + .../debug/AlarmStateSwitchRequest.java | 36 + .../BatteryMaintenanceSwitchRequest.java | 36 + .../debug/BatteryStoreModeSwitchRequest.java | 36 + .../cloudapi/debug/DebugErrorCodeEnum.java | 168 +++ .../sdk/cloudapi/debug/DebugMethodEnum.java | 76 ++ .../cloudapi/debug/RemoteDebugProgress.java | 42 + .../debug/RemoteDebugProgressData.java | 78 ++ .../cloudapi/debug/RemoteDebugResponse.java | 30 + .../cloudapi/debug/RemoteDebugStatusEnum.java | 57 + .../debug/RemoteDebugStepKeyEnum.java | 88 ++ .../debug/SdrWorkmodeSwitchRequest.java | 36 + .../debug/api/AbstractDebugService.java | 340 ++++++ .../sdk/cloudapi/device/AirConditioner.java | 42 + .../device/AirConditionerStateEnum.java | 56 + .../cloudapi/device/AlternateLandPoint.java | 69 ++ .../sdk/cloudapi/device/BackupBattery.java | 57 + .../com/dji/sdk/cloudapi/device/Battery.java | 138 +++ .../sdk/cloudapi/device/BatteryIndexEnum.java | 37 + .../cloudapi/device/BatteryStoreModeEnum.java | 36 + .../sdk/cloudapi/device/CameraModeEnum.java | 36 + .../sdk/cloudapi/device/CameraStateEnum.java | 37 + .../cloudapi/device/ControlSourceEnum.java | 38 + .../sdk/cloudapi/device/CoverStateEnum.java | 42 + .../sdk/cloudapi/device/DeviceDomainEnum.java | 43 + .../dji/sdk/cloudapi/device/DeviceEnum.java | 121 ++ .../sdk/cloudapi/device/DeviceModelEnum.java | 11 + .../sdk/cloudapi/device/DeviceOsdHost.java | 124 ++ .../cloudapi/device/DeviceOsdWsResponse.java | 53 + .../cloudapi/device/DeviceSubTypeEnum.java | 42 + .../sdk/cloudapi/device/DeviceTypeEnum.java | 81 ++ .../device/DockDistanceLimitStatus.java | 58 + .../device/DockDroneControlSource.java | 116 ++ .../sdk/cloudapi/device/DockDronePayload.java | 236 ++++ .../cloudapi/device/DockFirmwareVersion.java | 57 + .../cloudapi/device/DockLiveErrorStatus.java | 54 + .../sdk/cloudapi/device/DockLiveStatus.java | 32 + .../cloudapi/device/DockLiveStatusData.java | 81 ++ .../cloudapi/device/DockMaintainStatus.java | 66 ++ .../cloudapi/device}/DockModeCodeEnum.java | 22 +- .../dji/sdk/cloudapi/device/DockPayload.java | 56 + .../device/DockPayloadControlSource.java | 55 + .../cloudapi/device/DockPositionState.java | 81 ++ .../sdk/cloudapi/device/DockSubDevice.java | 66 ++ .../sdk/cloudapi/device/DockWpmzVersion.java | 30 + .../dji/sdk/cloudapi/device/DrcStateEnum.java | 38 + .../dji/sdk/cloudapi/device/DroneBattery.java | 80 ++ .../device/DroneBatteryMaintenance.java | 66 ++ .../device/DroneBatteryMaintenanceInfo.java | 32 + .../sdk/cloudapi/device/DroneChargeState.java | 42 + .../cloudapi/device/DroneMaintainStatus.java | 78 ++ .../cloudapi/device/DroneModeCodeEnum.java} | 20 +- .../cloudapi/device/DronePositionState.java | 66 ++ .../device/ExitWaylineWhenRcLostEnum.java | 36 + .../device/FlighttaskStepCodeEnum.java | 49 + .../com/dji/sdk/cloudapi/device/GearEnum.java | 54 + .../sdk/cloudapi/device/LinkWorkModeEnum.java | 36 + .../cloudapi/device/LiveviewWorldRegion.java | 66 ++ .../sdk/cloudapi/device/MaintainTypeEnum.java | 46 + .../device/MeasureTargetStateEnum.java | 42 + .../sdk/cloudapi/device/MediaFileDetail.java | 30 + .../cloudapi/device/ModeCodeReasonEnum.java | 79 ++ .../dji/sdk/cloudapi/device/NetworkState.java | 54 + .../device/NetworkStateQualityEnum.java | 40 + .../cloudapi/device/NetworkStateTypeEnum.java | 38 + .../cloudapi/device/ObstacleAvoidance.java | 54 + .../dji/sdk/cloudapi/device/OsdCamera.java | 138 +++ .../com/dji/sdk/cloudapi/device/OsdDock.java | 474 ++++++++ .../dji/sdk/cloudapi/device/OsdDockDrone.java | 469 ++++++++ .../device/OsdDockMaintainStatus.java | 32 + .../device/OsdDroneMaintainStatus.java | 32 + .../dji/sdk/cloudapi/device/OsdRcDrone.java | 311 +++++ .../sdk/cloudapi/device/OsdRemoteControl.java | 78 ++ .../device/PayloadFirmwareVersion.java | 53 + .../dji/sdk/cloudapi/device/PayloadIndex.java | 75 ++ .../sdk/cloudapi/device/PayloadModelEnum.java | 77 ++ .../cloudapi/device/PayloadPositionEnum.java | 40 + .../cloudapi/device/PositionFixedEnum.java | 42 + .../sdk/cloudapi/device/PutterStateEnum.java | 42 + .../dji/sdk/cloudapi/device/RainfallEnum.java | 42 + .../device/RcDistanceLimitStatus.java | 43 + .../cloudapi/device/RcDroneControlSource.java | 92 ++ .../sdk/cloudapi/device/RcDronePayload.java | 140 +++ .../cloudapi/device/RcFirmwareVersion.java | 31 + .../dji/sdk/cloudapi/device/RcLiveStatus.java | 32 + .../sdk/cloudapi/device/RcLiveStatusData.java | 56 + .../sdk/cloudapi/device/RcLostActionEnum.java | 38 + .../device/RcPayloadControlSource.java | 66 ++ .../sdk/cloudapi/device/SmartTrackPoint.java | 66 ++ .../com/dji/sdk/cloudapi/device/Storage.java | 42 + .../sdk/cloudapi/device/SwitchActionEnum.java | 36 + .../cloudapi/device/ThermalGainModeEnum.java | 40 + .../device/ThermalPaletteStyleEnum.java | 72 ++ .../cloudapi/device/TrackTargetModeEnum.java | 39 + .../dji/sdk/cloudapi/device/UpdateTopo.java | 105 ++ .../cloudapi/device/UpdateTopoSubDevice.java | 114 ++ .../com/dji/sdk/cloudapi/device/VideoId.java | 82 ++ .../cloudapi/device/WindDirectionEnum.java | 52 + .../dji/sdk/cloudapi/device/WirelessLink.java | 145 +++ .../device/api/AbstractDeviceService.java | 177 +++ .../firmware/FirmwareErrorCodeEnum.java | 97 ++ .../cloudapi/firmware/FirmwareMethodEnum.java | 24 + .../firmware/FirmwareUpgradeTypeEnum.java | 44 + .../cloudapi/firmware/OtaCreateDevice.java | 113 ++ .../cloudapi/firmware/OtaCreateRequest.java | 40 + .../cloudapi/firmware/OtaCreateResponse.java | 33 + .../sdk/cloudapi/firmware/OtaProgress.java | 54 + .../cloudapi/firmware/OtaProgressData.java | 42 + .../sdk/cloudapi/firmware/OtaProgressExt.java | 30 + .../firmware/OtaProgressStatusEnum.java | 57 + .../firmware/OtaProgressStepEnum.java | 36 + .../firmware/api/AbstractFirmwareService.java | 57 + .../com/dji/sdk/cloudapi/hms/DeviceHms.java | 104 ++ .../dji/sdk/cloudapi/hms/DeviceHmsArgs.java | 54 + .../java/com/dji/sdk/cloudapi/hms/Hms.java | 32 + .../sdk/cloudapi/hms/HmsBatteryIndexEnum.java | 48 + .../cloudapi/hms/HmsChargingRodIndexEnum.java | 50 + .../cloudapi/hms/HmsDockCoverIndexEnum.java | 48 + .../dji/sdk/cloudapi/hms/HmsFaqIdEnum.java | 43 + .../sdk/cloudapi/hms/HmsFormatKeyEnum.java | 44 + .../dji/sdk/cloudapi/hms/HmsInTheSkyEnum.java | 24 + .../dji/sdk/cloudapi/hms/HmsLevelEnum.java | 38 + .../cloudapi/hms/HmsMessageLanguageEnum.java | 23 + .../dji/sdk/cloudapi/hms/HmsModuleEnum.java | 40 + .../cloudapi/hms/api/AbstractHmsService.java | 27 + .../cloudapi/livestream/DockLiveCapacity.java | 66 ++ .../livestream/DockLiveCapacityCamera.java | 80 ++ .../livestream/DockLiveCapacityDevice.java | 81 ++ .../livestream/DockLiveCapacityVideo.java | 56 + .../DockLivestreamAbilityUpdate.java | 30 + .../livestream/LensChangeVideoTypeEnum.java | 38 + .../livestream/LiveErrorCodeEnum.java | 80 ++ .../livestream/LiveLensChangeRequest.java | 53 + .../livestream/LiveSetQualityRequest.java | 53 + .../livestream/LiveStartPushRequest.java | 93 ++ .../livestream/LiveStopPushRequest.java | 40 + .../livestream/LiveStreamMethodEnum.java | 30 + .../cloudapi/livestream/RcLiveCapacity.java | 66 ++ .../livestream/RcLiveCapacityCamera.java | 80 ++ .../livestream/RcLiveCapacityDevice.java | 81 ++ .../livestream/RcLiveCapacityVideo.java | 42 + .../livestream/RcLivestreamAbilityUpdate.java | 30 + .../sdk/cloudapi/livestream/UrlTypeEnum.java | 40 + .../cloudapi/livestream/VideoQualityEnum.java | 42 + .../cloudapi/livestream/VideoTypeEnum.java | 42 + .../api/AbstractLivestreamService.java | 101 ++ .../sdk/cloudapi/log/FileUploadListFile.java | 68 ++ .../cloudapi/log/FileUploadListRequest.java | 43 + .../cloudapi/log/FileUploadListResponse.java | 32 + .../sdk/cloudapi/log/FileUploadProgress.java | 42 + .../cloudapi/log/FileUploadProgressExt.java | 32 + .../cloudapi/log/FileUploadProgressFile.java | 90 ++ .../sdk/cloudapi/log/FileUploadStartFile.java | 75 ++ .../cloudapi/log/FileUploadStartParam.java | 38 + .../cloudapi/log/FileUploadStartRequest.java | 138 +++ .../cloudapi/log/FileUploadStatusEnum.java | 61 + .../cloudapi/log/FileUploadUpdateRequest.java | 56 + .../log/FileUploadUpdateStatusEnum.java | 34 + .../sdk/cloudapi/log/LogErrorCodeEnum.java | 66 ++ .../dji/sdk/cloudapi/log/LogFileIndex.java | 72 ++ .../dji/sdk/cloudapi/log/LogFileProgress.java | 102 ++ .../dji/sdk/cloudapi/log/LogMethodEnum.java | 25 + .../dji/sdk/cloudapi/log/LogModuleEnum.java | 36 + .../cloudapi/log/api/AbstractLogService.java | 81 ++ .../cloudapi/map/CreateMapElementRequest.java | 68 ++ .../map/CreateMapElementResponse.java | 39 + .../dji/sdk/cloudapi/map/ElementContent.java | 66 ++ .../sdk/cloudapi/map/ElementCoordinate.java | 64 + .../sdk/cloudapi/map/ElementGeometryType.java | 53 + .../map/ElementLineStringGeometry.java | 81 ++ .../cloudapi/map/ElementPointGeometry.java | 77 ++ .../cloudapi/map/ElementPolygonGeometry.java | 82 ++ .../dji/sdk/cloudapi/map/ElementProperty.java | 54 + .../dji/sdk/cloudapi/map/ElementResource.java | 67 ++ .../cloudapi/map/ElementResourceTypeEnum.java | 48 + .../cloudapi/map/GetMapElementsResponse.java | 100 ++ .../dji/sdk/cloudapi/map/GroupTypeEnum.java | 44 + .../map/MapElementCreateWsResponse.java | 120 ++ .../map/MapElementDeleteWsResponse.java | 57 + .../map/MapElementUpdateWsResponse.java | 120 ++ .../dji/sdk/cloudapi/map/MapGroupElement.java | 102 ++ .../map/MapGroupRefreshWsResponse.java | 45 + .../cloudapi/map/UpdateMapElementRequest.java | 52 + .../sdk/cloudapi/map/api/IHttpMapService.java | 124 ++ .../cloudapi/media/FastUploadExtension.java | 99 ++ .../cloudapi/media/FileUploadCallback.java | 54 + .../media/FileUploadCallbackFile.java | 78 ++ .../media/FolderUploadCallbackRequest.java | 69 ++ .../media/GetFileFingerprintRequest.java | 41 + .../media/GetFileFingerprintResponse.java | 42 + .../HighestPriorityUploadFlightTaskMedia.java | 30 + .../media/MediaFastUploadRequest.java | 79 ++ .../cloudapi/media/MediaFileExtension.java | 114 ++ .../sdk/cloudapi/media/MediaFileMetadata.java | 104 ++ .../sdk/cloudapi/media/MediaMethodEnum.java | 24 + .../cloudapi/media/MediaSubFileTypeEnum.java | 38 + .../media/MediaUploadCallbackRequest.java | 124 ++ .../com/dji/sdk/cloudapi/media/Position.java | 50 + .../sdk/cloudapi/media/StorageConfigGet.java | 30 + .../media/StorageConfigGetModuleEnum.java | 34 + .../media/UploadCallbackFileExtension.java | 70 ++ .../media/UploadCallbackFileMetadata.java | 85 ++ .../UploadFlighttaskMediaPrioritize.java | 37 + .../media/api/AbstractMediaService.java | 78 ++ .../cloudapi/media/api/IHttpMediaService.java | 115 ++ .../AirportBindStatusRequest.java | 32 + .../AirportBindStatusResponse.java | 40 + .../AirportOrganizationBindRequest.java | 32 + .../AirportOrganizationBindResponse.java | 40 + .../AirportOrganizationGetRequest.java | 42 + .../AirportOrganizationGetResponse.java | 35 + .../organization/BindStatusRequestDevice.java | 88 ++ .../BindStatusResponseDevice.java | 30 + .../organization/OrganizationBindDevice.java | 80 ++ .../organization/OrganizationBindInfo.java | 57 + .../api/AbstractOrganizationService.java | 54 + .../property/DistanceLimitStatusData.java | 52 + .../property/DistanceLimitStatusSet.java | 38 + .../property/ExitWaylineWhenRcLostSet.java | 38 + .../sdk/cloudapi/property/HeightLimitSet.java | 41 + .../property/NightLightsStateSet.java | 38 + .../property/ObstacleAvoidanceSet.java | 38 + .../cloudapi/property/PropertySetEnum.java | 62 + .../cloudapi/property/RcLostActionSet.java | 38 + .../sdk/cloudapi/property/RthAltitudeSet.java | 41 + .../ThermalCurrentPaletteStyleSet.java | 59 + .../cloudapi/property/ThermalGainModeSet.java | 59 + .../ThermalIsothermLowerLimitSet.java | 58 + .../property/ThermalIsothermStateSet.java | 59 + .../ThermalIsothermUpperLimitSet.java | 58 + .../property/api/AbstractPropertyService.java | 78 ++ .../cloudapi/storage/CredentialsToken.java | 111 ++ .../cloudapi/storage}/OssTypeEnum.java | 7 +- .../storage/StsCredentialsResponse.java | 113 ++ .../storage/api/IHttpStorageService.java | 39 + .../dji/sdk/cloudapi/tsa/DeviceIconUrl.java | 56 + .../dji/sdk/cloudapi/tsa/DeviceTopology.java | 128 ++ .../cloudapi/tsa}/IconUrlEnum.java | 14 +- .../sdk/cloudapi/tsa/TopologyDeviceModel.java | 82 ++ .../dji/sdk/cloudapi/tsa/TopologyList.java | 55 + .../sdk/cloudapi/tsa/TopologyResponse.java | 40 + .../sdk/cloudapi/tsa/api/IHttpTsaService.java | 44 + .../cloudapi/wayline/BreakpointStateEnum.java | 43 + .../wayline/DeviceExitHomingNotify.java | 54 + .../wayline/ExecutableConditions.java | 39 + .../cloudapi/wayline/ExecutionStepEnum.java | 66 ++ .../wayline/ExitingRTHActionEnum.java | 44 + .../wayline/ExitingRTHReasonEnum.java | 61 + .../wayline/FlighttaskBreakPoint.java | 163 +++ .../wayline/FlighttaskBreakReasonEnum.java | 11 + .../wayline/FlighttaskCreateFile.java | 51 + .../wayline/FlighttaskCreateRequest.java | 73 ++ .../wayline/FlighttaskExecuteRequest.java | 37 + .../sdk/cloudapi/wayline/FlighttaskFile.java | 52 + .../wayline/FlighttaskPrepareRequest.java | 242 ++++ .../cloudapi/wayline/FlighttaskProgress.java | 54 + .../wayline/FlighttaskProgressData.java | 48 + .../wayline/FlighttaskProgressExt.java | 80 ++ .../sdk/cloudapi/wayline/FlighttaskReady.java | 35 + .../wayline/FlighttaskResourceGetRequest.java | 30 + .../FlighttaskResourceGetResponse.java | 39 + .../wayline/FlighttaskStatusEnum.java | 59 + .../wayline/FlighttaskUndoRequest.java | 39 + .../wayline/GetWaylineListRequest.java | 116 ++ .../wayline/GetWaylineListResponse.java | 203 ++++ .../wayline/OutOfControlActionEnum.java | 38 + .../sdk/cloudapi/wayline/ReadyConditions.java | 77 ++ .../dji/sdk/cloudapi/wayline/RthModeEnum.java | 37 + .../sdk/cloudapi/wayline/SimulateMission.java | 65 ++ .../cloudapi/wayline/SimulateSwitchEnum.java | 37 + .../sdk/cloudapi/wayline/TaskTypeEnum.java | 38 + .../wayline/WaylineErrorCodeEnum.java | 322 +++++ .../cloudapi/wayline/WaylineMethodEnum.java | 39 + .../sdk/cloudapi/wayline/WaylineTypeEnum.java | 51 + .../WaylineUploadCallbackMetadata.java | 83 ++ .../wayline/WaylineUploadCallbackRequest.java | 69 ++ .../wayline/api/AbstractWaylineService.java | 195 ++++ .../wayline/api/IHttpWaylineService.java | 151 +++ .../java/com/dji/sdk/common/BaseModel.java | 81 ++ .../dji/sdk/common/CloudSDKVersionEnum.java | 35 + src/main/java/com/dji/sdk/common/Common.java | 60 + .../com/dji/sdk/common/CommonErrorEnum.java | 42 + .../dji/sdk/common/DockThingVersionEnum.java | 52 + .../dji/sdk/common/DroneThingVersionEnum.java | 56 + .../dji/sdk/common/ErrorCodeSourceEnum.java | 39 + .../com/dji/sdk/common/GatewayManager.java | 88 ++ .../dji/sdk/common/GatewayThingVersion.java | 48 + .../com/dji/sdk/common/GatewayTypeEnum.java | 33 + .../dji/sdk/common/HttpResultResponse.java | 95 ++ .../error => sdk/common}/IErrorInfo.java | 6 +- .../java/com/dji/sdk/common/Pagination.java | 80 ++ .../com/dji/sdk/common/PaginationData.java | 58 + .../dji/sdk/common/RcThingVersionEnum.java | 35 + .../java/com/dji/sdk/common/SDKManager.java | 50 + .../util => sdk/common}/SpringBeanUtils.java | 2 +- .../com/dji/sdk/config/CloudSDKHandler.java | 107 ++ .../dji/sdk/config/CloudSDKMvcConfigurer.java | 16 + .../config}/GetSnakeArgumentProcessor.java | 6 +- .../dji/sdk/config/GetSnakeDataBinder.java | 110 ++ .../dji/sdk/exception/CloudSDKErrorEnum.java | 49 + .../dji/sdk/exception/CloudSDKException.java | 48 + .../exception/CloudSDKVersionException.java | 17 + src/main/java/com/dji/sdk/mqtt/Chan.java | 59 + .../java/com/dji/sdk/mqtt/ChannelName.java | 137 +++ .../mqtt/CloudApiTopicEnum.java} | 31 +- .../com/dji/sdk/mqtt/CommonTopicRequest.java | 71 ++ .../com/dji/sdk/mqtt/CommonTopicResponse.java | 71 ++ .../mqtt}/IMqttMessageGateway.java | 3 +- .../mqtt}/IMqttTopicService.java | 10 +- .../mqtt}/InboundMessageRouter.java | 16 +- .../mqtt/MqttConfiguration.java} | 54 +- .../com/dji/sdk/mqtt/MqttGatewayPublish.java | 101 ++ src/main/java/com/dji/sdk/mqtt/MqttReply.java | 73 ++ .../com/dji/sdk/mqtt/MqttReplyHandler.java | 60 + .../dji/sdk/mqtt/MqttTopicServiceImpl.java | 57 + .../mqtt/model => sdk/mqtt}/TopicConst.java | 2 +- .../com/dji/sdk/mqtt/drc/DrcDownPublish.java | 40 + .../java/com/dji/sdk/mqtt/drc/DrcUpData.java | 44 + .../com/dji/sdk/mqtt/drc/DrcUpMethodEnum.java | 59 + .../com/dji/sdk/mqtt/drc/DrcUpRouter.java | 41 + .../com/dji/sdk/mqtt/drc/DrcUpSubscribe.java | 26 + .../com/dji/sdk/mqtt/drc/TopicDrcRequest.java | 73 ++ .../dji/sdk/mqtt/drc/TopicDrcResponse.java | 73 ++ .../sdk/mqtt/events/EventsDataRequest.java | 42 + .../dji/sdk/mqtt/events/EventsErrorCode.java | 88 ++ .../dji/sdk/mqtt/events/EventsMethodEnum.java | 105 ++ .../com/dji/sdk/mqtt/events/EventsRouter.java | 71 ++ .../dji/sdk/mqtt/events/EventsSubscribe.java | 41 + .../dji/sdk/mqtt/events/IEventsErrorCode.java | 22 + .../sdk/mqtt/events/TopicEventsRequest.java | 108 ++ .../sdk/mqtt/events/TopicEventsResponse.java | 73 ++ .../dji/sdk/mqtt/osd/OsdDeviceTypeEnum.java | 68 ++ .../java/com/dji/sdk/mqtt/osd/OsdRouter.java | 64 + .../com/dji/sdk/mqtt/osd/OsdSubscribe.java | 44 + .../com/dji/sdk/mqtt/osd/TopicOsdRequest.java | 83 ++ .../sdk/mqtt/property/PropertySetPublish.java | 40 + .../property/PropertySetReplyHandler.java | 44 + .../property/PropertySetReplyResultEnum.java | 38 + .../mqtt/property/PropertySetSubscribe.java | 30 + .../property/TopicPropertySetRequest.java | 61 + .../property/TopicPropertySetResponse.java | 61 + .../sdk/mqtt/requests/RequestsMethodEnum.java | 63 + .../dji/sdk/mqtt/requests/RequestsRouter.java | 64 + .../sdk/mqtt/requests/RequestsSubscribe.java | 34 + .../mqtt/requests/TopicRequestsRequest.java | 84 ++ .../mqtt/requests/TopicRequestsResponse.java | 73 ++ .../sdk/mqtt/services/IServicesErrorCode.java | 22 + .../sdk/mqtt/services/ServicesErrorCode.java | 93 ++ .../sdk/mqtt/services/ServicesPublish.java | 89 ++ .../sdk/mqtt/services/ServicesReplyData.java | 42 + .../mqtt/services/ServicesReplyHandler.java | 45 + .../mqtt/services/ServicesReplyReceiver.java | 66 ++ .../sdk/mqtt/services/ServicesSubscribe.java | 30 + .../mqtt/services/TopicServicesRequest.java | 73 ++ .../mqtt/services/TopicServicesResponse.java | 73 ++ .../sdk/mqtt/state/DockStateDataKeyEnum.java | 55 + .../sdk/mqtt/state/RcStateDataKeyEnum.java | 53 + .../dji/sdk/mqtt/state/StateDataKeyEnum.java | 64 + .../com/dji/sdk/mqtt/state/StateRouter.java | 63 + .../dji/sdk/mqtt/state/StateSubscribe.java | 44 + .../dji/sdk/mqtt/state/TopicStateRequest.java | 83 ++ .../com/dji/sdk/mqtt/status/StatusRouter.java | 73 ++ .../dji/sdk/mqtt/status/StatusSubscribe.java | 39 + .../sdk/mqtt/status/TopicStatusRequest.java | 84 ++ .../sdk/mqtt/status/TopicStatusResponse.java | 73 ++ .../com/dji/sdk/swagger/SwaggerConfig.java | 56 + .../com/dji/sdk/websocket/BizCodeEnum.java | 42 + .../ConcurrentWebSocketSession.java | 2 +- .../websocket/WebSocketConfiguration.java} | 17 +- .../websocket/WebSocketDefaultFactory.java | 20 + .../websocket/WebSocketDefaultHandler.java | 40 + .../websocket/WebSocketMessageResponse.java | 83 ++ .../websocket/api/WebSocketMessageSend.java | 64 + src/main/resources/application.yml | 15 +- src/main/resources/image/1.png | Bin 0 -> 29224 bytes src/main/resources/image/2.png | Bin 0 -> 146952 bytes src/main/resources/image/3.png | Bin 0 -> 31103 bytes src/main/resources/image/4.png | Bin 0 -> 199623 bytes src/main/resources/image/5.png | Bin 0 -> 51278 bytes src/main/resources/image/6.png | Bin 0 -> 228475 bytes src/main/resources/image/7.png | Bin 0 -> 185000 bytes 726 files changed, 31400 insertions(+), 8630 deletions(-) delete mode 100644 src/main/java/com/dji/sample/common/error/LiveErrorEnum.java delete mode 100644 src/main/java/com/dji/sample/common/model/Pagination.java delete mode 100644 src/main/java/com/dji/sample/common/model/PaginationData.java delete mode 100644 src/main/java/com/dji/sample/common/model/ResponseResult.java create mode 100644 src/main/java/com/dji/sample/common/util/SpringBeanUtilsTest.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/config/MqttOutboundConfiguration.java rename src/main/java/com/dji/sample/component/mqtt/config/{MqttConfiguration.java => MqttPropertyConfiguration.java} (86%) delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/AbstractStateTopicHandler.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/EventsRouter.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateDefaultHandler.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateDeviceBasicHandler.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateFirmwareVersionHandler.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateLiveCapacityHandler.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StateRouter.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/handler/StatusRouter.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/Chan.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/CommonTopicResponse.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/ConfigScopeEnum.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/ErrorInfoReply.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/EventsMethodEnum.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/EventsOutputProgressReceiver.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/OutputProgressReceiver.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/RequestsReply.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/ServiceReply.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/SetReply.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/model/StateDataEnum.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java delete mode 100644 src/main/java/com/dji/sample/component/mqtt/service/impl/MqttTopicServiceImpl.java create mode 100644 src/main/java/com/dji/sample/component/websocket/config/MyConcurrentWebSocketSession.java rename src/main/java/com/dji/sample/component/websocket/config/{WebSocketDefaultFactory.java => MyWebSocketFactory.java} (67%) rename src/main/java/com/dji/sample/component/websocket/config/{WebSocketDefaultHandler.java => MyWebSocketHandler.java} (86%) delete mode 100644 src/main/java/com/dji/sample/component/websocket/model/CustomWebSocketMessage.java rename src/main/java/com/dji/sample/component/websocket/service/{ISendMessageService.java => IWebSocketMessageService.java} (62%) rename src/main/java/com/dji/sample/component/websocket/service/impl/{SendMessageServiceImpl.java => WebSocketMessageServiceImpl.java} (72%) delete mode 100644 src/main/java/com/dji/sample/configuration/mvc/GetSnakeDataBinder.java create mode 100644 src/main/java/com/dji/sample/control/model/dto/AirConditionerMode.java delete mode 100644 src/main/java/com/dji/sample/control/model/dto/DrcModeDTO.java delete mode 100644 src/main/java/com/dji/sample/control/model/dto/DrcModeReasonReceiver.java delete mode 100644 src/main/java/com/dji/sample/control/model/dto/DrcStatusNotifyReceiver.java delete mode 100644 src/main/java/com/dji/sample/control/model/dto/FlyToProgressReceiver.java delete mode 100644 src/main/java/com/dji/sample/control/model/dto/MqttBrokerDTO.java delete mode 100644 src/main/java/com/dji/sample/control/model/dto/PointDTO.java create mode 100644 src/main/java/com/dji/sample/control/model/dto/ReturnHomeCancelState.java delete mode 100644 src/main/java/com/dji/sample/control/model/dto/TakeoffProgressReceiver.java delete mode 100644 src/main/java/com/dji/sample/control/model/enums/BatteryStoreModeEnum.java delete mode 100644 src/main/java/com/dji/sample/control/model/enums/CameraStateEnum.java delete mode 100644 src/main/java/com/dji/sample/control/model/enums/LinkWorkModeEnum.java create mode 100644 src/main/java/com/dji/sample/control/service/impl/SDKControlService.java create mode 100644 src/main/java/com/dji/sample/control/service/impl/SDKRemoteDebug.java delete mode 100644 src/main/java/com/dji/sample/manage/model/dto/DeviceModelDTO.java create mode 100644 src/main/java/com/dji/sample/manage/model/dto/DevicePayloadReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/dto/IconUrlDTO.java rename src/main/java/com/dji/sample/manage/model/{receiver/LogsFileUpload.java => dto/LogsFileUploadDTO.java} (64%) rename src/main/java/com/dji/sample/manage/model/{receiver/LogsFileUploadList.java => dto/LogsFileUploadListDTO.java} (69%) delete mode 100644 src/main/java/com/dji/sample/manage/model/dto/TelemetryDeviceDTO.java delete mode 100644 src/main/java/com/dji/sample/manage/model/dto/TopologyDTO.java delete mode 100644 src/main/java/com/dji/sample/manage/model/enums/ControlSourceEnum.java create mode 100644 src/main/java/com/dji/sample/manage/model/enums/CustomizeConfigScopeEnum.java delete mode 100644 src/main/java/com/dji/sample/manage/model/enums/DeviceDomainEnum.java delete mode 100644 src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java delete mode 100644 src/main/java/com/dji/sample/manage/model/enums/DockDrcStateEnum.java delete mode 100644 src/main/java/com/dji/sample/manage/model/enums/DroneRcLostActionEnum.java rename src/main/java/com/dji/sample/manage/model/enums/{WaylineRcLostActionEnum.java => ExitWaylineWhenRcLostActionEnum.java} (59%) delete mode 100644 src/main/java/com/dji/sample/manage/model/enums/HmsEnum.java delete mode 100644 src/main/java/com/dji/sample/manage/model/enums/LogsFileMethodEnum.java create mode 100644 src/main/java/com/dji/sample/manage/model/enums/PropertySetFieldEnum.java create mode 100644 src/main/java/com/dji/sample/manage/model/param/DeviceLogsGetParam.java delete mode 100644 src/main/java/com/dji/sample/manage/model/param/DeviceOtaCreateParam.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/BatteryReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/BatteryStateReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/BindDeviceReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/BindStatusReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DeviceHmsReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DevicePayloadReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DockMediaFileDetailReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DockSubDeviceReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/DroneChargeStateReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/FirmwareProgressExtReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/HmsArgsReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/LiveStatusReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/LiveviewWorldRegionReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/LogsExtFileReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/LogsFile.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/LogsProgressReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OrganizationGetReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OsdCameraReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OsdGatewayReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OsdPayloadReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/OutputLogsExtReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/PositionStateReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/RequestConfigReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/model/receiver/StorageReceiver.java delete mode 100644 src/main/java/com/dji/sample/manage/service/ITSAService.java delete mode 100644 src/main/java/com/dji/sample/manage/service/impl/AbstractTSAService.java delete mode 100644 src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java delete mode 100644 src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java delete mode 100644 src/main/java/com/dji/sample/manage/service/impl/GatewayOSDServiceImpl.java create mode 100644 src/main/java/com/dji/sample/manage/service/impl/SDKDeviceService.java create mode 100644 src/main/java/com/dji/sample/manage/service/impl/SDKLivestreamService.java create mode 100644 src/main/java/com/dji/sample/manage/service/impl/SDKLogService.java create mode 100644 src/main/java/com/dji/sample/manage/service/impl/SDKOrganizationService.java create mode 100644 src/main/java/com/dji/sample/manage/service/impl/SDKPropertySetService.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ContentPropertyDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ElementCoordinateDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ElementCreateDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ElementLineStringDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ElementPointDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ElementPolygonDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ElementResourceDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ElementType.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ElementUpdateDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/GroupDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/ResourceContentDTO.java delete mode 100644 src/main/java/com/dji/sample/map/model/dto/WebSocketElementDelDTO.java delete mode 100644 src/main/java/com/dji/sample/media/model/CredentialsDTO.java delete mode 100644 src/main/java/com/dji/sample/media/model/FileExtensionDTO.java delete mode 100644 src/main/java/com/dji/sample/media/model/FileMetadataDTO.java delete mode 100644 src/main/java/com/dji/sample/media/model/FileUploadCallback.java delete mode 100644 src/main/java/com/dji/sample/media/model/FileUploadDTO.java delete mode 100644 src/main/java/com/dji/sample/media/model/PositionDTO.java delete mode 100644 src/main/java/com/dji/sample/media/model/StsCredentialsDTO.java create mode 100644 src/main/java/com/dji/sample/media/service/IMediaRedisService.java create mode 100644 src/main/java/com/dji/sample/media/service/impl/MediaRedisServiceImpl.java rename src/main/java/com/dji/sample/wayline/model/dto/{WaylineJobKey.java => ConditionalWaylineJobKey.java} (55%) rename src/main/java/com/dji/sample/wayline/model/dto/{WaylineTaskProgressExt.java => FlighttaskProgressExt.java} (87%) rename src/main/java/com/dji/sample/wayline/model/dto/{WaylineTaskProgress.java => FlighttaskProgressProgress.java} (82%) delete mode 100644 src/main/java/com/dji/sample/wayline/model/dto/WaylineFileUploadDTO.java delete mode 100644 src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java create mode 100644 src/main/java/com/dji/sample/wayline/service/impl/SDKWaylineService.java create mode 100644 src/main/java/com/dji/sdk/README.md create mode 100644 src/main/java/com/dji/sdk/annotations/CloudSDKVersion.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/config/ConfigScopeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/config/ConfigTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/config/ProductConfigResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/config/RequestsConfigRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/config/api/AbstractConfigService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/CameraAimRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/CameraFocalLengthSetRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/CameraModeSwitchRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/CameraPhotoTakeRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/CameraRecordingStartRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/CameraRecordingStopRequest.java rename src/main/java/com/dji/{sample/control/model/enums => sdk/cloudapi/control}/CameraTypeEnum.java (55%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/CommanderFlightModeEnum.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/control/CommanderModeLostActionEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/ControlErrorCodeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/ControlMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/DelayInfoPush.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/DrcModeEnterRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/DrcModeMqttBroker.java rename src/main/java/com/dji/{sample/control/model/enums => sdk/cloudapi/control}/DrcStatusErrorEnum.java (72%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/DrcStatusNotify.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/DroneControlRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/DroneControlResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/FlyToPointProgress.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/FlyToPointRequest.java rename src/main/java/com/dji/{sample/control/model/enums => sdk/cloudapi/control}/FlyToStatusEnum.java (60%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/GimbalResetModeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/GimbalResetRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/HeartBeatRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/HsiInfoPush.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/JoystickInvalidNotify.java rename src/main/java/com/dji/{sample/control/model/enums/DrcModeReasonEnum.java => sdk/cloudapi/control/JoystickInvalidReasonEnum.java} (57%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/LiveviewDelay.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/OsdInfoPush.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/PayloadAuthorityGrabRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/PayloadControlMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/Point.java rename src/main/java/com/dji/{sample/control/model/enums => sdk/cloudapi/control}/TakeoffStatusEnum.java (74%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointProgress.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/ZoomCameraTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/control/api/AbstractControlService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/AirConditionerModeSwitchActionEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/AirConditionerModeSwitchRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/AlarmStateSwitchRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/BatteryMaintenanceSwitchRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/BatteryStoreModeSwitchRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/DebugErrorCodeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/DebugMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugProgress.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugProgressData.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStatusEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStepKeyEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/SdrWorkmodeSwitchRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/debug/api/AbstractDebugService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/AirConditioner.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/AirConditionerStateEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/AlternateLandPoint.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/BackupBattery.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/Battery.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/BatteryIndexEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/BatteryStoreModeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/CameraModeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/CameraStateEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/ControlSourceEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/CoverStateEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DeviceDomainEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DeviceEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DeviceModelEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DeviceOsdHost.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DeviceOsdWsResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DeviceSubTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DeviceTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockDistanceLimitStatus.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockDroneControlSource.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockDronePayload.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockFirmwareVersion.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockLiveErrorStatus.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockLiveStatus.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockLiveStatusData.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockMaintainStatus.java rename src/main/java/com/dji/{sample/manage/model/enums => sdk/cloudapi/device}/DockModeCodeEnum.java (51%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockPayload.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockPayloadControlSource.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockPositionState.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockSubDevice.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DockWpmzVersion.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DrcStateEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DroneBattery.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DroneBatteryMaintenance.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DroneBatteryMaintenanceInfo.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DroneChargeState.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DroneMaintainStatus.java rename src/main/java/com/dji/{sample/manage/model/enums/DeviceModeCodeEnum.java => sdk/cloudapi/device/DroneModeCodeEnum.java} (61%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/DronePositionState.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/device/ExitWaylineWhenRcLostEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/FlighttaskStepCodeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/GearEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/LinkWorkModeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/LiveviewWorldRegion.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/MaintainTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/MeasureTargetStateEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/MediaFileDetail.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/ModeCodeReasonEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/NetworkState.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/NetworkStateQualityEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/NetworkStateTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/ObstacleAvoidance.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/OsdCamera.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/OsdDock.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/OsdDockDrone.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/OsdDockMaintainStatus.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/OsdDroneMaintainStatus.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/OsdRcDrone.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/OsdRemoteControl.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/PayloadFirmwareVersion.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/PayloadIndex.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/PayloadModelEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/PayloadPositionEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/PositionFixedEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/PutterStateEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RainfallEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RcDistanceLimitStatus.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RcDroneControlSource.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RcDronePayload.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RcFirmwareVersion.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RcLiveStatus.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RcLiveStatusData.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RcLostActionEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/RcPayloadControlSource.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/SmartTrackPoint.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/Storage.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/SwitchActionEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/ThermalGainModeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/ThermalPaletteStyleEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/TrackTargetModeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/UpdateTopo.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/UpdateTopoSubDevice.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/VideoId.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/WindDirectionEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/WirelessLink.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/device/api/AbstractDeviceService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareErrorCodeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareUpgradeTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateDevice.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgress.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressData.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressExt.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressStatusEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressStepEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/firmware/api/AbstractFirmwareService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/DeviceHms.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/DeviceHmsArgs.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/Hms.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsBatteryIndexEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsChargingRodIndexEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsDockCoverIndexEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsFaqIdEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsFormatKeyEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsInTheSkyEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsLevelEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsMessageLanguageEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/HmsModuleEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/hms/api/AbstractHmsService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacity.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityCamera.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityDevice.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityVideo.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/DockLivestreamAbilityUpdate.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/LensChangeVideoTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/LiveErrorCodeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/LiveLensChangeRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/LiveSetQualityRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/LiveStartPushRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/LiveStopPushRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/LiveStreamMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacity.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityCamera.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityDevice.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityVideo.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/RcLivestreamAbilityUpdate.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/UrlTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/VideoQualityEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/VideoTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/livestream/api/AbstractLivestreamService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadListFile.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadListRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadListResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgress.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgressExt.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgressFile.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartFile.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartParam.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadStatusEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadUpdateRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/FileUploadUpdateStatusEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/LogErrorCodeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/LogFileIndex.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/LogFileProgress.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/LogMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/LogModuleEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/log/api/AbstractLogService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/CreateMapElementRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/CreateMapElementResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementContent.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementCoordinate.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementGeometryType.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementLineStringGeometry.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementPointGeometry.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementPolygonGeometry.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementProperty.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementResource.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/ElementResourceTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/GetMapElementsResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/GroupTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/MapElementCreateWsResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/MapElementDeleteWsResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/MapElementUpdateWsResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/MapGroupElement.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/MapGroupRefreshWsResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/UpdateMapElementRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/map/api/IHttpMapService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/FastUploadExtension.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/FileUploadCallback.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/FileUploadCallbackFile.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/FolderUploadCallbackRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/GetFileFingerprintRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/GetFileFingerprintResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/HighestPriorityUploadFlightTaskMedia.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/MediaFastUploadRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/MediaFileExtension.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/MediaFileMetadata.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/MediaMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/MediaSubFileTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/MediaUploadCallbackRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/Position.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/StorageConfigGet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/StorageConfigGetModuleEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/UploadCallbackFileExtension.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/UploadCallbackFileMetadata.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/UploadFlighttaskMediaPrioritize.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/api/AbstractMediaService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/media/api/IHttpMediaService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/AirportBindStatusRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/AirportBindStatusResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationBindRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationBindResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationGetRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationGetResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/BindStatusRequestDevice.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/BindStatusResponseDevice.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/OrganizationBindDevice.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/OrganizationBindInfo.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/organization/api/AbstractOrganizationService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/DistanceLimitStatusData.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/DistanceLimitStatusSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/ExitWaylineWhenRcLostSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/HeightLimitSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/NightLightsStateSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/ObstacleAvoidanceSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/PropertySetEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/RcLostActionSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/RthAltitudeSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/ThermalCurrentPaletteStyleSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/ThermalGainModeSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermLowerLimitSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermStateSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermUpperLimitSet.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/property/api/AbstractPropertyService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/storage/CredentialsToken.java rename src/main/java/com/dji/{sample/component/oss/model/enums => sdk/cloudapi/storage}/OssTypeEnum.java (56%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/storage/StsCredentialsResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/storage/api/IHttpStorageService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/tsa/DeviceIconUrl.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/tsa/DeviceTopology.java rename src/main/java/com/dji/{sample/manage/model/enums => sdk/cloudapi/tsa}/IconUrlEnum.java (66%) create mode 100644 src/main/java/com/dji/sdk/cloudapi/tsa/TopologyDeviceModel.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/tsa/TopologyList.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/tsa/TopologyResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/tsa/api/IHttpTsaService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/BreakpointStateEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/DeviceExitHomingNotify.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/ExecutableConditions.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/ExecutionStepEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/ExitingRTHActionEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/ExitingRTHReasonEnum.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakPoint.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakReasonEnum.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskCreateFile.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskCreateRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskExecuteRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskFile.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskPrepareRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgress.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgressData.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgressExt.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskReady.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskResourceGetRequest.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskResourceGetResponse.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskStatusEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskUndoRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/GetWaylineListRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/GetWaylineListResponse.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/OutOfControlActionEnum.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/ReadyConditions.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/RthModeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/SimulateMission.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/SimulateSwitchEnum.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/TaskTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/WaylineErrorCodeEnum.java create mode 100755 src/main/java/com/dji/sdk/cloudapi/wayline/WaylineMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/WaylineTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/WaylineUploadCallbackMetadata.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/WaylineUploadCallbackRequest.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/api/AbstractWaylineService.java create mode 100644 src/main/java/com/dji/sdk/cloudapi/wayline/api/IHttpWaylineService.java create mode 100644 src/main/java/com/dji/sdk/common/BaseModel.java create mode 100644 src/main/java/com/dji/sdk/common/CloudSDKVersionEnum.java create mode 100644 src/main/java/com/dji/sdk/common/Common.java create mode 100644 src/main/java/com/dji/sdk/common/CommonErrorEnum.java create mode 100644 src/main/java/com/dji/sdk/common/DockThingVersionEnum.java create mode 100644 src/main/java/com/dji/sdk/common/DroneThingVersionEnum.java create mode 100644 src/main/java/com/dji/sdk/common/ErrorCodeSourceEnum.java create mode 100644 src/main/java/com/dji/sdk/common/GatewayManager.java create mode 100644 src/main/java/com/dji/sdk/common/GatewayThingVersion.java create mode 100644 src/main/java/com/dji/sdk/common/GatewayTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/common/HttpResultResponse.java rename src/main/java/com/dji/{sample/common/error => sdk/common}/IErrorInfo.java (72%) create mode 100644 src/main/java/com/dji/sdk/common/Pagination.java create mode 100644 src/main/java/com/dji/sdk/common/PaginationData.java create mode 100644 src/main/java/com/dji/sdk/common/RcThingVersionEnum.java create mode 100644 src/main/java/com/dji/sdk/common/SDKManager.java rename src/main/java/com/dji/{sample/common/util => sdk/common}/SpringBeanUtils.java (95%) create mode 100644 src/main/java/com/dji/sdk/config/CloudSDKHandler.java create mode 100644 src/main/java/com/dji/sdk/config/CloudSDKMvcConfigurer.java rename src/main/java/com/dji/{sample/configuration/mvc => sdk/config}/GetSnakeArgumentProcessor.java (85%) create mode 100644 src/main/java/com/dji/sdk/config/GetSnakeDataBinder.java create mode 100644 src/main/java/com/dji/sdk/exception/CloudSDKErrorEnum.java create mode 100644 src/main/java/com/dji/sdk/exception/CloudSDKException.java create mode 100644 src/main/java/com/dji/sdk/exception/CloudSDKVersionException.java create mode 100644 src/main/java/com/dji/sdk/mqtt/Chan.java create mode 100644 src/main/java/com/dji/sdk/mqtt/ChannelName.java rename src/main/java/com/dji/{sample/component/mqtt/model/DeviceTopicEnum.java => sdk/mqtt/CloudApiTopicEnum.java} (62%) create mode 100644 src/main/java/com/dji/sdk/mqtt/CommonTopicRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/CommonTopicResponse.java rename src/main/java/com/dji/{sample/component/mqtt/service => sdk/mqtt}/IMqttMessageGateway.java (89%) rename src/main/java/com/dji/{sample/component/mqtt/service => sdk/mqtt}/IMqttTopicService.java (74%) rename src/main/java/com/dji/{sample/component/mqtt/handler => sdk/mqtt}/InboundMessageRouter.java (75%) rename src/main/java/com/dji/{sample/component/mqtt/config/MqttInboundConfiguration.java => sdk/mqtt/MqttConfiguration.java} (58%) create mode 100644 src/main/java/com/dji/sdk/mqtt/MqttGatewayPublish.java create mode 100644 src/main/java/com/dji/sdk/mqtt/MqttReply.java create mode 100644 src/main/java/com/dji/sdk/mqtt/MqttReplyHandler.java create mode 100644 src/main/java/com/dji/sdk/mqtt/MqttTopicServiceImpl.java rename src/main/java/com/dji/{sample/component/mqtt/model => sdk/mqtt}/TopicConst.java (95%) create mode 100644 src/main/java/com/dji/sdk/mqtt/drc/DrcDownPublish.java create mode 100644 src/main/java/com/dji/sdk/mqtt/drc/DrcUpData.java create mode 100644 src/main/java/com/dji/sdk/mqtt/drc/DrcUpMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/mqtt/drc/DrcUpRouter.java create mode 100644 src/main/java/com/dji/sdk/mqtt/drc/DrcUpSubscribe.java create mode 100644 src/main/java/com/dji/sdk/mqtt/drc/TopicDrcRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/drc/TopicDrcResponse.java create mode 100644 src/main/java/com/dji/sdk/mqtt/events/EventsDataRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/events/EventsErrorCode.java create mode 100644 src/main/java/com/dji/sdk/mqtt/events/EventsMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/mqtt/events/EventsRouter.java create mode 100644 src/main/java/com/dji/sdk/mqtt/events/EventsSubscribe.java create mode 100644 src/main/java/com/dji/sdk/mqtt/events/IEventsErrorCode.java create mode 100644 src/main/java/com/dji/sdk/mqtt/events/TopicEventsRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/events/TopicEventsResponse.java create mode 100644 src/main/java/com/dji/sdk/mqtt/osd/OsdDeviceTypeEnum.java create mode 100644 src/main/java/com/dji/sdk/mqtt/osd/OsdRouter.java create mode 100644 src/main/java/com/dji/sdk/mqtt/osd/OsdSubscribe.java create mode 100644 src/main/java/com/dji/sdk/mqtt/osd/TopicOsdRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/property/PropertySetPublish.java create mode 100644 src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyHandler.java create mode 100644 src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyResultEnum.java create mode 100644 src/main/java/com/dji/sdk/mqtt/property/PropertySetSubscribe.java create mode 100644 src/main/java/com/dji/sdk/mqtt/property/TopicPropertySetRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/property/TopicPropertySetResponse.java create mode 100644 src/main/java/com/dji/sdk/mqtt/requests/RequestsMethodEnum.java create mode 100644 src/main/java/com/dji/sdk/mqtt/requests/RequestsRouter.java create mode 100644 src/main/java/com/dji/sdk/mqtt/requests/RequestsSubscribe.java create mode 100644 src/main/java/com/dji/sdk/mqtt/requests/TopicRequestsRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/requests/TopicRequestsResponse.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/IServicesErrorCode.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/ServicesErrorCode.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/ServicesPublish.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/ServicesReplyData.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/ServicesReplyHandler.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/ServicesReplyReceiver.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/ServicesSubscribe.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/TopicServicesRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/services/TopicServicesResponse.java create mode 100644 src/main/java/com/dji/sdk/mqtt/state/DockStateDataKeyEnum.java create mode 100644 src/main/java/com/dji/sdk/mqtt/state/RcStateDataKeyEnum.java create mode 100644 src/main/java/com/dji/sdk/mqtt/state/StateDataKeyEnum.java create mode 100644 src/main/java/com/dji/sdk/mqtt/state/StateRouter.java create mode 100644 src/main/java/com/dji/sdk/mqtt/state/StateSubscribe.java create mode 100644 src/main/java/com/dji/sdk/mqtt/state/TopicStateRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/status/StatusRouter.java create mode 100644 src/main/java/com/dji/sdk/mqtt/status/StatusSubscribe.java create mode 100644 src/main/java/com/dji/sdk/mqtt/status/TopicStatusRequest.java create mode 100644 src/main/java/com/dji/sdk/mqtt/status/TopicStatusResponse.java create mode 100644 src/main/java/com/dji/sdk/swagger/SwaggerConfig.java create mode 100644 src/main/java/com/dji/sdk/websocket/BizCodeEnum.java rename src/main/java/com/dji/{sample/component/websocket/config => sdk/websocket}/ConcurrentWebSocketSession.java (93%) rename src/main/java/com/dji/{sample/component/websocket/config/WebSocketMessageConfiguration.java => sdk/websocket/WebSocketConfiguration.java} (62%) create mode 100644 src/main/java/com/dji/sdk/websocket/WebSocketDefaultFactory.java create mode 100644 src/main/java/com/dji/sdk/websocket/WebSocketDefaultHandler.java create mode 100644 src/main/java/com/dji/sdk/websocket/WebSocketMessageResponse.java create mode 100644 src/main/java/com/dji/sdk/websocket/api/WebSocketMessageSend.java create mode 100644 src/main/resources/image/1.png create mode 100644 src/main/resources/image/2.png create mode 100644 src/main/resources/image/3.png create mode 100644 src/main/resources/image/4.png create mode 100644 src/main/resources/image/5.png create mode 100644 src/main/resources/image/6.png create mode 100644 src/main/resources/image/7.png diff --git a/pom.xml b/pom.xml index 9bc22a3..249aa19 100644 --- a/pom.xml +++ b/pom.xml @@ -1,85 +1,86 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.4.11 - - - - com.dji - cloud-api-sample - 1.5.0 - cloud-api-sample - - - 11 - 3.4.2 - 1.2.6 - 3.12.1 - 5.5.5 - 8.3.7 - 4.9.1 - 3.1.0 - 3.12.0 - 1.1.1 - 2.3.3 - 2.15.0 - 2.3.0 - - - - - org.springframework.boot - spring-boot-starter-web - - - - mysql - mysql-connector-java - - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-test - test - - - - com.baomidou - mybatis-plus-boot-starter - ${mybatis-plus.version} - - - - com.alibaba - druid-spring-boot-starter - ${druid.version} - - - - com.auth0 - java-jwt - ${jwt.version} - - - - org.springframework.integration - spring-integration-mqtt - ${mqtt.version} - - - - org.springframework.boot - spring-boot-starter-websocket - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.12 + + + + com.dji + cloud-api-sample + 1.7.0 + cloud-api-sample + + + 11 + 3.4.2 + 1.2.6 + 3.12.1 + 5.5.5 + 8.3.7 + 4.9.1 + 3.1.0 + 3.12.0 + 1.1.1 + 2.3.3 + 2.15.0 + 2.3.0 + + + + + org.springframework.boot + spring-boot-starter-web + + + + mysql + mysql-connector-java + 8.0.31 + + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + com.auth0 + java-jwt + ${jwt.version} + + + + org.springframework.integration + spring-integration-mqtt + ${mqtt.version} + + + + org.springframework.boot + spring-boot-starter-websocket + org.jetbrains annotations @@ -87,115 +88,121 @@ compile - - io.minio - minio - ${minio.version} - - - - com.squareup.okhttp3 - okhttp - ${okhttp3.version} - - - - com.aliyun - aliyun-java-sdk-sts - ${aliyun-sdk-sts.version} - - - - com.aliyun.oss - aliyun-sdk-oss - ${aliyun-oss.version} - - - - javax.xml.bind - jaxb-api - - - javax.activation - activation - ${javax-activation.version} - - - - org.glassfish.jaxb - jaxb-runtime - ${glassfish-jaxb.version} - - - - org.springframework.boot - spring-boot-starter-data-redis - - - org.apache.commons - commons-pool2 - - - - com.amazonaws - aws-java-sdk-s3 - 1.12.261 - - - com.amazonaws - aws-java-sdk-sts - 1.12.261 - - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - - - - org.springframework.boot - spring-boot-starter-aop - - - - org.dom4j - dom4j - 2.1.3 - - - - jaxen - jaxen - - - - org.bouncycastle - bcpkix-jdk15on - 1.69 - - - - org.springframework.boot - spring-boot-starter-validation - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - + + io.minio + minio + ${minio.version} + + + + com.squareup.okhttp3 + okhttp + ${okhttp3.version} + + + + com.aliyun + aliyun-java-sdk-sts + ${aliyun-sdk-sts.version} + + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun-oss.version} + + + + javax.xml.bind + jaxb-api + + + javax.activation + activation + ${javax-activation.version} + + + + org.glassfish.jaxb + jaxb-runtime + ${glassfish-jaxb.version} + + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.apache.commons + commons-pool2 + + + + com.amazonaws + aws-java-sdk-s3 + 1.12.261 + + + com.amazonaws + aws-java-sdk-sts + 1.12.261 + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.dom4j + dom4j + 2.1.3 + + + + jaxen + jaxen + + + + org.bouncycastle + bcpkix-jdk15on + 1.69 + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.springdoc + springdoc-openapi-ui + 1.7.0 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + diff --git a/sql/cloud_sample.sql b/sql/cloud_sample.sql index ef864d9..18b9c78 100644 --- a/sql/cloud_sample.sql +++ b/sql/cloud_sample.sql @@ -21,7 +21,7 @@ CREATE TABLE `logs_file` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `file_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'uuid', `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The name of the file in the bucket.', - `size` int NOT NULL DEFAULT '0' COMMENT 'file size', + `size` bigint NOT NULL DEFAULT '0' COMMENT 'file size', `logs_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The logs_id in the manage_device_logs table.', `device_sn` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The sn of the device.', `fingerprint` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'file fingerprint', @@ -152,7 +152,7 @@ CREATE TABLE `manage_device_firmware` ( `file_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined' COMMENT 'The file name of the firmware package, including the file suffix', `firmware_version` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'It needs to be formatted according to the official firmware version. 00.00.0000', `object_key` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'The object key of the firmware package in the bucket.', - `file_size` int NOT NULL COMMENT 'The size of the firmware package.', + `file_size` bigint NOT NULL COMMENT 'The size of the firmware package.', `file_md5` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The md5 of the firmware package.', `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `release_note` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The release note of the firmware package.', @@ -393,7 +393,7 @@ CREATE TABLE `media_file` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `file_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'uuid', `file_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The original name of the file.', - `file_path` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The path of the file.', + `file_path` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The path of the file.', `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The workspace to which the file belongs.', `fingerprint` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT 'The fingerprint of the file. This property exists only for media files uploaded by Pilot.', `tinny_fingerprint` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT 'The tiny fingerprint of the file. This property exists only for media files uploaded by Pilot.', diff --git a/src/main/java/com/dji/sample/CloudApiSampleApplication.java b/src/main/java/com/dji/sample/CloudApiSampleApplication.java index a3619ab..b36e909 100644 --- a/src/main/java/com/dji/sample/CloudApiSampleApplication.java +++ b/src/main/java/com/dji/sample/CloudApiSampleApplication.java @@ -3,12 +3,13 @@ package com.dji.sample; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableScheduling; @MapperScan("com.dji.sample.*.dao") @SpringBootApplication @EnableScheduling -//@EnableConfigurationProperties(OssConfiguration.class) +@ComponentScan("com.dji") 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 1ab3b32..73043a6 100644 --- a/src/main/java/com/dji/sample/common/error/CommonErrorEnum.java +++ b/src/main/java/com/dji/sample/common/error/CommonErrorEnum.java @@ -1,5 +1,7 @@ package com.dji.sample.common.error; +import com.dji.sdk.common.IErrorInfo; + /** * @author sean.zhou * @version 0.1 @@ -43,12 +45,13 @@ public enum CommonErrorEnum implements IErrorInfo { } @Override - public String getErrorMsg() { + public String getMessage() { return this.msg; } @Override - public Integer getErrorCode() { + public Integer getCode() { return this.code; } + } diff --git a/src/main/java/com/dji/sample/common/error/LiveErrorEnum.java b/src/main/java/com/dji/sample/common/error/LiveErrorEnum.java deleted file mode 100644 index 6ba3f95..0000000 --- a/src/main/java/com/dji/sample/common/error/LiveErrorEnum.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.dji.sample.common.error; - -/** - * Live streaming related error codes. When on-demand via mqtt, - * it can be matched with the error code information replied by the pilot. - * @author sean.zhou - * @version 0.1 - * @date 2021/11/25 - */ -public enum LiveErrorEnum implements IErrorInfo { - - NO_AIRCRAFT(613001, "No aircraft."), - - NO_CAMERA(613002, "No camera."), - - LIVE_STREAM_ALREADY_STARTED(613003, "The camera has started live streaming."), - - FUNCTION_NOT_SUPPORT(613004, "The function is not supported."), - - STRATEGY_NOT_SUPPORT(613005, "The strategy is not supported."), - - NOT_IN_CAMERA_INTERFACE(613006, "The current app is not in the camera interface."), - - NO_FLIGHT_CONTROL(613007, "The remote control has no flight control rights and cannot respond to control commands"), - - NO_STREAM_DATA(613008, "The current app has no stream data."), - - TOO_FREQUENT(613009, "The operation is too frequent."), - - ENABLE_FAILED(613010, "Please check whether the live stream service is normal."), - - NO_LIVE_STREAM(613011, "There are no live stream currently."), - - SWITCH_NOT_SUPPORT(613012, "There is already another camera in the live stream. It's not support to switch the stream directly."), - - URL_TYPE_NOT_SUPPORTED(613013, "This url type is not supported."), - - ERROR_PARAMETERS(613014, "The live stream parameters are abnormal or incomplete."), - - NO_REPLY(613098, "No live reply received."), - - UNKNOWN(613099, "UNKNOWN"); - - - private String msg; - - private int code; - - LiveErrorEnum(int code, String msg) { - this.code = code; - this.msg = msg; - } - - @Override - public String getErrorMsg() { - return this.msg; - } - - @Override - public Integer getErrorCode() { - return this.code; - } - - /** - * Get the corresponding enumeration object based on the error code. - * @param code error code - * @return enumeration object - */ - public static LiveErrorEnum find(int code) { - final int MOD = 100_000; - for (LiveErrorEnum errorEnum : LiveErrorEnum.class.getEnumConstants()) { - if (errorEnum.code % MOD == code % MOD) { - return errorEnum; - } - } - return UNKNOWN; - } -} diff --git a/src/main/java/com/dji/sample/common/model/Pagination.java b/src/main/java/com/dji/sample/common/model/Pagination.java deleted file mode 100644 index 74d1e8a..0000000 --- a/src/main/java/com/dji/sample/common/model/Pagination.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.dji.sample.common.model; - -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import lombok.Data; - -/** - * Used for paging display in the wayline. These field names cannot be changed. - * Because they need to be the same as the pilot. - * @author sean - * @version 0.3 - * @date 2021/12/22 - */ -@Data -public class Pagination { - - /** - * The current page number. - */ - private long page; - - /** - * The amount of data displayed per page. - */ - private long pageSize; - - /** - * The total amount of all data. - */ - private long total; - - public Pagination(Page page) { - this.page = page.getCurrent(); - this.pageSize = page.getSize(); - this.total = page.getTotal(); - } -} diff --git a/src/main/java/com/dji/sample/common/model/PaginationData.java b/src/main/java/com/dji/sample/common/model/PaginationData.java deleted file mode 100644 index 95e2442..0000000 --- a/src/main/java/com/dji/sample/common/model/PaginationData.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dji.sample.common.model; - -import lombok.Data; - -import java.util.List; - -/** - * The format of the data response when a paginated display is required. - * @author sean - * @version 0.3 - * @date 2021/12/22 - */ -@Data -public class PaginationData { - - /** - * The collection in which the data list is stored. - */ - private List list; - - private Pagination pagination; - - public PaginationData(List list, Pagination pagination) { - this.list = list; - this.pagination = pagination; - } -} diff --git a/src/main/java/com/dji/sample/common/model/ResponseResult.java b/src/main/java/com/dji/sample/common/model/ResponseResult.java deleted file mode 100644 index 8ad8e1b..0000000 --- a/src/main/java/com/dji/sample/common/model/ResponseResult.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.dji.sample.common.model; - -import com.dji.sample.common.error.IErrorInfo; -import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.http.HttpStatus; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -@JsonInclude -public class ResponseResult { - - public static final int CODE_SUCCESS = 0; - public static final String MESSAGE_SUCCESS = "success"; - - private int code; - - private String message; - - private T data; - - public static ResponseResult success(T data) { - return ResponseResult.builder() - .code(CODE_SUCCESS) - .message(MESSAGE_SUCCESS) - .data(data) - .build(); - } - - public static ResponseResult success() { - return ResponseResult.builder() - .code(0) - .message(MESSAGE_SUCCESS) - .build(); - } - - public static ResponseResult error() { - return ResponseResult.builder() - .code(HttpStatus.INTERNAL_SERVER_ERROR.value()) - .message(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()) - .build(); - } - - public static ResponseResult error(String message) { - return ResponseResult.builder() - .code(HttpStatus.INTERNAL_SERVER_ERROR.value()) - .message(message) - .build(); - } - - public static ResponseResult error(int code, String message) { - return ResponseResult.builder() - .code(code) - .message(message) - .build(); - } - - public static ResponseResult error(IErrorInfo errorInfo) { - return ResponseResult.builder() - .code(errorInfo.getErrorCode()) - .message(errorInfo.getErrorMsg()) - .build(); - } -} 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 c04168e..d1e9c03 100644 --- a/src/main/java/com/dji/sample/common/util/JwtUtil.java +++ b/src/main/java/com/dji/sample/common/util/JwtUtil.java @@ -105,7 +105,7 @@ public class JwtUtil { } if (Objects.nonNull(age)) { - builder.withExpiresAt(new Date(now.getTime() + age * 1000)); + builder.withExpiresAt(new Date(now.getTime() + age)); } String token = builder diff --git a/src/main/java/com/dji/sample/common/util/SpringBeanUtilsTest.java b/src/main/java/com/dji/sample/common/util/SpringBeanUtilsTest.java new file mode 100644 index 0000000..cbe4ab1 --- /dev/null +++ b/src/main/java/com/dji/sample/common/util/SpringBeanUtilsTest.java @@ -0,0 +1,30 @@ +package com.dji.sample.common.util; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/10 + */ +@Component +public class SpringBeanUtilsTest implements ApplicationContextAware { + + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringBeanUtilsTest.applicationContext = applicationContext; + } + + public static T getBean(Class clazz) { + return applicationContext.getBean(clazz); + } + + public static Object getBean(String beanName) { + return applicationContext.getBean(beanName); + } +} diff --git a/src/main/java/com/dji/sample/component/ApplicationBootInitial.java b/src/main/java/com/dji/sample/component/ApplicationBootInitial.java index d99f58d..16a691c 100644 --- a/src/main/java/com/dji/sample/component/ApplicationBootInitial.java +++ b/src/main/java/com/dji/sample/component/ApplicationBootInitial.java @@ -2,11 +2,17 @@ package com.dji.sample.component; 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.IDeviceRedisService; import com.dji.sample.manage.service.IDeviceService; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.common.SDKManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; +import java.util.Optional; + /** * @author sean.zhou * @date 2021/11/24 @@ -18,6 +24,9 @@ public class ApplicationBootInitial implements CommandLineRunner { @Autowired private IDeviceService deviceService; + @Autowired + private IDeviceRedisService deviceRedisService; + /** * 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. @@ -29,7 +38,15 @@ public class ApplicationBootInitial implements CommandLineRunner { int start = RedisConst.DEVICE_ONLINE_PREFIX.length(); RedisOpsUtils.getAllKeys(RedisConst.DEVICE_ONLINE_PREFIX + "*") - .forEach(key -> deviceService.subscribeTopicOnline(key.substring(start))); + .stream() + .map(key -> key.substring(start)) + .map(deviceRedisService::getDeviceOnline) + .map(Optional::get) + .filter(device -> DeviceDomainEnum.DRONE != device.getDomain()) + .forEach(device -> deviceService.subDeviceOnlineSubscribeTopic( + SDKManager.registerDevice(device.getDeviceSn(), device.getChildDeviceSn(), device.getDomain(), + device.getType(), device.getSubType(), device.getThingVersion(), + deviceRedisService.getDeviceOnline(device.getChildDeviceSn()).map(DeviceDTO::getThingVersion).orElse(null)))); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/AuthInterceptor.java b/src/main/java/com/dji/sample/component/AuthInterceptor.java index 0d3b8f7..9933466 100644 --- a/src/main/java/com/dji/sample/component/AuthInterceptor.java +++ b/src/main/java/com/dji/sample/component/AuthInterceptor.java @@ -26,7 +26,7 @@ public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); - log.debug("request uri: {}", uri); + log.debug("request uri: {}, IP: {}", uri, request.getRemoteAddr()); // The options method is passed directly. if (HttpMethod.OPTIONS.matches(request.getMethod())) { response.setStatus(HttpStatus.OK.value()); @@ -36,7 +36,7 @@ public class AuthInterceptor implements HandlerInterceptor { // Check if the token exists. if (!StringUtils.hasText(token)) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); - log.error(CommonErrorEnum.NO_TOKEN.getErrorMsg()); + log.error(CommonErrorEnum.NO_TOKEN.getMessage()); return false; } diff --git a/src/main/java/com/dji/sample/component/GlobalExceptionHandler.java b/src/main/java/com/dji/sample/component/GlobalExceptionHandler.java index 0b6b6b0..f7e79c2 100644 --- a/src/main/java/com/dji/sample/component/GlobalExceptionHandler.java +++ b/src/main/java/com/dji/sample/component/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ package com.dji.sample.component; -import com.dji.sample.common.model.ResponseResult; +import com.dji.sdk.common.HttpResultResponse; import org.springframework.validation.BindException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; @@ -22,21 +22,21 @@ public class GlobalExceptionHandler { * @return */ @ExceptionHandler(Exception.class) - public ResponseResult exceptionHandler(Exception e) { + public HttpResultResponse exceptionHandler(Exception e) { e.printStackTrace(); - return ResponseResult.error(e.getLocalizedMessage()); + return HttpResultResponse.error(e.getLocalizedMessage()); } @ExceptionHandler(NullPointerException.class) - public ResponseResult nullPointerExceptionHandler(NullPointerException e) { + public HttpResultResponse nullPointerExceptionHandler(NullPointerException e) { e.printStackTrace(); - return ResponseResult.error("A null object appeared."); + return HttpResultResponse.error("A null object appeared."); } @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class}) - public ResponseResult methodArgumentNotValidExceptionHandler(BindException e) { + public HttpResultResponse methodArgumentNotValidExceptionHandler(BindException e) { e.printStackTrace(); - return ResponseResult.error(e.getFieldError().getField() + e.getFieldError().getDefaultMessage()); + return HttpResultResponse.error(e.getFieldError().getField() + e.getFieldError().getDefaultMessage()); } } diff --git a/src/main/java/com/dji/sample/component/GlobalScheduleService.java b/src/main/java/com/dji/sample/component/GlobalScheduleService.java index 7e77a34..fe4dfd2 100644 --- a/src/main/java/com/dji/sample/component/GlobalScheduleService.java +++ b/src/main/java/com/dji/sample/component/GlobalScheduleService.java @@ -1,12 +1,11 @@ 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 com.dji.sample.wayline.service.IWaylineJobService; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.mqtt.IMqttTopicService; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -31,8 +30,6 @@ public class GlobalScheduleService { @Autowired private IMqttTopicService topicService; - @Autowired - private IWaylineJobService waylineJobService; @Autowired private ObjectMapper mapper; /** @@ -46,14 +43,13 @@ public class GlobalScheduleService { long expire = RedisOpsUtils.getExpire(key); if (expire <= 30) { DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(key); - if (DeviceDomainEnum.SUB_DEVICE.getVal() == device.getDomain()) { + if (null == device) { + return; + } + if (DeviceDomainEnum.DRONE == device.getDomain()) { deviceService.subDeviceOffline(key.substring(start)); } else { - deviceService.unsubscribeTopicOffline(key.substring(start)); - deviceService.pushDeviceOfflineTopo(device.getWorkspaceId(), device.getDeviceSn()); - RedisOpsUtils.hashDel(RedisConst.LIVE_CAPACITY, new String[]{device.getDeviceSn()}); - RedisOpsUtils.del(RedisConst.HMS_PREFIX + device.getDeviceSn()); - RedisOpsUtils.del(RedisConst.OSD_PREFIX + device.getDeviceSn()); + deviceService.gatewayOffline(key.substring(start)); } RedisOpsUtils.del(key); } 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 8ff8629..aba87cb 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 @@ -1,6 +1,6 @@ package com.dji.sample.component.mqtt.config; -import com.dji.sample.component.mqtt.model.ChannelName; +import com.dji.sdk.mqtt.ChannelName; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -27,18 +27,13 @@ public class MqttMessageChannel { return new ExecutorChannel(threadPool); } - @Bean(name = ChannelName.INBOUND_STATUS) - public MessageChannel statusChannel() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_STATUS_ONLINE) - public MessageChannel statusOnlineChannel() { + @Bean(name = ChannelName.DEFAULT) + public MessageChannel defaultChannel() { return new DirectChannel(); } - @Bean(name = ChannelName.INBOUND_STATUS_OFFLINE) - public MessageChannel statusOffChannel() { + @Bean(name = ChannelName.INBOUND_STATUS) + public MessageChannel statusChannel() { return new DirectChannel(); } @@ -47,158 +42,34 @@ public class MqttMessageChannel { return new DirectChannel(); } - @Bean(name = ChannelName.INBOUND_STATE_BASIC) - public MessageChannel stateBasicChannel() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_STATE_PAYLOAD) - public MessageChannel statePayloadChannel() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_SERVICE_REPLY) + @Bean(name = ChannelName.INBOUND_SERVICES_REPLY) public MessageChannel serviceReplyChannel() { return new DirectChannel(); } - @Bean(name = ChannelName.INBOUND_STATE_CAPACITY) - public MessageChannel stateCapacityChannel() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE) - public MessageChannel statePayloadUpdateChannel() { - return new DirectChannel(); - } - @Bean(name = ChannelName.INBOUND_OSD) public MessageChannel osdChannel() { return new ExecutorChannel(threadPool); } - @Bean(name = ChannelName.DEFAULT) - public MessageChannel defaultChannel() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.OUTBOUND) - public MessageChannel outboundChannel() { - 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.OUTBOUND_EVENTS) - public MessageChannel eventsOutboundChannel() { - 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(); - } - - @Bean(name = ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS) - public MessageChannel eventsControlProgress() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_EVENTS_OTA_PROGRESS) - public MessageChannel eventsOtaProgress() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_PROGRESS) - public MessageChannel eventsFileUploadProgress() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET) - public MessageChannel requestsFlightTaskResourceGet() { - return new DirectChannel(); - } - @Bean(name = ChannelName.INBOUND_PROPERTY_SET_REPLY) public MessageChannel propertySetReply() { return new DirectChannel(); } - @Bean(name = ChannelName.INBOUND_REQUESTS_CONFIG) - public MessageChannel requestsConfig() { + @Bean(name = ChannelName.INBOUND_DRC_UP) + public MessageChannel drcUp() { return new DirectChannel(); } - @Bean(name = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA) - public MessageChannel eventsHighestPriorityUploadFlightTaskMedia() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_READY) - public MessageChannel eventsEventsFlightTaskReady() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS) - public MessageChannel eventsFlyToPointProgress() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS) - public MessageChannel eventsTakeoffToPointProgress() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_EVENTS_DRC_STATUS_NOTIFY) - public MessageChannel eventsDrcStatusNotify() { - return new DirectChannel(); - } - - @Bean(name = ChannelName.INBOUND_EVENTS_DRC_MODE_EXIT_NOTIFY) - public MessageChannel eventsDrcModeExitNotify() { - return new DirectChannel(); - } } diff --git a/src/main/java/com/dji/sample/component/mqtt/config/MqttOutboundConfiguration.java b/src/main/java/com/dji/sample/component/mqtt/config/MqttOutboundConfiguration.java deleted file mode 100644 index a376093..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/config/MqttOutboundConfiguration.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.dji.sample.component.mqtt.config; - -import com.dji.sample.component.mqtt.model.ChannelName; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.integration.mqtt.core.MqttPahoClientFactory; -import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; -import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; -import org.springframework.messaging.MessageHandler; - -/** - * Client configuration for outbound messages. - * @author sean.zhou - * @date 2021/11/10 - * @version 0.1 - */ -@Configuration -public class MqttOutboundConfiguration { - - @Autowired - private MqttConfiguration mqttConfiguration; - - @Autowired - private MqttPahoClientFactory mqttClientFactory; - - /** - * Clients of outbound message channels. - * @return - */ - @Bean - @ServiceActivator(inputChannel = ChannelName.OUTBOUND) - public MessageHandler mqttOutbound() { - MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler( - MqttConfiguration.getBasicClientOptions().getClientId() + "_producer_" + System.currentTimeMillis(), - mqttClientFactory); - DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter(); - // use byte types uniformly - converter.setPayloadAsBytes(true); - - messageHandler.setAsync(true); - messageHandler.setDefaultQos(0); - messageHandler.setConverter(converter); - return messageHandler; - } -} diff --git a/src/main/java/com/dji/sample/component/mqtt/config/MqttConfiguration.java b/src/main/java/com/dji/sample/component/mqtt/config/MqttPropertyConfiguration.java similarity index 86% rename from src/main/java/com/dji/sample/component/mqtt/config/MqttConfiguration.java rename to src/main/java/com/dji/sample/component/mqtt/config/MqttPropertyConfiguration.java index ba8d939..2f7dd86 100644 --- a/src/main/java/com/dji/sample/component/mqtt/config/MqttConfiguration.java +++ b/src/main/java/com/dji/sample/component/mqtt/config/MqttPropertyConfiguration.java @@ -5,7 +5,7 @@ import com.dji.sample.common.util.JwtUtil; import com.dji.sample.component.mqtt.model.MqttClientOptions; import com.dji.sample.component.mqtt.model.MqttProtocolEnum; import com.dji.sample.component.mqtt.model.MqttUseEnum; -import com.dji.sample.control.model.dto.MqttBrokerDTO; +import com.dji.sdk.cloudapi.control.DrcModeMqttBroker; import lombok.Data; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -26,12 +26,12 @@ import java.util.Map; @Configuration @Data @ConfigurationProperties -public class MqttConfiguration { +public class MqttPropertyConfiguration { private static Map mqtt; public void setMqtt(Map mqtt) { - MqttConfiguration.mqtt = mqtt; + MqttPropertyConfiguration.mqtt = mqtt; } /** @@ -79,7 +79,7 @@ public class MqttConfiguration { * @param map Custom data added in token. * @return */ - public static MqttBrokerDTO getMqttBrokerWithDrc(String clientId, String username, Long age, Map map) { + public static DrcModeMqttBroker getMqttBrokerWithDrc(String clientId, String username, Long age, Map map) { if (!mqtt.containsKey(MqttUseEnum.DRC)) { throw new RuntimeException("Please configure the drc link parameters of mqtt in the backend configuration file first."); } @@ -87,13 +87,13 @@ public class MqttConfiguration { String token = JwtUtil.createToken(map, age, algorithm, null, null); - return MqttBrokerDTO.builder() - .address(getMqttAddress(mqtt.get(MqttUseEnum.DRC))) - .username(username) - .clientId(clientId) - .expireTime(System.currentTimeMillis() / 1000 + age) - .password(token) - .build(); + return new DrcModeMqttBroker() + .setAddress(getMqttAddress(mqtt.get(MqttUseEnum.DRC))) + .setUsername(username) + .setClientId(clientId) + .setExpireTime(System.currentTimeMillis() / 1000 + age) + .setPassword(token) + .setEnableTls(false); } 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 deleted file mode 100644 index c3969f3..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/AbstractStateTopicHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -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 deleted file mode 100644 index 152ae81..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/EventsRouter.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.dji.sample.component.mqtt.handler; - -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.IMessageSenderService; -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.annotation.ServiceActivator; -import org.springframework.integration.dsl.IntegrationFlow; -import org.springframework.integration.dsl.IntegrationFlows; -import org.springframework.integration.mqtt.support.MqttHeaders; -import org.springframework.messaging.MessageHeaders; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Optional; - -/** - * @author sean - * @version 1.1 - * @date 2022/6/1 - */ -@Configuration -public class EventsRouter { - - @Autowired - private ObjectMapper mapper; - - @Autowired - private IMessageSenderService messageSenderService; - - @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 -> Arrays.stream(EventsMethodEnum.values()).forEach( - methodEnum -> mapping.channelMapping(methodEnum, methodEnum.getChannelName()))) - .get(); - } - - @ServiceActivator(inputChannel = ChannelName.OUTBOUND_EVENTS, outputChannel = ChannelName.OUTBOUND) - public void replyEventsOutbound(CommonTopicReceiver receiver, MessageHeaders headers) { - if (Optional.ofNullable(receiver).map(CommonTopicReceiver::getNeedReply).flatMap(val -> Optional.of(1 != val)).orElse(true)) { - return; - } - messageSenderService.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF, - CommonTopicResponse.builder() - .tid(receiver.getTid()) - .bid(receiver.getBid()) - .method(receiver.getMethod()) - .timestamp(System.currentTimeMillis()) - .data(RequestsReply.success()) - .build()); - - } - -} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java deleted file mode 100644 index 9b503eb..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/PropertySetReplyHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.dji.sample.component.mqtt.handler; - -import com.dji.sample.component.mqtt.model.Chan; -import com.dji.sample.component.mqtt.model.ChannelName; -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.messaging.Message; -import org.springframework.stereotype.Component; - -import java.io.IOException; - -/** - * @author sean - * @version 1.2 - * @date 2022/9/9 - */ -@Component -public class PropertySetReplyHandler { - - @Autowired - private ObjectMapper mapper; - - /** - * Handle the reply message from the pilot side to the on-demand video. - * @param message reply message - * @throws IOException - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_PROPERTY_SET_REPLY) - public void serviceReply(Message message) throws IOException { - byte[] payload = (byte[])message.getPayload(); - - CommonTopicReceiver receiver = mapper.readValue(payload, new TypeReference() {}); - Chan> chan = Chan.getInstance(); - // Put the message to the chan object. - chan.put(receiver); - } -} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java b/src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java deleted file mode 100644 index 95e0f7d..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/RequestsRouter.java +++ /dev/null @@ -1,45 +0,0 @@ -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; -import java.util.Arrays; - -/** - * @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 -> Arrays.stream(RequestsMethodEnum.values()).forEach( - methodEnum -> mapping.channelMapping(methodEnum, methodEnum.getChannelName()))) - .get(); - } -} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java deleted file mode 100644 index 8d8030d..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/ServicesReplyHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.dji.sample.component.mqtt.handler; - -import com.dji.sample.component.mqtt.model.Chan; -import com.dji.sample.component.mqtt.model.ChannelName; -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; -import com.dji.sample.component.mqtt.model.ServiceReply; -import com.dji.sample.manage.model.enums.LogsFileMethodEnum; -import com.dji.sample.manage.model.receiver.LogsFileUploadList; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.messaging.Message; -import org.springframework.stereotype.Component; - -import java.io.IOException; - -/** - * @author sean - * @version 1.2 - * @date 2022/9/9 - */ -@Component -public class ServicesReplyHandler { - - @Autowired - private ObjectMapper mapper; - - /** - * Handle the reply message from the pilot side to the on-demand video. - * @param message reply message - * @throws IOException - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_SERVICE_REPLY) - public void serviceReply(Message message) throws IOException { - byte[] payload = (byte[])message.getPayload(); - - CommonTopicReceiver receiver = mapper.readValue(payload, new TypeReference() {}); - ServiceReply reply; - if (LogsFileMethodEnum.FILE_UPLOAD_LIST.getMethod().equals(receiver.getMethod())) { - LogsFileUploadList list = mapper.convertValue(receiver.getData(), new TypeReference() {}); - reply = new ServiceReply(); - reply.setResult(list.getResult()); - reply.setOutput(list.getFiles()); - } else { - reply = mapper.convertValue(receiver.getData(), new TypeReference() {}); - } - receiver.setData(reply); - Chan> chan = Chan.getInstance(); - // Put the message to the chan object. - chan.put(receiver); - } -} diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/StateDefaultHandler.java b/src/main/java/com/dji/sample/component/mqtt/handler/StateDefaultHandler.java deleted file mode 100644 index a1d2624..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/StateDefaultHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index b85ebca..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/StateDeviceBasicHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -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 deleted file mode 100644 index aceeae4..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/StateFirmwareVersionHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -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 deleted file mode 100644 index dc593ab..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/StateLiveCapacityHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -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 deleted file mode 100644 index 4f0e1e5..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/StateRouter.java +++ /dev/null @@ -1,104 +0,0 @@ -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 deleted file mode 100644 index 0b34137..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/handler/StatusRouter.java +++ /dev/null @@ -1,67 +0,0 @@ -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 deleted file mode 100644 index f55212a..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/Chan.java +++ /dev/null @@ -1,45 +0,0 @@ -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_000L * 10_000; - - 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 deleted file mode 100644 index cf0ef71..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -/** - * The name of all channels. - * - * @author sean.zhou - * @date 2021/11/10 - * @version 0.1 - */ -public class ChannelName { - - public static final String INBOUND = "inbound"; - - public static final String INBOUND_STATUS = "inboundStatus"; - - public static final String INBOUND_STATUS_ROUTER = "inboundStatusRouter"; - - public static final String INBOUND_STATUS_ONLINE = "inboundStatusOnline"; - - public static final String INBOUND_STATUS_OFFLINE = "inboundStatusOffline"; - - public static final String INBOUND_STATE = "inboundState"; - - public static final String INBOUND_STATE_SPLITTER = "inboundStateSplitter"; - - public static final String INBOUND_STATE_ROUTER = "inboundStateRouter"; - - public static final String INBOUND_STATE_BASIC = "inboundStateBasic"; - - public static final String INBOUND_STATE_PAYLOAD = "inboundStatePayload"; - - public static final String INBOUND_STATE_PAYLOAD_UPDATE = "inboundStatePayloadUpdate"; - - public static final String INBOUND_STATE_CAPACITY = "inboundStateCapacity"; - - public static final String INBOUND_STATE_LIST = "inboundStateList"; - - public static final String INBOUND_SERVICE_REPLY = "inboundStateServiceReply"; - - public static final String INBOUND_OSD = "inboundOsd"; - - public static final String DEFAULT = "default"; - - public static final String OUTBOUND = "outbound"; - - 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 OUTBOUND_EVENTS = "outboundEvents"; - - 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"; - - public static final String INBOUND_EVENTS_CONTROL_PROGRESS = "inboundEventsControlProgress"; - - public static final String INBOUND_EVENTS_OTA_PROGRESS = "inboundEventsOtaProgress"; - - public static final String INBOUND_EVENTS_FILE_UPLOAD_PROGRESS = "inboundEventsFileUploadProgress"; - - public static final String INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET = "inboundEventsFlightTaskResourceGet"; - - public static final String INBOUND_PROPERTY_SET_REPLY = "inboundPropertySetReply"; - - public static final String INBOUND_REQUESTS_CONFIG = "inboundRequestsConfig"; - - public static final String INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA = "inboundEventsHighestPriorityUploadFlightTaskMedia"; - - public static final String INBOUND_EVENTS_FLIGHT_TASK_READY = "inboundEventsFlightTaskReady"; - - public static final String INBOUND_EVENTS_FLY_TO_POINT_PROGRESS = "inboundFlyToPointProgress"; - - public static final String INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS = "inboundTakeoffToPointProgress"; - - public static final String INBOUND_EVENTS_DRC_STATUS_NOTIFY = "inboundDrcStatusNotify"; - - public static final String INBOUND_EVENTS_DRC_MODE_EXIT_NOTIFY = "inboundDrcModeExitNotify"; -} 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 deleted file mode 100644 index 6090338..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -/** - * Unified topic receiving format. - * @author sean.zhou - * @date 2021/11/10 - * @version 0.1 - */ -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class CommonTopicReceiver { - - /** - * The command is sent and the response is matched by the tid and bid fields in the message, - * and the reply should keep the tid and bid the same. - */ - private String tid; - - private String bid; - - private String method; - - private Long timestamp; - - private T data; - - private String gateway; - - private Integer needReply; - -} \ 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 deleted file mode 100644 index e8055c0..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/CommonTopicResponse.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * Unified Topic response format - * @author sean.zhou - * @date 2021/11/15 - * @version 0.1 - */ -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -@JsonIgnoreProperties(ignoreUnknown = true) -public class CommonTopicResponse { - - /** - * The command is sent and the response is matched by the tid and bid fields in the message, - * and the reply should keep the tid and bid the same. - */ - private String tid; - - private String bid; - - private String method; - - private T data; - - private Long timestamp; -} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/model/ConfigScopeEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/ConfigScopeEnum.java deleted file mode 100644 index 7cd4479..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/ConfigScopeEnum.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import com.dji.sample.manage.service.IRequestsConfigService; -import com.dji.sample.manage.service.impl.ConfigProductServiceImpl; -import lombok.Getter; - -import java.util.Arrays; -import java.util.Optional; - -/** - * @author sean - * @version 1.3 - * @date 2022/11/10 - */ -@Getter -public enum ConfigScopeEnum { - - PRODUCT("product", ConfigProductServiceImpl.class); - - String scope; - - Class clazz; - - ConfigScopeEnum(String scope, Class clazz) { - this.scope = scope; - this.clazz = clazz; - } - - public static Optional find(String scope) { - return Arrays.stream(ConfigScopeEnum.values()).filter(scopeEnum -> scopeEnum.scope.equals(scope)).findAny(); - } -} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/ErrorInfoReply.java b/src/main/java/com/dji/sample/component/mqtt/model/ErrorInfoReply.java deleted file mode 100644 index 160a86e..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/ErrorInfoReply.java +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 37205bc..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/EventsMethodEnum.java +++ /dev/null @@ -1,83 +0,0 @@ -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", ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS), - - FILE_UPLOAD_CALLBACK("file_upload_callback", ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK), - - HMS("hms", ChannelName.INBOUND_EVENTS_HMS), - - DEVICE_REBOOT("device_reboot", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - DRONE_OPEN("drone_open", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - DRONE_CLOSE("drone_close", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - DEVICE_CHECK("device_check", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - DRONE_FORMAT("drone_format", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - DEVICE_FORMAT("device_format", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - COVER_OPEN("cover_open", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - COVER_CLOSE("cover_close", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - PUTTER_OPEN("putter_open", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - PUTTER_CLOSE("putter_close", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - CHARGE_OPEN("charge_open", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - CHARGE_CLOSE("charge_close", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS), - - OTA_PROGRESS("ota_progress", ChannelName.INBOUND_EVENTS_OTA_PROGRESS), - - FILE_UPLOAD_PROGRESS("fileupload_progress", ChannelName.INBOUND_EVENTS_FILE_UPLOAD_PROGRESS), - - HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA("highest_priority_upload_flighttask_media", ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA), - - FLIGHT_TASK_READY("flighttask_ready", ChannelName.INBOUND_EVENTS_FLIGHT_TASK_READY), - - FLY_TO_POINT_PROGRESS("fly_to_point_progress", ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS), - - TAKE_OFF_TO_POINT_PROGRESS("takeoff_to_point_progress", ChannelName.INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS), - - DRC_STATUS_NOTIFY("drc_status_notify", ChannelName.INBOUND_EVENTS_DRC_STATUS_NOTIFY), - - JOYSTICK_INVALID_NOTIFY("joystick_invalid_notify", ChannelName.INBOUND_EVENTS_DRC_MODE_EXIT_NOTIFY), - - UNKNOWN("Unknown", ChannelName.DEFAULT); - - private String method; - - private String channelName; - - EventsMethodEnum(String method, String channelName) { - this.method = method; - this.channelName = channelName; - } - - public String getMethod() { - return method; - } - - public String getChannelName() { - return channelName; - } - - 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/EventsOutputProgressReceiver.java b/src/main/java/com/dji/sample/component/mqtt/model/EventsOutputProgressReceiver.java deleted file mode 100644 index 499d85a..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/EventsOutputProgressReceiver.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import lombok.Data; - -/** - * @author sean - * @version 1.2 - * @date 2022/7/29 - */ -@Data -public class EventsOutputProgressReceiver { - - private String status; - - private OutputProgressReceiver progress; - - private T ext; -} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java b/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java index 1597e21..f24f6af 100644 --- a/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java +++ b/src/main/java/com/dji/sample/component/mqtt/model/EventsReceiver.java @@ -1,29 +1,64 @@ package com.dji.sample.component.mqtt.model; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.EventsErrorCode; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; /** * @author sean * @version 1.1 * @date 2022/6/9 */ +@EqualsAndHashCode(callSuper = true) @Data @JsonIgnoreProperties(ignoreUnknown = true) @Builder @NoArgsConstructor @AllArgsConstructor -public class EventsReceiver { - - private Integer result; - - private T output; +public class EventsReceiver extends EventsDataRequest { private String bid; private String sn; + @Override + public EventsErrorCode getResult() { + return super.getResult(); + } + + @Override + public EventsReceiver setResult(EventsErrorCode result) { + super.setResult(result); + return this; + } + + @Override + public T getOutput() { + return super.getOutput(); + } + + @Override + public EventsReceiver setOutput(T output) { + super.setOutput(output); + return this; + } + + public String getBid() { + return bid; + } + + public EventsReceiver setBid(String bid) { + this.bid = bid; + return this; + } + + public String getSn() { + return sn; + } + + public EventsReceiver setSn(String sn) { + this.sn = sn; + return this; + } } diff --git a/src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java deleted file mode 100644 index dd37ac6..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/EventsResultStatusEnum.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import lombok.Getter; - -import java.util.Arrays; - -/** - * @author sean - * @version 1.2 - * @date 2022/8/17 - */ -@Getter -public enum EventsResultStatusEnum { - - SENT("sent", false), - - IN_PROGRESS("in_progress", false), - - OK("ok", true), - - PAUSED("paused", false), - - REJECTED("rejected", true), - - FAILED("failed", true), - - CANCELED("canceled", true), - - TIMEOUT("timeout", true), - - PARTIALLY_DONE("partially_done", true), - - UNKNOWN("unknown", false); - - String desc; - - Boolean end; - - EventsResultStatusEnum(String desc, Boolean end) { - this.desc = desc; - this.end = end; - } - - public static EventsResultStatusEnum find(String desc) { - return Arrays.stream(EventsResultStatusEnum.values()) - .filter(status -> status.desc.equals(desc)) - .findFirst().orElse(UNKNOWN); - } -} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java b/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java index 0e4f498..632152e 100644 --- a/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java +++ b/src/main/java/com/dji/sample/component/mqtt/model/MapKeyConst.java @@ -11,28 +11,6 @@ public final class 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"; - - public static final String MODULE_LIST = "module_list"; - - public static final String FLIGHT_ID = "flight_id"; - - public static final String FLIGHT_IDS = "flight_ids"; - public static final String ACL = "acl"; } diff --git a/src/main/java/com/dji/sample/component/mqtt/model/MqttProtocolEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/MqttProtocolEnum.java index ac54b1f..49bce90 100644 --- a/src/main/java/com/dji/sample/component/mqtt/model/MqttProtocolEnum.java +++ b/src/main/java/com/dji/sample/component/mqtt/model/MqttProtocolEnum.java @@ -12,7 +12,7 @@ public enum MqttProtocolEnum { MQTT("tcp"), - MQTTS("tcp"), + MQTTS("ssl"), WS("ws"), diff --git a/src/main/java/com/dji/sample/component/mqtt/model/OutputProgressReceiver.java b/src/main/java/com/dji/sample/component/mqtt/model/OutputProgressReceiver.java deleted file mode 100644 index 33e9db1..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/OutputProgressReceiver.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import lombok.Data; - -/** - * @author sean - * @version 1.2 - * @date 2022/7/29 - */ -@Data -public class OutputProgressReceiver { - - private Integer percent; - - private String stepKey; - - private Integer stepResult; -} 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 deleted file mode 100644 index 8d04ceb..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/RequestsMethodEnum.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import lombok.Getter; - -import java.util.Arrays; - -/** - * @author sean - * @version 1.0 - * @date 2022/5/25 - */ -@Getter -public enum RequestsMethodEnum { - - STORAGE_CONFIG_GET("storage_config_get", ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET), - - AIRPORT_BIND_STATUS("airport_bind_status", ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS), - - AIRPORT_ORGANIZATION_BIND("airport_organization_bind", ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND), - - AIRPORT_ORGANIZATION_GET("airport_organization_get", ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET), - - FLIGHT_TASK_RESOURCE_GET("flighttask_resource_get", ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET), - - CONFIG("config", ChannelName.INBOUND_REQUESTS_CONFIG), - - UNKNOWN("Unknown", ChannelName.DEFAULT); - - private String method; - - private String channelName; - - RequestsMethodEnum(String method, String channelName) { - this.method = method; - this.channelName = channelName; - } - - 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 deleted file mode 100644 index e788466..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/RequestsReply.java +++ /dev/null @@ -1,44 +0,0 @@ -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 deleted file mode 100644 index d0b1562..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/ServiceReply.java +++ /dev/null @@ -1,20 +0,0 @@ -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/SetReply.java b/src/main/java/com/dji/sample/component/mqtt/model/SetReply.java deleted file mode 100644 index 2c7f453..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/SetReply.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import lombok.Data; - -/** - * @author sean - * @version 1.3 - * @date 2022/10/28 - */ -@Data -public class SetReply { - - private Integer result; -} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java deleted file mode 100644 index 5a127e4..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/SetReplyStatusResultEnum.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.dji.sample.component.mqtt.model; - -import lombok.Getter; - -/** - * @author sean - * @version 1.3 - * @date 2022/10/28 - */ -@Getter -public enum SetReplyStatusResultEnum { - - SUCCESS(0, "success"), - - FAILED(1, "failed"), - - TIMEOUT(2, "timeout"), - - UNKNOWN(-1, "unknown"); - - int val; - - String desc; - - SetReplyStatusResultEnum(int val, String desc) { - this.val = val; - this.desc = desc; - } - -} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/StateDataEnum.java b/src/main/java/com/dji/sample/component/mqtt/model/StateDataEnum.java deleted file mode 100644 index 82bed19..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/model/StateDataEnum.java +++ /dev/null @@ -1,26 +0,0 @@ -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/service/IMessageSenderService.java b/src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java deleted file mode 100644 index e21816c..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.dji.sample.component.mqtt.service; - -import com.dji.sample.component.mqtt.model.CommonTopicResponse; -import com.dji.sample.component.mqtt.model.ServiceReply; -import com.fasterxml.jackson.core.type.TypeReference; - -/** - * @author sean.zhou - * @version 0.1 - * @date 2021/11/25 - */ -public interface IMessageSenderService { - - /** - * Publish a message to a specific topic. - * @param topic target - * @param response message - */ - void publish(String topic, CommonTopicResponse response); - - /** - * Use a specific qos to push messages to a specific topic. - * @param topic target - * @param qos qos - * @param response message - */ - void publish(String topic, int qos, CommonTopicResponse response); - - /** - * Send message and receive a response at the same time. - * @param clazz - * @param topic - * @param response notification of whether the start is successful. - * @return - */ - T publishWithReply(Class clazz, String topic, CommonTopicResponse response); - - /** - * Send message and receive a response at the same time. - * @param clazz - * @param topic - * @param response - * @param retryTime - * @param - * @return - */ - T publishWithReply(Class clazz, String topic, CommonTopicResponse response, int retryTime); - - /** - * Used exclusively for sending messages for services. - * @param clazz The generic class for ServiceReply. - * @param sn - * @param method - * @param data - * @param bid - * @param - * @return - */ - ServiceReply publishServicesTopic(TypeReference clazz, String sn, String method, Object data, String bid); - - /** - * Used exclusively for sending messages for services, and does not set the received subtype. - * @param sn - * @param method - * @param data - * @param bid - * @return - */ - ServiceReply publishServicesTopic(String sn, String method, Object data, String bid); - - /** - * Used exclusively for sending messages for services. - * @param clazz The generic class for ServiceReply. - * @param sn - * @param method - * @param data - * @param - * @return - */ - ServiceReply publishServicesTopic(TypeReference clazz, String sn, String method, Object data); - - /** - * Used exclusively for sending messages for services, and does not set the received subtype. - * @param sn - * @param method - * @param data - * @return - */ - ServiceReply publishServicesTopic(String sn, String method, Object data); -} 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 deleted file mode 100644 index fdaf62c..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.dji.sample.component.mqtt.service.impl; - -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.IMessageSenderService; -import com.dji.sample.component.mqtt.service.IMqttMessageGateway; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.TypeMismatchException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @author sean.zhou - * @date 2021/11/16 - * @version 0.1 - */ -@Service -@Slf4j -public class MessageSenderServiceImpl implements IMessageSenderService { - - @Autowired - private IMqttMessageGateway messageGateway; - - @Autowired - private ObjectMapper mapper; - - public void publish(String topic, CommonTopicResponse response) { - this.publish(topic, 1, response); - } - - public void publish(String topic, int qos, CommonTopicResponse response) { - try { - log.info("send topic: {}, payload: {}", topic, response.toString()); - messageGateway.publish(topic, mapper.writeValueAsBytes(response), qos); - } catch (JsonProcessingException e) { - log.info("Failed to publish the message. {}", response.toString()); - e.printStackTrace(); - } - } - - public T publishWithReply(Class clazz, String topic, CommonTopicResponse response) { - return this.publishWithReply(clazz, topic, response, 2); - } - - public T publishWithReply(Class clazz, String topic, CommonTopicResponse response, int retryTime) { - AtomicInteger time = new AtomicInteger(0); - // Retry three times - while (time.getAndIncrement() <= retryTime) { - 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.getTid()); - - // Need to match tid and bid. - if (Objects.nonNull(receiver) && receiver.getTid().equals(response.getTid()) && - receiver.getBid().equals(response.getBid())) { - if (clazz.isAssignableFrom(receiver.getData().getClass())) { - return receiver.getData(); - } - throw new TypeMismatchException(receiver.getData(), clazz); - } - // It must be guaranteed that the tid and bid of each message are different. - response.setBid(UUID.randomUUID().toString()); - response.setTid(UUID.randomUUID().toString()); - } - throw new RuntimeException("No message reply received."); - } - - @Override - public ServiceReply publishServicesTopic(TypeReference clazz, String sn, String method, Object data, String bid) { - String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + sn + TopicConst.SERVICES_SUF; - ServiceReply reply = this.publishWithReply(ServiceReply.class, topic, - CommonTopicResponse.builder() - .tid(UUID.randomUUID().toString()) - .bid(StringUtils.hasText(bid) ? bid : UUID.randomUUID().toString()) - .timestamp(System.currentTimeMillis()) - .method(method) - .data(Objects.requireNonNullElse(data, "")) - .build()); - if (Objects.isNull(clazz)) { - return reply; - } - // put together in "output" - if (Objects.nonNull(reply.getInfo())) { - reply.setOutput(mapper.convertValue(reply.getInfo(), clazz)); - } - if (Objects.nonNull(reply.getOutput())) { - reply.setOutput(mapper.convertValue(reply.getOutput(), clazz)); - } - return reply; - } - - @Override - public ServiceReply publishServicesTopic(String sn, String method, Object data, String bid) { - return this.publishServicesTopic(null, sn, method, data, bid); - } - - @Override - public ServiceReply publishServicesTopic(TypeReference clazz, String sn, String method, Object data) { - return this.publishServicesTopic(clazz, sn, method, data, null); - } - - @Override - public ServiceReply publishServicesTopic(String sn, String method, Object data) { - return this.publishServicesTopic(null, sn, method, data, null); - } - -} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/service/impl/MqttTopicServiceImpl.java b/src/main/java/com/dji/sample/component/mqtt/service/impl/MqttTopicServiceImpl.java deleted file mode 100644 index 6880ecf..0000000 --- a/src/main/java/com/dji/sample/component/mqtt/service/impl/MqttTopicServiceImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.dji.sample.component.mqtt.service.impl; - -import com.dji.sample.component.mqtt.service.IMqttTopicService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * - * @author sean.zhou - * @date 2021/11/10 - * @version 0.1 - */ -@Component -@Slf4j -public class MqttTopicServiceImpl implements IMqttTopicService { - - @Resource - private MqttPahoMessageDrivenChannelAdapter adapter; - - @Override - public void subscribe(String topic) { - log.debug("subscribe topic: {}", topic); - adapter.addTopic(topic); - } - - @Override - public void subscribe(String topic, int qos) { - log.debug("subscribe topic: {}", topic); - adapter.addTopic(topic, qos); - } - - @Override - public void unsubscribe(String topic) { - log.debug("unsubscribe topic: {}", topic); - adapter.removeTopic(topic); - } - - public String[] getSubscribedTopic() { - return adapter.getTopic(); - } -} 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 index da668c9..496c16a 100644 --- a/src/main/java/com/dji/sample/component/oss/model/OssConfiguration.java +++ b/src/main/java/com/dji/sample/component/oss/model/OssConfiguration.java @@ -1,5 +1,6 @@ package com.dji.sample.component.oss.model; +import com.dji.sdk.cloudapi.storage.OssTypeEnum; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -13,9 +14,9 @@ import org.springframework.stereotype.Component; public class OssConfiguration { /** - * @see com.dji.sample.component.oss.model.enums.OssTypeEnum + * @see OssTypeEnum */ - public static String provider; + public static OssTypeEnum provider; /** * Whether to use the object storage service. @@ -43,7 +44,7 @@ public class OssConfiguration { public static String objectDirPrefix; - public void setProvider(String provider) { + public void setProvider(OssTypeEnum provider) { OssConfiguration.provider = provider; } 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 5d8e415..3b26050 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 @@ -1,6 +1,7 @@ package com.dji.sample.component.oss.service; -import com.dji.sample.media.model.CredentialsDTO; +import com.dji.sdk.cloudapi.storage.CredentialsToken; +import com.dji.sdk.cloudapi.storage.OssTypeEnum; import java.io.InputStream; import java.net.URL; @@ -12,13 +13,13 @@ import java.net.URL; */ public interface IOssService { - String getOssType(); + OssTypeEnum getOssType(); /** * Get temporary credentials. * @return */ - CredentialsDTO getCredentials(); + CredentialsToken getCredentials(); /** * Get the address of the object based on the bucket name and the object name. 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 6717849..cf78637 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 @@ -13,9 +13,9 @@ 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.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 com.dji.sdk.cloudapi.storage.CredentialsToken; +import com.dji.sdk.cloudapi.storage.OssTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -36,12 +36,12 @@ public class AliyunOssServiceImpl implements IOssService { private OSS ossClient; @Override - public String getOssType() { - return OssTypeEnum.ALIYUN.getType(); + public OssTypeEnum getOssType() { + return OssTypeEnum.ALIYUN; } @Override - public CredentialsDTO getCredentials() { + public CredentialsToken getCredentials() { try { DefaultProfile profile = DefaultProfile.getProfile( @@ -54,7 +54,7 @@ public class AliyunOssServiceImpl implements IOssService { request.setRoleSessionName(OssConfiguration.roleSessionName); AssumeRoleResponse response = client.getAcsResponse(request); - return new CredentialsDTO(response.getCredentials(), OssConfiguration.expire); + return new CredentialsToken(response.getCredentials(), OssConfiguration.expire); } catch (ClientException e) { log.debug("Failed to obtain sts."); @@ -95,7 +95,7 @@ public class AliyunOssServiceImpl implements IOssService { throw new RuntimeException("The filename already exists."); } PutObjectResult objectResult = ossClient.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata())); - log.info("Upload File: {}", objectResult.getETag()); + log.info("Upload FlighttaskCreateFile: {}", objectResult.getETag()); } public void createClient() { diff --git a/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java b/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java index fa896b6..10e109a 100644 --- a/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java +++ b/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java @@ -13,9 +13,9 @@ 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 com.dji.sdk.cloudapi.storage.CredentialsToken; +import com.dji.sdk.cloudapi.storage.OssTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -39,12 +39,12 @@ public class AmazonS3ServiceImpl implements IOssService { private AmazonS3 client; @Override - public String getOssType() { - return OssTypeEnum.AWS.getType(); + public OssTypeEnum getOssType() { + return OssTypeEnum.AWS; } @Override - public CredentialsDTO getCredentials() { + public CredentialsToken getCredentials() { AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard() .withCredentials(new AWSStaticCredentialsProvider( new BasicAWSCredentials(OssConfiguration.accessKey, OssConfiguration.secretKey))) @@ -56,7 +56,7 @@ public class AmazonS3ServiceImpl implements IOssService { .withDurationSeconds(Math.toIntExact(OssConfiguration.expire)); AssumeRoleResult result = stsClient.assumeRole(request); Credentials credentials = result.getCredentials(); - return new CredentialsDTO(credentials); + return new CredentialsToken(credentials); } @Override @@ -84,7 +84,7 @@ public class AmazonS3ServiceImpl implements IOssService { throw new RuntimeException("The filename already exists."); } PutObjectResult objectResult = client.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata())); - log.info("Upload File: {}", objectResult.toString()); + log.info("Upload FlighttaskCreateFile: {}", objectResult.toString()); } public void createClient() { diff --git a/src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java b/src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java index cafeb66..5039094 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,9 +1,9 @@ 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 com.dji.sdk.cloudapi.storage.CredentialsToken; +import com.dji.sdk.cloudapi.storage.OssTypeEnum; import io.minio.*; import io.minio.credentials.AssumeRoleProvider; import io.minio.errors.*; @@ -31,17 +31,17 @@ public class MinIOServiceImpl implements IOssService { private MinioClient client; @Override - public String getOssType() { - return OssTypeEnum.MINIO.getType(); + public OssTypeEnum getOssType() { + return OssTypeEnum.MINIO; } @Override - public CredentialsDTO getCredentials() { + public CredentialsToken getCredentials() { try { AssumeRoleProvider provider = new AssumeRoleProvider(OssConfiguration.endpoint, OssConfiguration.accessKey, OssConfiguration.secretKey, Math.toIntExact(OssConfiguration.expire), null, OssConfiguration.region, null, null, null, null); - return new CredentialsDTO(provider.fetch(), OssConfiguration.expire); + return new CredentialsToken(provider.fetch(), OssConfiguration.expire); } catch (NoSuchAlgorithmException e) { log.debug("Failed to obtain sts."); e.printStackTrace(); @@ -100,9 +100,9 @@ public class MinIOServiceImpl implements IOssService { try { ObjectWriteResponse response = client.putObject( PutObjectArgs.builder().bucket(bucket).object(objectKey).stream(input, input.available(), 0).build()); - log.info("Upload File: {}", response.etag()); + log.info("Upload FlighttaskCreateFile: {}", response.etag()); } catch (MinioException | IOException | InvalidKeyException | NoSuchAlgorithmException ex) { - log.error("Failed to upload File {}.", objectKey); + log.error("Failed to upload FlighttaskCreateFile {}.", objectKey); ex.printStackTrace(); } } diff --git a/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java b/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java index 28333ca..c46fad8 100644 --- a/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java +++ b/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java @@ -1,9 +1,9 @@ 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 com.dji.sdk.cloudapi.storage.CredentialsToken; +import com.dji.sdk.cloudapi.storage.OssTypeEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -29,7 +29,7 @@ public class OssServiceContext { return; } this.ossService = ossServices.stream() - .filter(ossService -> ossService.getOssType().equals(OssConfiguration.provider)) + .filter(ossService -> ossService.getOssType() == OssConfiguration.provider) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Oss provider is illegal. Optional: " + Arrays.toString(Arrays.stream(OssTypeEnum.values()).map(OssTypeEnum::getType).toArray()))); @@ -39,7 +39,7 @@ public class OssServiceContext { return this.ossService; } - public CredentialsDTO getCredentials() { + public CredentialsToken getCredentials() { return this.ossService.getCredentials(); } diff --git a/src/main/java/com/dji/sample/component/redis/RedisConst.java b/src/main/java/com/dji/sample/component/redis/RedisConst.java index 574ae62..023eabd 100644 --- a/src/main/java/com/dji/sample/component/redis/RedisConst.java +++ b/src/main/java/com/dji/sample/component/redis/RedisConst.java @@ -1,7 +1,5 @@ package com.dji.sample.component.redis; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; - /** * @author sean * @version 1.0 @@ -21,9 +19,7 @@ public final class RedisConst { public static final Integer WEBSOCKET_ALIVE_SECOND = 60 * 60 * 24; - public static final String ONLINE_PREFIX = "online" + DELIMITER; - - public static final String DEVICE_ONLINE_PREFIX = ONLINE_PREFIX + DeviceDomainEnum.SUB_DEVICE + DELIMITER; + public static final String DEVICE_ONLINE_PREFIX = "online" + DELIMITER; public static final String WEBSOCKET_PREFIX = "webSocket" + DELIMITER; @@ -37,9 +33,11 @@ public final class RedisConst { public static final String LOGS_FILE_PREFIX = "logs_file" + DELIMITER; - public static final String WAYLINE_JOB_PREPARED = "wayline_job_prepared"; + public static final String WAYLINE_JOB_TIMED_EXECUTE = "wayline_job_timed_execute"; + + public static final String WAYLINE_JOB_CONDITION_PREPARE = "wayline_job_condition_prepare"; - public static final String WAYLINE_JOB_CONDITION_PREFIX = "wayline_job_condition" + DELIMITER; + public static final String WAYLINE_JOB_CONDITION_PREFIX = WAYLINE_JOB_CONDITION_PREPARE + DELIMITER; public static final String WAYLINE_JOB_BLOCK_PREFIX = "wayline_job_block" + DELIMITER; diff --git a/src/main/java/com/dji/sample/component/websocket/config/MyConcurrentWebSocketSession.java b/src/main/java/com/dji/sample/component/websocket/config/MyConcurrentWebSocketSession.java new file mode 100644 index 0000000..6941064 --- /dev/null +++ b/src/main/java/com/dji/sample/component/websocket/config/MyConcurrentWebSocketSession.java @@ -0,0 +1,25 @@ +package com.dji.sample.component.websocket.config; + +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/24 + */ +public class MyConcurrentWebSocketSession extends ConcurrentWebSocketSessionDecorator { + + private static final int SEND_BUFFER_SIZE_LIMIT = 1024 * 1024; + + private static final int SEND_TIME_LIMIT = 1000; + + private MyConcurrentWebSocketSession(WebSocketSession delegate, int sendTimeLimit, int bufferSizeLimit) { + super(delegate, sendTimeLimit, bufferSizeLimit); + } + + MyConcurrentWebSocketSession(WebSocketSession delegate) { + this(delegate, SEND_TIME_LIMIT, SEND_BUFFER_SIZE_LIMIT); + } + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java b/src/main/java/com/dji/sample/component/websocket/config/MyWebSocketFactory.java similarity index 67% rename from src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java rename to src/main/java/com/dji/sample/component/websocket/config/MyWebSocketFactory.java index f6ad8fb..22fb995 100644 --- a/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java +++ b/src/main/java/com/dji/sample/component/websocket/config/MyWebSocketFactory.java @@ -1,10 +1,11 @@ package com.dji.sample.component.websocket.config; import com.dji.sample.component.websocket.service.IWebSocketManageService; +import com.dji.sdk.websocket.WebSocketDefaultFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; /** * @@ -13,13 +14,14 @@ import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; * @version 0.1 */ @Component -public class WebSocketDefaultFactory implements WebSocketHandlerDecoratorFactory { +@Primary +public class MyWebSocketFactory extends WebSocketDefaultFactory { @Autowired private IWebSocketManageService webSocketManageService; @Override public WebSocketHandler decorate(WebSocketHandler handler) { - return new WebSocketDefaultHandler(handler, webSocketManageService); + return new MyWebSocketHandler(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/MyWebSocketHandler.java similarity index 86% rename from src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultHandler.java rename to src/main/java/com/dji/sample/component/websocket/config/MyWebSocketHandler.java index 11ee861..d6016b0 100644 --- a/src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultHandler.java +++ b/src/main/java/com/dji/sample/component/websocket/config/MyWebSocketHandler.java @@ -1,13 +1,13 @@ package com.dji.sample.component.websocket.config; import com.dji.sample.component.websocket.service.IWebSocketManageService; +import com.dji.sdk.websocket.WebSocketDefaultHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.WebSocketHandlerDecorator; import java.security.Principal; @@ -18,11 +18,11 @@ import java.security.Principal; * @version 0.1 */ @Slf4j -public class WebSocketDefaultHandler extends WebSocketHandlerDecorator { +public class MyWebSocketHandler extends WebSocketDefaultHandler { private IWebSocketManageService webSocketManageService; - WebSocketDefaultHandler(WebSocketHandler delegate, IWebSocketManageService webSocketManageService) { + MyWebSocketHandler(WebSocketHandler delegate, IWebSocketManageService webSocketManageService) { super(delegate); this.webSocketManageService = webSocketManageService; } @@ -31,7 +31,7 @@ public class WebSocketDefaultHandler extends WebSocketHandlerDecorator { public void afterConnectionEstablished(WebSocketSession session) throws Exception { Principal principal = session.getPrincipal(); if (StringUtils.hasText(principal.getName())) { - webSocketManageService.put(principal.getName(), new ConcurrentWebSocketSession(session)); + webSocketManageService.put(principal.getName(), new MyConcurrentWebSocketSession(session)); log.debug("{} is connected. ID: {}. WebSocketSession[current count: {}]", principal.getName(), session.getId(), webSocketManageService.getConnectedCount()); return; 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 2094d48..cecbf57 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 @@ -15,7 +15,7 @@ public enum BizCodeEnum { DEVICE_OSD("device_osd"), - GATEWAY_OSD("gateway_osd"), + RC_OSD("gateway_osd"), DOCK_OSD("dock_osd"), diff --git a/src/main/java/com/dji/sample/component/websocket/model/CustomWebSocketMessage.java b/src/main/java/com/dji/sample/component/websocket/model/CustomWebSocketMessage.java deleted file mode 100644 index c1d162d..0000000 --- a/src/main/java/com/dji/sample/component/websocket/model/CustomWebSocketMessage.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.dji.sample.component.websocket.model; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Builder; -import lombok.Data; - -/** - * The format of WebSocket messages that the pilot can receive. - * @author sean.zhou - * @date 2021/11/17 - * @version 0.1 - */ -@Data -@Builder -public class CustomWebSocketMessage { - - /** - * @see BizCodeEnum - * specific value - */ - @JsonProperty("biz_code") - private String bizCode; - - @Builder.Default - private String version = "1.0"; - - private Long timestamp; - - private T data; -} \ No newline at end of file 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 index f09a6e2..21d3009 100644 --- a/src/main/java/com/dji/sample/component/websocket/service/IWebSocketManageService.java +++ b/src/main/java/com/dji/sample/component/websocket/service/IWebSocketManageService.java @@ -1,6 +1,6 @@ package com.dji.sample.component.websocket.service; -import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; +import com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession; import java.util.Collection; @@ -11,13 +11,13 @@ import java.util.Collection; */ public interface IWebSocketManageService { - void put(String key, ConcurrentWebSocketSession val); + void put(String key, MyConcurrentWebSocketSession val); void remove(String key, String sessionId); - Collection getValueWithWorkspace(String workspaceId); + Collection getValueWithWorkspace(String workspaceId); - Collection getValueWithWorkspaceAndUserType(String workspaceId, Integer userType); + Collection getValueWithWorkspaceAndUserType(String workspaceId, Integer userType); Long getConnectedCount(); } diff --git a/src/main/java/com/dji/sample/component/websocket/service/ISendMessageService.java b/src/main/java/com/dji/sample/component/websocket/service/IWebSocketMessageService.java similarity index 62% rename from src/main/java/com/dji/sample/component/websocket/service/ISendMessageService.java rename to src/main/java/com/dji/sample/component/websocket/service/IWebSocketMessageService.java index a9b3d8f..ee51adb 100644 --- a/src/main/java/com/dji/sample/component/websocket/service/ISendMessageService.java +++ b/src/main/java/com/dji/sample/component/websocket/service/IWebSocketMessageService.java @@ -1,7 +1,7 @@ package com.dji.sample.component.websocket.service; -import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; -import com.dji.sample.component.websocket.model.CustomWebSocketMessage; +import com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession; +import com.dji.sdk.websocket.WebSocketMessageResponse; import java.util.Collection; @@ -10,21 +10,21 @@ import java.util.Collection; * @date 2021/11/24 * @version 0.1 */ -public interface ISendMessageService { +public interface IWebSocketMessageService { /** * Send a message to the specific connection. * @param session A WebSocket connection object * @param message message */ - void sendMessage(ConcurrentWebSocketSession session, CustomWebSocketMessage message); + void sendMessage(MyConcurrentWebSocketSession session, WebSocketMessageResponse message); /** * Send the same message to specific connection. * @param sessions A collection of WebSocket connection objects. * @param message message */ - void sendBatch(Collection sessions, CustomWebSocketMessage message); + void sendBatch(Collection sessions, WebSocketMessageResponse message); void sendBatch(String workspaceId, Integer userType, String bizCode, Object data); 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 index a0070d4..755e78d 100644 --- 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 @@ -2,7 +2,7 @@ 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.config.MyConcurrentWebSocketSession; import com.dji.sample.component.websocket.service.IWebSocketManageService; import com.dji.sample.manage.model.enums.UserTypeEnum; import lombok.extern.slf4j.Slf4j; @@ -24,10 +24,10 @@ import java.util.stream.Collectors; @Service public class WebSocketManageServiceImpl implements IWebSocketManageService { - private static final ConcurrentHashMap SESSIONS = new ConcurrentHashMap<>(16); - + private static final ConcurrentHashMap SESSIONS = new ConcurrentHashMap<>(16); + @Override - public void put(String key, ConcurrentWebSocketSession val) { + public void put(String key, MyConcurrentWebSocketSession val) { String[] name = key.split("/"); if (name.length != 3) { log.debug("The key is out of format. [{workspaceId}/{userType}/{userId}]"); @@ -56,7 +56,7 @@ public class WebSocketManageServiceImpl implements IWebSocketManageService { } @Override - public Collection getValueWithWorkspace(String workspaceId) { + public Collection getValueWithWorkspace(String workspaceId) { if (!StringUtils.hasText(workspaceId)) { return Collections.emptySet(); } @@ -70,12 +70,12 @@ public class WebSocketManageServiceImpl implements IWebSocketManageService { } @Override - public Collection getValueWithWorkspaceAndUserType(String workspaceId, Integer userType) { + public Collection getValueWithWorkspaceAndUserType(String workspaceId, Integer userType) { String key = RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(userType).getDesc(); return RedisOpsUtils.hashKeys(key) .stream() .map(SESSIONS::get) - .filter(this.getValueWithWorkspace(workspaceId)::contains) + .filter(getValueWithWorkspace(workspaceId)::contains) .collect(Collectors.toSet()); } 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/WebSocketMessageServiceImpl.java similarity index 72% rename from src/main/java/com/dji/sample/component/websocket/service/impl/SendMessageServiceImpl.java rename to src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketMessageServiceImpl.java index b605cf9..adfd6ee 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/WebSocketMessageServiceImpl.java @@ -1,9 +1,9 @@ package com.dji.sample.component.websocket.service.impl; -import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; -import com.dji.sample.component.websocket.model.CustomWebSocketMessage; -import com.dji.sample.component.websocket.service.ISendMessageService; +import com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession; import com.dji.sample.component.websocket.service.IWebSocketManageService; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; +import com.dji.sdk.websocket.WebSocketMessageResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -22,7 +22,7 @@ import java.util.Objects; */ @Service @Slf4j -public class SendMessageServiceImpl implements ISendMessageService { +public class WebSocketMessageServiceImpl implements IWebSocketMessageService { @Autowired private ObjectMapper mapper; @@ -31,7 +31,7 @@ public class SendMessageServiceImpl implements ISendMessageService { private IWebSocketManageService webSocketManageService; @Override - public void sendMessage(ConcurrentWebSocketSession session, CustomWebSocketMessage message) { + public void sendMessage(MyConcurrentWebSocketSession session, WebSocketMessageResponse message) { if (session == null) { return; } @@ -52,7 +52,7 @@ public class SendMessageServiceImpl implements ISendMessageService { } @Override - public void sendBatch(Collection sessions, CustomWebSocketMessage message) { + public void sendBatch(Collection sessions, WebSocketMessageResponse message) { if (sessions.isEmpty()) { return; } @@ -61,7 +61,7 @@ public class SendMessageServiceImpl implements ISendMessageService { TextMessage data = new TextMessage(mapper.writeValueAsBytes(message)); - for (ConcurrentWebSocketSession session : sessions) { + for (MyConcurrentWebSocketSession session : sessions) { if (!session.isOpen()) { session.close(); log.debug("This session is closed."); @@ -82,15 +82,14 @@ public class SendMessageServiceImpl implements ISendMessageService { if (!StringUtils.hasText(workspaceId)) { throw new RuntimeException("Workspace ID does not exist."); } - Collection sessions = Objects.isNull(userType) ? + Collection sessions = Objects.isNull(userType) ? webSocketManageService.getValueWithWorkspace(workspaceId) : webSocketManageService.getValueWithWorkspaceAndUserType(workspaceId, userType); - this.sendBatch(sessions, CustomWebSocketMessage.builder() - .data(data) - .timestamp(System.currentTimeMillis()) - .bizCode(bizCode) - .build()); + this.sendBatch(sessions, new WebSocketMessageResponse() + .setData(Objects.requireNonNullElse(data, "")) + .setTimestamp(System.currentTimeMillis()) + .setBizCode(bizCode)); } @Override diff --git a/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java b/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java index eee6f48..9bfd8ce 100644 --- a/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java +++ b/src/main/java/com/dji/sample/configuration/SpringBeanConfiguration.java @@ -23,7 +23,6 @@ public class SpringBeanConfiguration { 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(); diff --git a/src/main/java/com/dji/sample/configuration/mvc/GetSnakeDataBinder.java b/src/main/java/com/dji/sample/configuration/mvc/GetSnakeDataBinder.java deleted file mode 100644 index 8a7ca6a..0000000 --- a/src/main/java/com/dji/sample/configuration/mvc/GetSnakeDataBinder.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.dji.sample.configuration.mvc; - -import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.PropertyValue; -import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder; - -import javax.servlet.ServletRequest; -import java.util.ArrayList; -import java.util.List; - -/** - * @author sean - * @version 1.2 - * @date 2022/9/9 - */ -public class GetSnakeDataBinder extends ExtendedServletRequestDataBinder { - - public GetSnakeDataBinder(Object target, String objectName) { - super(target, objectName); - } - - @Override - protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) { - List propertyValueList = mpvs.getPropertyValueList(); - List values = new ArrayList<>(propertyValueList); - for (PropertyValue property : values) { - String name = convertSnake(property.getName()); - if (!property.getName().equals(name)) { - mpvs.addPropertyValue(new PropertyValue(name, property.getValue())); - propertyValueList.remove(property); - } - } - super.addBindValues(mpvs, request); - } - - private String convertSnake(String key) { - StringBuilder sb = new StringBuilder(); - boolean isChange = false; - for (char c : key.toCharArray()) { - if (c == '_') { - isChange = true; - continue; - } - if (isChange) { - sb.append((char) (c - 32)); - isChange = false; - continue; - } - sb.append(c); - } - return sb.toString(); - } -} diff --git a/src/main/java/com/dji/sample/configuration/mvc/GlobalMVCConfigurer.java b/src/main/java/com/dji/sample/configuration/mvc/GlobalMVCConfigurer.java index c28e6c4..bb03a1d 100644 --- a/src/main/java/com/dji/sample/configuration/mvc/GlobalMVCConfigurer.java +++ b/src/main/java/com/dji/sample/configuration/mvc/GlobalMVCConfigurer.java @@ -4,7 +4,6 @@ import com.dji.sample.component.AuthInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -29,14 +28,13 @@ public class GlobalMVCConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // Exclude the login interface. - excludePaths.add(managePrefix + manageVersion + "/login"); - excludePaths.add(managePrefix + manageVersion + "/token/refresh"); + excludePaths.add("/" + managePrefix + manageVersion + "/login"); + excludePaths.add("/" + managePrefix + manageVersion + "/token/refresh"); + excludePaths.add("/swagger-ui.html"); + excludePaths.add("/swagger-ui/**"); + excludePaths.add("/v3/**"); + excludePaths.add("/ui/**"); // Intercept for all request interfaces. registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns(excludePaths); } - - @Override - public void addArgumentResolvers(List resolvers) { - resolvers.add(new GetSnakeArgumentProcessor(true)); - } } diff --git a/src/main/java/com/dji/sample/control/controller/DockController.java b/src/main/java/com/dji/sample/control/controller/DockController.java index 53d07f5..3146d9d 100644 --- a/src/main/java/com/dji/sample/control/controller/DockController.java +++ b/src/main/java/com/dji/sample/control/controller/DockController.java @@ -1,9 +1,10 @@ package com.dji.sample.control.controller; -import com.dji.sample.common.model.ResponseResult; import com.dji.sample.control.model.enums.DroneAuthorityEnum; +import com.dji.sample.control.model.enums.RemoteDebugMethodEnum; import com.dji.sample.control.model.param.*; import com.dji.sample.control.service.IControlService; +import com.dji.sdk.common.HttpResultResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -24,39 +25,39 @@ public class DockController { private IControlService controlService; @PostMapping("/{sn}/jobs/{service_identifier}") - public ResponseResult createControlJob(@PathVariable String sn, - @PathVariable("service_identifier") String serviceIdentifier, - @RequestBody(required = false) RemoteDebugParam param) { - return controlService.controlDockDebug(sn, serviceIdentifier, param); + public HttpResultResponse createControlJob(@PathVariable String sn, + @PathVariable("service_identifier") String serviceIdentifier, + @Valid @RequestBody(required = false) RemoteDebugParam param) { + return controlService.controlDockDebug(sn, RemoteDebugMethodEnum.find(serviceIdentifier), param); } @PostMapping("/{sn}/jobs/fly-to-point") - public ResponseResult flyToPoint(@PathVariable String sn, @Valid @RequestBody FlyToPointParam param) { + public HttpResultResponse flyToPoint(@PathVariable String sn, @Valid @RequestBody FlyToPointParam param) { return controlService.flyToPoint(sn, param); } @DeleteMapping("/{sn}/jobs/fly-to-point") - public ResponseResult flyToPointStop(@PathVariable String sn) { + public HttpResultResponse flyToPointStop(@PathVariable String sn) { return controlService.flyToPointStop(sn); } @PostMapping("/{sn}/jobs/takeoff-to-point") - public ResponseResult takeoffToPoint(@PathVariable String sn, @Valid @RequestBody TakeoffToPointParam param) { + public HttpResultResponse takeoffToPoint(@PathVariable String sn, @Valid @RequestBody TakeoffToPointParam param) { return controlService.takeoffToPoint(sn, param); } @PostMapping("/{sn}/authority/flight") - public ResponseResult seizeFlightAuthority(@PathVariable String sn) { + public HttpResultResponse seizeFlightAuthority(@PathVariable String sn) { return controlService.seizeAuthority(sn, DroneAuthorityEnum.FLIGHT, null); } @PostMapping("/{sn}/authority/payload") - public ResponseResult seizePayloadAuthority(@PathVariable String sn, @Valid @RequestBody DronePayloadParam param) { + public HttpResultResponse seizePayloadAuthority(@PathVariable String sn, @Valid @RequestBody DronePayloadParam param) { return controlService.seizeAuthority(sn, DroneAuthorityEnum.PAYLOAD, param); } @PostMapping("/{sn}/payload/commands") - public ResponseResult payloadCommands(@PathVariable String sn, @Valid @RequestBody PayloadCommandsParam param) throws Exception { + public HttpResultResponse payloadCommands(@PathVariable String sn, @Valid @RequestBody PayloadCommandsParam param) throws Exception { param.setSn(sn); return controlService.payloadCommands(param); } diff --git a/src/main/java/com/dji/sample/control/controller/DrcController.java b/src/main/java/com/dji/sample/control/controller/DrcController.java index 6b66089..f40670c 100644 --- a/src/main/java/com/dji/sample/control/controller/DrcController.java +++ b/src/main/java/com/dji/sample/control/controller/DrcController.java @@ -1,12 +1,12 @@ package com.dji.sample.control.controller; import com.dji.sample.common.model.CustomClaim; -import com.dji.sample.common.model.ResponseResult; import com.dji.sample.control.model.dto.JwtAclDTO; -import com.dji.sample.control.model.dto.MqttBrokerDTO; import com.dji.sample.control.model.param.DrcConnectParam; import com.dji.sample.control.model.param.DrcModeParam; import com.dji.sample.control.service.IDrcService; +import com.dji.sdk.cloudapi.control.DrcModeMqttBroker; +import com.dji.sdk.common.HttpResultResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -30,25 +30,25 @@ public class DrcController { private IDrcService drcService; @PostMapping("/workspaces/{workspace_id}/drc/connect") - public ResponseResult drcConnect(@PathVariable("workspace_id") String workspaceId, HttpServletRequest request, @Valid @RequestBody DrcConnectParam param) { + public HttpResultResponse drcConnect(@PathVariable("workspace_id") String workspaceId, HttpServletRequest request, @Valid @RequestBody DrcConnectParam param) { CustomClaim claims = (CustomClaim) request.getAttribute(TOKEN_CLAIM); - MqttBrokerDTO brokerDTO = drcService.userDrcAuth(workspaceId, claims.getId(), claims.getUsername(), param); - return ResponseResult.success(brokerDTO); + DrcModeMqttBroker brokerDTO = drcService.userDrcAuth(workspaceId, claims.getId(), claims.getUsername(), param); + return HttpResultResponse.success(brokerDTO); } @PostMapping("/workspaces/{workspace_id}/drc/enter") - public ResponseResult drcEnter(@PathVariable("workspace_id") String workspaceId, @Valid @RequestBody DrcModeParam param) { + public HttpResultResponse drcEnter(@PathVariable("workspace_id") String workspaceId, @Valid @RequestBody DrcModeParam param) { JwtAclDTO acl = drcService.deviceDrcEnter(workspaceId, param); - return ResponseResult.success(acl); + return HttpResultResponse.success(acl); } @PostMapping("/workspaces/{workspace_id}/drc/exit") - public ResponseResult drcExit(@PathVariable("workspace_id") String workspaceId, @Valid @RequestBody DrcModeParam param) { + public HttpResultResponse drcExit(@PathVariable("workspace_id") String workspaceId, @Valid @RequestBody DrcModeParam param) { drcService.deviceDrcExit(workspaceId, param); - return ResponseResult.success(); + return HttpResultResponse.success(); } diff --git a/src/main/java/com/dji/sample/control/model/dto/AirConditionerMode.java b/src/main/java/com/dji/sample/control/model/dto/AirConditionerMode.java new file mode 100644 index 0000000..901cccd --- /dev/null +++ b/src/main/java/com/dji/sample/control/model/dto/AirConditionerMode.java @@ -0,0 +1,22 @@ +package com.dji.sample.control.model.dto; + +import com.dji.sample.control.service.impl.RemoteDebugHandler; +import com.dji.sdk.cloudapi.device.AirConditionerStateEnum; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AirConditionerMode extends RemoteDebugHandler { + + private AirConditionerStateEnum action; +} diff --git a/src/main/java/com/dji/sample/control/model/dto/AlarmState.java b/src/main/java/com/dji/sample/control/model/dto/AlarmState.java index 1fb590b..62cf797 100644 --- a/src/main/java/com/dji/sample/control/model/dto/AlarmState.java +++ b/src/main/java/com/dji/sample/control/model/dto/AlarmState.java @@ -1,14 +1,12 @@ package com.dji.sample.control.model.dto; import com.dji.sample.control.service.impl.RemoteDebugHandler; -import com.dji.sample.manage.model.enums.StateSwitchEnum; +import com.dji.sdk.cloudapi.device.SwitchActionEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import java.util.Objects; - /** * @author sean * @version 1.3 @@ -20,10 +18,6 @@ import java.util.Objects; @NoArgsConstructor public class AlarmState extends RemoteDebugHandler { - private Integer action; + private SwitchActionEnum action; - @Override - public boolean valid() { - return Objects.nonNull(action) && StateSwitchEnum.find(action).isPresent(); - } } diff --git a/src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java b/src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java index 9656a9e..871d29c 100644 --- a/src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java +++ b/src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java @@ -1,14 +1,12 @@ package com.dji.sample.control.model.dto; -import com.dji.sample.control.model.enums.BatteryStoreModeEnum; import com.dji.sample.control.service.impl.RemoteDebugHandler; +import com.dji.sdk.cloudapi.device.BatteryStoreModeEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import java.util.Objects; - /** * @author sean * @version 1.3 @@ -20,10 +18,5 @@ import java.util.Objects; @NoArgsConstructor public class BatteryStoreMode extends RemoteDebugHandler { - private Integer action; - - @Override - public boolean valid() { - return Objects.nonNull(action) && BatteryStoreModeEnum.find(action).isPresent(); - } + private BatteryStoreModeEnum action; } diff --git a/src/main/java/com/dji/sample/control/model/dto/DrcModeDTO.java b/src/main/java/com/dji/sample/control/model/dto/DrcModeDTO.java deleted file mode 100644 index 771e85a..0000000 --- a/src/main/java/com/dji/sample/control/model/dto/DrcModeDTO.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.dji.sample.control.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 1.3 - * @date 2023/1/12 - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class DrcModeDTO { - - private MqttBrokerDTO mqttBroker; - - /** - * range: 1 - 30 - */ - @Builder.Default - private Integer osdFrequency = 10; - - /** - * range: 1 - 30 - */ - @Builder.Default - private Integer hsiFrequency = 1; -} diff --git a/src/main/java/com/dji/sample/control/model/dto/DrcModeReasonReceiver.java b/src/main/java/com/dji/sample/control/model/dto/DrcModeReasonReceiver.java deleted file mode 100644 index 26429a9..0000000 --- a/src/main/java/com/dji/sample/control/model/dto/DrcModeReasonReceiver.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.dji.sample.control.model.dto; - -import com.dji.sample.control.model.enums.DrcModeReasonEnum; -import lombok.Data; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/14 - */ -@Data -public class DrcModeReasonReceiver { - - private DrcModeReasonEnum reason; -} diff --git a/src/main/java/com/dji/sample/control/model/dto/DrcStatusNotifyReceiver.java b/src/main/java/com/dji/sample/control/model/dto/DrcStatusNotifyReceiver.java deleted file mode 100644 index 1ef8c04..0000000 --- a/src/main/java/com/dji/sample/control/model/dto/DrcStatusNotifyReceiver.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.dji.sample.control.model.dto; - -import com.dji.sample.control.model.enums.DrcStatusErrorEnum; -import com.dji.sample.manage.model.enums.DockDrcStateEnum; -import lombok.Data; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/17 - */ -@Data -public class DrcStatusNotifyReceiver { - - private DrcStatusErrorEnum result; - - private DockDrcStateEnum drcState; -} diff --git a/src/main/java/com/dji/sample/control/model/dto/FlyToProgressReceiver.java b/src/main/java/com/dji/sample/control/model/dto/FlyToProgressReceiver.java deleted file mode 100644 index f51fbf6..0000000 --- a/src/main/java/com/dji/sample/control/model/dto/FlyToProgressReceiver.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.dji.sample.control.model.dto; - -import com.dji.sample.control.model.enums.FlyToStatusEnum; -import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; -import lombok.Data; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/14 - */ -@Data -public class FlyToProgressReceiver { - - private WaylineErrorCodeEnum result; - - private FlyToStatusEnum status; - - private String flyToId; - - private Integer wayPointIndex; -} diff --git a/src/main/java/com/dji/sample/control/model/dto/LinkWorkMode.java b/src/main/java/com/dji/sample/control/model/dto/LinkWorkMode.java index 884330d..599e5d6 100644 --- a/src/main/java/com/dji/sample/control/model/dto/LinkWorkMode.java +++ b/src/main/java/com/dji/sample/control/model/dto/LinkWorkMode.java @@ -1,14 +1,15 @@ package com.dji.sample.control.model.dto; -import com.dji.sample.control.model.enums.LinkWorkModeEnum; import com.dji.sample.control.service.impl.RemoteDebugHandler; +import com.dji.sdk.cloudapi.device.LinkWorkModeEnum; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; +import com.fasterxml.jackson.annotation.JsonValue; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import java.util.Objects; +import java.util.Map; /** * @author sean @@ -17,15 +18,18 @@ import java.util.Objects; */ @EqualsAndHashCode(callSuper = true) @Data -@AllArgsConstructor @NoArgsConstructor public class LinkWorkMode extends RemoteDebugHandler { - @JsonProperty("link_workmode") - private Integer linkWorkMode; + private LinkWorkModeEnum linkWorkMode; - @Override - public boolean valid() { - return Objects.nonNull(linkWorkMode) && LinkWorkModeEnum.find(linkWorkMode).isPresent(); + @JsonCreator + public LinkWorkMode(@JsonProperty("action") Integer linkWorkMode) { + this.linkWorkMode = LinkWorkModeEnum.find(linkWorkMode); + } + + @JsonValue + public Map toMap() { + return Map.of("link_workmode", linkWorkMode.getMode()); } } diff --git a/src/main/java/com/dji/sample/control/model/dto/MqttBrokerDTO.java b/src/main/java/com/dji/sample/control/model/dto/MqttBrokerDTO.java deleted file mode 100644 index af19dba..0000000 --- a/src/main/java/com/dji/sample/control/model/dto/MqttBrokerDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.dji.sample.control.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 1.3 - * @date 2023/1/11 - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class MqttBrokerDTO { - - private String address; - - private String username; - - private String password; - - private String clientId; - - private Long expireTime; - - @Builder.Default - private Boolean enableTls = false; -} diff --git a/src/main/java/com/dji/sample/control/model/dto/PointDTO.java b/src/main/java/com/dji/sample/control/model/dto/PointDTO.java deleted file mode 100644 index 8c6acaf..0000000 --- a/src/main/java/com/dji/sample/control/model/dto/PointDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.dji.sample.control.model.dto; - -import lombok.Data; -import org.hibernate.validator.constraints.Range; - -import javax.validation.constraints.NotNull; - -/** - * @author sean - * @version 1.3 - * @date 2023/2/14 - */ -@Data -public class PointDTO { - - @Range(min = -90, max = 90) - @NotNull - private Double latitude; - - @NotNull - @Range(min = -180, max = 180) - private Double longitude; - - /** - * WGS84 - * The M30 series are ellipsoidal heights. - */ - @NotNull - @Range(min = 2, max = 1500) - private Double height; -} diff --git a/src/main/java/com/dji/sample/control/model/dto/RemoteDebugOpenState.java b/src/main/java/com/dji/sample/control/model/dto/RemoteDebugOpenState.java index 069307d..3e67a3e 100644 --- a/src/main/java/com/dji/sample/control/model/dto/RemoteDebugOpenState.java +++ b/src/main/java/com/dji/sample/control/model/dto/RemoteDebugOpenState.java @@ -1,9 +1,9 @@ package com.dji.sample.control.model.dto; -import com.dji.sample.common.util.SpringBeanUtils; +import com.dji.sample.common.util.SpringBeanUtilsTest; import com.dji.sample.control.service.impl.RemoteDebugHandler; -import com.dji.sample.manage.model.enums.DockModeCodeEnum; import com.dji.sample.manage.service.IDeviceService; +import com.dji.sdk.cloudapi.device.DockModeCodeEnum; import lombok.Data; import lombok.EqualsAndHashCode; @@ -18,7 +18,7 @@ public class RemoteDebugOpenState extends RemoteDebugHandler { @Override public boolean canPublish(String sn) { - IDeviceService deviceService = SpringBeanUtils.getBean(IDeviceService.class); + IDeviceService deviceService = SpringBeanUtilsTest.getBean(IDeviceService.class); DockModeCodeEnum dockMode = deviceService.getDockMode(sn); return DockModeCodeEnum.IDLE == dockMode; } diff --git a/src/main/java/com/dji/sample/control/model/dto/ReturnHomeCancelState.java b/src/main/java/com/dji/sample/control/model/dto/ReturnHomeCancelState.java new file mode 100644 index 0000000..73b203f --- /dev/null +++ b/src/main/java/com/dji/sample/control/model/dto/ReturnHomeCancelState.java @@ -0,0 +1,28 @@ +package com.dji.sample.control.model.dto; + +import com.dji.sample.common.util.SpringBeanUtilsTest; +import com.dji.sample.control.service.impl.RemoteDebugHandler; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sdk.cloudapi.device.DroneModeCodeEnum; +import com.dji.sdk.cloudapi.device.OsdDockDrone; + +/** + * @author sean + * @version 1.4 + * @date 2023/4/19 + */ + +public class ReturnHomeCancelState extends RemoteDebugHandler { + + @Override + public boolean canPublish(String sn) { + IDeviceRedisService deviceRedisService = SpringBeanUtilsTest.getBean(IDeviceRedisService.class); + return deviceRedisService.getDeviceOnline(sn) + .map(DeviceDTO::getChildDeviceSn) + .flatMap(deviceSn -> deviceRedisService.getDeviceOsd(deviceSn, OsdDockDrone.class)) + .map(osd -> DroneModeCodeEnum.RETURN_AUTO == osd.getModeCode()) + .orElse(false); + } + +} diff --git a/src/main/java/com/dji/sample/control/model/dto/ReturnHomeState.java b/src/main/java/com/dji/sample/control/model/dto/ReturnHomeState.java index 66957ab..a30f125 100644 --- a/src/main/java/com/dji/sample/control/model/dto/ReturnHomeState.java +++ b/src/main/java/com/dji/sample/control/model/dto/ReturnHomeState.java @@ -1,10 +1,11 @@ package com.dji.sample.control.model.dto; -import com.dji.sample.common.util.SpringBeanUtils; +import com.dji.sample.common.util.SpringBeanUtilsTest; import com.dji.sample.control.service.impl.RemoteDebugHandler; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sdk.cloudapi.device.DroneModeCodeEnum; +import com.dji.sdk.cloudapi.device.OsdDockDrone; /** * @author sean @@ -16,12 +17,18 @@ public class ReturnHomeState extends RemoteDebugHandler { @Override public boolean canPublish(String sn) { - IDeviceRedisService deviceRedisService = SpringBeanUtils.getBean(IDeviceRedisService.class); + IDeviceRedisService deviceRedisService = SpringBeanUtilsTest.getBean(IDeviceRedisService.class); return deviceRedisService.getDeviceOnline(sn) .map(DeviceDTO::getChildDeviceSn) - .flatMap(deviceSn -> deviceRedisService.getDeviceOsd(deviceSn, OsdSubDeviceReceiver.class)) - .map(OsdSubDeviceReceiver::getElevation) - .map(elevation -> elevation > 0) + .flatMap(deviceSn -> deviceRedisService.getDeviceOsd(deviceSn, OsdDockDrone.class)) + .map(osd -> osd.getElevation() > 0 && modeCodeCanReturnHome(osd.getModeCode())) .orElse(false); } + + private boolean modeCodeCanReturnHome(DroneModeCodeEnum modeCode) { + return DroneModeCodeEnum.TAKEOFF_FINISHED == modeCode || DroneModeCodeEnum.TAKEOFF_AUTO == modeCode + || DroneModeCodeEnum.WAYLINE == modeCode || DroneModeCodeEnum.PANORAMIC_SHOT == modeCode + || DroneModeCodeEnum.ACTIVE_TRACK == modeCode || DroneModeCodeEnum.APAS == modeCode + || DroneModeCodeEnum.VIRTUAL_JOYSTICK == modeCode || DroneModeCodeEnum.MANUAL == modeCode; + } } diff --git a/src/main/java/com/dji/sample/control/model/dto/TakeoffProgressReceiver.java b/src/main/java/com/dji/sample/control/model/dto/TakeoffProgressReceiver.java deleted file mode 100644 index e4f5a83..0000000 --- a/src/main/java/com/dji/sample/control/model/dto/TakeoffProgressReceiver.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dji.sample.control.model.dto; - -import com.dji.sample.control.model.enums.TakeoffStatusEnum; -import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; -import lombok.Data; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/14 - */ -@Data -public class TakeoffProgressReceiver { - - private WaylineErrorCodeEnum result; - - private TakeoffStatusEnum status; - - private String flightId; - - private String trackId; - - private Integer wayPointIndex; - -} diff --git a/src/main/java/com/dji/sample/control/model/enums/BatteryStoreModeEnum.java b/src/main/java/com/dji/sample/control/model/enums/BatteryStoreModeEnum.java deleted file mode 100644 index 9b3d0a8..0000000 --- a/src/main/java/com/dji/sample/control/model/enums/BatteryStoreModeEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.dji.sample.control.model.enums; - -import lombok.Getter; - -import java.util.Arrays; -import java.util.Optional; - -/** - * @author sean - * @version 1.3 - * @date 2022/11/14 - */ -@Getter -public enum BatteryStoreModeEnum { - - PLAN(1), - - EMERGENCY(2); - - Integer mode; - - BatteryStoreModeEnum(Integer mode) { - this.mode = mode; - } - - public static Optional find(int mode) { - return Arrays.stream(BatteryStoreModeEnum.values()).filter(modeEnum -> modeEnum.mode == mode).findAny(); - } -} diff --git a/src/main/java/com/dji/sample/control/model/enums/CameraStateEnum.java b/src/main/java/com/dji/sample/control/model/enums/CameraStateEnum.java deleted file mode 100644 index fbe9f1a..0000000 --- a/src/main/java/com/dji/sample/control/model/enums/CameraStateEnum.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.control.model.enums; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.Arrays; - -/** - * @author sean - * @version 1.4 - * @date 2023/4/23 - */ -public enum CameraStateEnum { - - IDLE, WORKING; - - @JsonValue - public int getVal() { - return ordinal(); - } - - @JsonCreator - public static CameraStateEnum find(int val) { - return Arrays.stream(values()).filter(stateEnum -> stateEnum.ordinal() == val).findAny().get(); - } -} diff --git a/src/main/java/com/dji/sample/control/model/enums/LinkWorkModeEnum.java b/src/main/java/com/dji/sample/control/model/enums/LinkWorkModeEnum.java deleted file mode 100644 index b54b0c3..0000000 --- a/src/main/java/com/dji/sample/control/model/enums/LinkWorkModeEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.dji.sample.control.model.enums; - -import lombok.Getter; - -import java.util.Arrays; -import java.util.Optional; - -/** - * @author sean - * @version 1.3 - * @date 2022/11/25 - */ -@Getter -public enum LinkWorkModeEnum { - - SDR_ONLY(0), - - SDR_WITH_4G(1); - - int mode; - - LinkWorkModeEnum(Integer mode) { - this.mode = mode; - } - - public static Optional find(int mode) { - return Arrays.stream(LinkWorkModeEnum.values()).filter(modeEnum -> modeEnum.mode == mode).findAny(); - } -} diff --git a/src/main/java/com/dji/sample/control/model/enums/PayloadCommandsEnum.java b/src/main/java/com/dji/sample/control/model/enums/PayloadCommandsEnum.java index 1f1241f..6629f8c 100644 --- a/src/main/java/com/dji/sample/control/model/enums/PayloadCommandsEnum.java +++ b/src/main/java/com/dji/sample/control/model/enums/PayloadCommandsEnum.java @@ -1,6 +1,7 @@ package com.dji.sample.control.model.enums; import com.dji.sample.control.service.impl.*; +import com.dji.sdk.cloudapi.control.PayloadControlMethodEnum; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; @@ -13,40 +14,45 @@ import java.util.Arrays; */ public enum PayloadCommandsEnum { - CAMERA_MODE_SWitCH("camera_mode_switch", CameraModeSwitchImpl.class), + CAMERA_MODE_SWitCH(PayloadControlMethodEnum.CAMERA_MODE_SWITCH, CameraModeSwitchImpl.class), - CAMERA_PHOTO_TAKE("camera_photo_take", CameraPhotoTakeImpl.class), + CAMERA_PHOTO_TAKE(PayloadControlMethodEnum.CAMERA_PHOTO_TAKE, CameraPhotoTakeImpl.class), - CAMERA_RECORDING_START("camera_recording_start", CameraRecordingStartImpl.class), + CAMERA_RECORDING_START(PayloadControlMethodEnum.CAMERA_RECORDING_START, CameraRecordingStartImpl.class), - CAMERA_RECORDING_STOP("camera_recording_stop", CameraRecordingStopImpl.class), + CAMERA_RECORDING_STOP(PayloadControlMethodEnum.CAMERA_RECORDING_STOP, CameraRecordingStopImpl.class), - CAMERA_AIM("camera_aim", CameraAimImpl.class), + CAMERA_AIM(PayloadControlMethodEnum.CAMERA_AIM, CameraAimImpl.class), - CAMERA_FOCAL_LENGTH_SET("camera_focal_length_set", CameraFocalLengthSetImpl.class), + CAMERA_FOCAL_LENGTH_SET(PayloadControlMethodEnum.CAMERA_FOCAL_LENGTH_SET, CameraFocalLengthSetImpl.class), - GIMBAL_RESET("gimbal_reset", GimbalResetImpl.class); + GIMBAL_RESET(PayloadControlMethodEnum.GIMBAL_RESET, GimbalResetImpl.class); - String cmd; + PayloadControlMethodEnum cmd; Class clazz; - PayloadCommandsEnum(String cmd, Class clazz) { + PayloadCommandsEnum(PayloadControlMethodEnum cmd, Class clazz) { this.cmd = cmd; this.clazz = clazz; } @JsonValue - public String getCmd() { - return cmd; + public String getMethod() { + return cmd.getPayloadMethod().getMethod(); } public Class getClazz() { return clazz; } + public PayloadControlMethodEnum getCmd() { + return cmd; + } + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) - public static PayloadCommandsEnum find(String cmd) { - return Arrays.stream(values()).filter(cmdEnum -> cmdEnum.cmd.equals(cmd)).findAny().get(); + public static PayloadCommandsEnum find(String method) { + return Arrays.stream(values()).filter(methodEnum -> methodEnum.cmd.getPayloadMethod().getMethod().equals(method)).findAny() + .orElseThrow(); } } diff --git a/src/main/java/com/dji/sample/control/model/enums/RemoteDebugMethodEnum.java b/src/main/java/com/dji/sample/control/model/enums/RemoteDebugMethodEnum.java index 3bfed6e..b4b63f1 100644 --- a/src/main/java/com/dji/sample/control/model/enums/RemoteDebugMethodEnum.java +++ b/src/main/java/com/dji/sample/control/model/enums/RemoteDebugMethodEnum.java @@ -2,9 +2,12 @@ package com.dji.sample.control.model.enums; import com.dji.sample.control.model.dto.*; import com.dji.sample.control.service.impl.RemoteDebugHandler; +import com.dji.sdk.cloudapi.debug.DebugMethodEnum; +import com.fasterxml.jackson.annotation.JsonCreator; import lombok.Getter; import java.util.Arrays; +import java.util.Objects; /** * @author sean @@ -14,67 +17,80 @@ import java.util.Arrays; @Getter public enum RemoteDebugMethodEnum { - DEBUG_MODE_OPEN("debug_mode_open", false, RemoteDebugOpenState.class), + DEBUG_MODE_OPEN(DebugMethodEnum.DEBUG_MODE_OPEN, false, RemoteDebugOpenState.class), - DEBUG_MODE_CLOSE("debug_mode_close", false, null), + DEBUG_MODE_CLOSE(DebugMethodEnum.DEBUG_MODE_CLOSE, false, null), - SUPPLEMENT_LIGHT_OPEN("supplement_light_open", false, null), + SUPPLEMENT_LIGHT_OPEN(DebugMethodEnum.SUPPLEMENT_LIGHT_OPEN, false, null), - SUPPLEMENT_LIGHT_CLOSE("supplement_light_close", false, null), + SUPPLEMENT_LIGHT_CLOSE(DebugMethodEnum.SUPPLEMENT_LIGHT_CLOSE, false, null), RETURN_HOME("return_home", false, ReturnHomeState.class), - DEVICE_REBOOT("device_reboot", true, null), + RETURN_HOME_CANCEL("return_home_cancel", false, ReturnHomeCancelState.class), - DRONE_OPEN("drone_open", true, null), + DEVICE_REBOOT(DebugMethodEnum.DEVICE_REBOOT, true, null), - DRONE_CLOSE("drone_close", true, null), + DRONE_OPEN(DebugMethodEnum.DRONE_OPEN, true, null), - DEVICE_CHECK("device_check", true, null), + DRONE_CLOSE(DebugMethodEnum.DRONE_CLOSE, true, null), - DRONE_FORMAT("drone_format", true, null), + DRONE_FORMAT(DebugMethodEnum.DRONE_FORMAT, true, null), - DEVICE_FORMAT("device_format", true, null), + DEVICE_FORMAT(DebugMethodEnum.DEVICE_FORMAT, true, null), - COVER_OPEN("cover_open", true, null), + COVER_OPEN(DebugMethodEnum.COVER_OPEN, true, null), - COVER_CLOSE("cover_close", true, null), + COVER_CLOSE(DebugMethodEnum.COVER_CLOSE, true, null), - PUTTER_OPEN("putter_open", true, null), + PUTTER_OPEN(DebugMethodEnum.PUTTER_OPEN, true, null), - PUTTER_CLOSE("putter_close", true, null), + PUTTER_CLOSE(DebugMethodEnum.PUTTER_CLOSE, true, null), - CHARGE_OPEN("charge_open", true, null), + CHARGE_OPEN(DebugMethodEnum.CHARGE_OPEN, true, null), - CHARGE_CLOSE("charge_close", true, null), + CHARGE_CLOSE(DebugMethodEnum.CHARGE_CLOSE, true, null), - BATTERY_MAINTENANCE_SWITCH("battery_maintenance_switch", true, AlarmState.class), - - ALARM_STATE_SWITCH("alarm_state_switch", true, AlarmState.class), - - BATTERY_STORE_MODE_SWITCH("battery_store_mode_switch", true, BatteryStoreMode.class), + BATTERY_MAINTENANCE_SWITCH(DebugMethodEnum.BATTERY_MAINTENANCE_SWITCH, false, AlarmState.class), - SDR_WORK_MODE_SWITCH("sdr_workmode_switch", false, LinkWorkMode.class), + ALARM_STATE_SWITCH(DebugMethodEnum.ALARM_STATE_SWITCH, false, AlarmState.class), - UNKNOWN("unknown", false, null); + BATTERY_STORE_MODE_SWITCH(DebugMethodEnum.BATTERY_STORE_MODE_SWITCH, false, BatteryStoreMode.class), + + SDR_WORK_MODE_SWITCH(DebugMethodEnum.SDR_WORKMODE_SWITCH, false, LinkWorkMode.class), + + AIR_CONDITIONER_MODE_SWITCH(DebugMethodEnum.AIR_CONDITIONER_MODE_SWITCH, false, AirConditionerMode.class); + + private DebugMethodEnum debugMethodEnum; private String method; - private Boolean progress; + private boolean progress; private Class clazz; - RemoteDebugMethodEnum(String method, Boolean progress, Class clazz) { - this.method = method; + RemoteDebugMethodEnum(DebugMethodEnum debugMethodEnum, boolean progress, Class clazz) { + this.debugMethodEnum = debugMethodEnum; this.progress = progress; this.clazz = clazz; + this.method = debugMethodEnum.getMethod(); + } + + RemoteDebugMethodEnum(String method, boolean progress, Class clazz) { + this.debugMethodEnum = null; + this.progress = progress; + this.clazz = clazz; + this.method = method; } + @JsonCreator public static RemoteDebugMethodEnum find(String method) { - return Arrays.stream(RemoteDebugMethodEnum.values()) - .filter(methodEnum -> methodEnum.method.equals(method)) + return Arrays.stream(values()) + .filter(methodEnum -> methodEnum.method.equals(method) + || (Objects.nonNull(methodEnum.debugMethodEnum) + && methodEnum.debugMethodEnum.getMethod().equals(method))) .findAny() - .orElse(UNKNOWN); + .orElseThrow(); } } diff --git a/src/main/java/com/dji/sample/control/model/param/DrcModeParam.java b/src/main/java/com/dji/sample/control/model/param/DrcModeParam.java index a750dc5..d483da1 100644 --- a/src/main/java/com/dji/sample/control/model/param/DrcModeParam.java +++ b/src/main/java/com/dji/sample/control/model/param/DrcModeParam.java @@ -28,8 +28,10 @@ public class DrcModeParam { private String dockSn; @Range(min = 1800, max = 86400) + @Builder.Default private long expireSec = RedisConst.DRC_MODE_ALIVE_SECOND; @Valid + @Builder.Default private DeviceDrcInfoParam deviceInfo = new DeviceDrcInfoParam(); } diff --git a/src/main/java/com/dji/sample/control/model/param/DronePayloadParam.java b/src/main/java/com/dji/sample/control/model/param/DronePayloadParam.java index 97d156e..de9aa21 100644 --- a/src/main/java/com/dji/sample/control/model/param/DronePayloadParam.java +++ b/src/main/java/com/dji/sample/control/model/param/DronePayloadParam.java @@ -1,8 +1,8 @@ package com.dji.sample.control.model.param; -import com.dji.sample.control.model.enums.CameraModeEnum; -import com.dji.sample.control.model.enums.CameraTypeEnum; -import com.dji.sample.control.model.enums.GimbalResetModeEnum; +import com.dji.sdk.cloudapi.control.CameraTypeEnum; +import com.dji.sdk.cloudapi.control.GimbalResetModeEnum; +import com.dji.sdk.cloudapi.device.CameraModeEnum; import lombok.Data; import org.hibernate.validator.constraints.Range; diff --git a/src/main/java/com/dji/sample/control/model/param/FlyToPointParam.java b/src/main/java/com/dji/sample/control/model/param/FlyToPointParam.java index 98b03e1..d003c67 100644 --- a/src/main/java/com/dji/sample/control/model/param/FlyToPointParam.java +++ b/src/main/java/com/dji/sample/control/model/param/FlyToPointParam.java @@ -1,6 +1,6 @@ package com.dji.sample.control.model.param; -import com.dji.sample.control.model.dto.PointDTO; +import com.dji.sdk.cloudapi.control.Point; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -33,5 +33,5 @@ public class FlyToPointParam { @Size(min = 1) @Valid @NotNull - private List points; + private List points; } diff --git a/src/main/java/com/dji/sample/control/model/param/RemoteDebugParam.java b/src/main/java/com/dji/sample/control/model/param/RemoteDebugParam.java index 4d48659..8678d3a 100644 --- a/src/main/java/com/dji/sample/control/model/param/RemoteDebugParam.java +++ b/src/main/java/com/dji/sample/control/model/param/RemoteDebugParam.java @@ -2,6 +2,8 @@ package com.dji.sample.control.model.param; import lombok.Data; +import javax.validation.constraints.NotNull; + /** * @author sean * @version 1.3 @@ -10,6 +12,7 @@ import lombok.Data; @Data public class RemoteDebugParam { + @NotNull private Integer action; } diff --git a/src/main/java/com/dji/sample/control/model/param/TakeoffToPointParam.java b/src/main/java/com/dji/sample/control/model/param/TakeoffToPointParam.java index 4de1273..d2e79fc 100644 --- a/src/main/java/com/dji/sample/control/model/param/TakeoffToPointParam.java +++ b/src/main/java/com/dji/sample/control/model/param/TakeoffToPointParam.java @@ -1,10 +1,15 @@ package com.dji.sample.control.model.param; -import com.dji.sample.manage.model.enums.DroneRcLostActionEnum; -import com.dji.sample.manage.model.enums.WaylineRcLostActionEnum; +import com.dji.sdk.cloudapi.control.CommanderFlightModeEnum; +import com.dji.sdk.cloudapi.control.CommanderModeLostActionEnum; +import com.dji.sdk.cloudapi.device.ExitWaylineWhenRcLostEnum; +import com.dji.sdk.cloudapi.device.RcLostActionEnum; +import com.dji.sdk.cloudapi.wayline.RthModeEnum; import lombok.Data; import org.hibernate.validator.constraints.Range; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; /** @@ -25,7 +30,7 @@ public class TakeoffToPointParam { @NotNull private Double targetLatitude; - @Range(min = 2, max = 1500) + @Range(min = 2, max = 10000) @NotNull private Double targetHeight; @@ -38,12 +43,22 @@ public class TakeoffToPointParam { private Double rthAltitude; @NotNull - private DroneRcLostActionEnum rcLostAction; + private RcLostActionEnum rcLostAction; @NotNull - private WaylineRcLostActionEnum exitWaylineWhenRcLost; + private ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost; @Range(min = 1, max = 15) @NotNull private Double maxSpeed; + + private RthModeEnum rthMode; + + private CommanderModeLostActionEnum commanderModeLostAction; + + private CommanderFlightModeEnum commanderFlightMode; + + @Min(2) + @Max(3000) + private Float commanderFlightHeight; } diff --git a/src/main/java/com/dji/sample/control/service/IControlService.java b/src/main/java/com/dji/sample/control/service/IControlService.java index f9e122b..d0a34ce 100644 --- a/src/main/java/com/dji/sample/control/service/IControlService.java +++ b/src/main/java/com/dji/sample/control/service/IControlService.java @@ -1,10 +1,9 @@ package com.dji.sample.control.service; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.control.model.enums.DroneAuthorityEnum; +import com.dji.sample.control.model.enums.RemoteDebugMethodEnum; import com.dji.sample.control.model.param.*; -import org.springframework.messaging.MessageHeaders; +import com.dji.sdk.common.HttpResultResponse; /** * @author sean @@ -20,7 +19,7 @@ public interface IControlService { * @param param * @return */ - ResponseResult controlDockDebug(String sn, String serviceIdentifier, RemoteDebugParam param); + HttpResultResponse controlDockDebug(String sn, RemoteDebugMethodEnum serviceIdentifier, RemoteDebugParam param); /** * Make the drone fly to the target point. @@ -28,14 +27,14 @@ public interface IControlService { * @param param * @return */ - ResponseResult flyToPoint(String sn, FlyToPointParam param); + HttpResultResponse flyToPoint(String sn, FlyToPointParam param); /** * End the mission of flying the drone to the target point. * @param sn * @return */ - ResponseResult flyToPointStop(String sn); + HttpResultResponse flyToPointStop(String sn); /** * Handle progress result notifications for fly to target point. @@ -43,7 +42,7 @@ public interface IControlService { * @param headers * @return */ - CommonTopicReceiver handleFlyToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers); +// CommonTopicReceiver handleFlyToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers); /** * Control the drone to take off. @@ -51,15 +50,7 @@ public interface IControlService { * @param param * @return */ - ResponseResult takeoffToPoint(String sn, TakeoffToPointParam param); - - /** - * Handle progress result notifications for takeoff to target point. - * @param receiver - * @param headers - * @return - */ - CommonTopicReceiver handleTakeoffToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers); + HttpResultResponse takeoffToPoint(String sn, TakeoffToPointParam param); /** * Seize the control authority of the drone or the payload control authority. @@ -68,12 +59,12 @@ public interface IControlService { * @param param * @return */ - ResponseResult seizeAuthority(String sn, DroneAuthorityEnum authority, DronePayloadParam param); + HttpResultResponse seizeAuthority(String sn, DroneAuthorityEnum authority, DronePayloadParam param); /** * Control the payload of the drone. * @param param * @return */ - ResponseResult payloadCommands(PayloadCommandsParam param) throws Exception; + HttpResultResponse payloadCommands(PayloadCommandsParam param) throws Exception; } diff --git a/src/main/java/com/dji/sample/control/service/IDrcService.java b/src/main/java/com/dji/sample/control/service/IDrcService.java index 505f526..55d0b75 100644 --- a/src/main/java/com/dji/sample/control/service/IDrcService.java +++ b/src/main/java/com/dji/sample/control/service/IDrcService.java @@ -1,9 +1,9 @@ package com.dji.sample.control.service; import com.dji.sample.control.model.dto.JwtAclDTO; -import com.dji.sample.control.model.dto.MqttBrokerDTO; import com.dji.sample.control.model.param.DrcConnectParam; import com.dji.sample.control.model.param.DrcModeParam; +import com.dji.sdk.cloudapi.control.DrcModeMqttBroker; /** * @author sean @@ -41,7 +41,7 @@ public interface IDrcService { * @param param * @return */ - MqttBrokerDTO userDrcAuth(String workspaceId, String userId, String username, DrcConnectParam param); + DrcModeMqttBroker userDrcAuth(String workspaceId, String userId, String username, DrcConnectParam param); /** * Make the dock enter drc mode. And grant relevant permissions. diff --git a/src/main/java/com/dji/sample/control/service/impl/CameraFocalLengthSetImpl.java b/src/main/java/com/dji/sample/control/service/impl/CameraFocalLengthSetImpl.java index d8b1450..e37f8a3 100644 --- a/src/main/java/com/dji/sample/control/service/impl/CameraFocalLengthSetImpl.java +++ b/src/main/java/com/dji/sample/control/service/impl/CameraFocalLengthSetImpl.java @@ -1,8 +1,8 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.control.model.enums.CameraStateEnum; -import com.dji.sample.control.model.enums.CameraTypeEnum; import com.dji.sample.control.model.param.DronePayloadParam; +import com.dji.sdk.cloudapi.control.CameraTypeEnum; +import com.dji.sdk.cloudapi.device.CameraStateEnum; import java.util.Objects; @@ -32,7 +32,8 @@ public class CameraFocalLengthSetImpl extends PayloadCommandsHandler { } switch (param.getCameraType()) { case IR: - return param.getZoomFactor().intValue() != osdCamera.getIrZoomFactor(); + return Objects.nonNull(osdCamera.getIrZoomFactor()) + && param.getZoomFactor().intValue() != osdCamera.getIrZoomFactor(); case ZOOM: return param.getZoomFactor().intValue() != osdCamera.getZoomFactor(); } diff --git a/src/main/java/com/dji/sample/control/service/impl/CameraModeSwitchImpl.java b/src/main/java/com/dji/sample/control/service/impl/CameraModeSwitchImpl.java index b20dbf0..9d2db66 100644 --- a/src/main/java/com/dji/sample/control/service/impl/CameraModeSwitchImpl.java +++ b/src/main/java/com/dji/sample/control/service/impl/CameraModeSwitchImpl.java @@ -1,7 +1,7 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.control.model.enums.CameraStateEnum; import com.dji.sample.control.model.param.DronePayloadParam; +import com.dji.sdk.cloudapi.device.CameraStateEnum; import java.util.Objects; diff --git a/src/main/java/com/dji/sample/control/service/impl/CameraPhotoTakeImpl.java b/src/main/java/com/dji/sample/control/service/impl/CameraPhotoTakeImpl.java index 79034b3..d9f6bcc 100644 --- a/src/main/java/com/dji/sample/control/service/impl/CameraPhotoTakeImpl.java +++ b/src/main/java/com/dji/sample/control/service/impl/CameraPhotoTakeImpl.java @@ -1,7 +1,7 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.control.model.enums.CameraStateEnum; import com.dji.sample.control.model.param.DronePayloadParam; +import com.dji.sdk.cloudapi.device.CameraStateEnum; /** * @author sean diff --git a/src/main/java/com/dji/sample/control/service/impl/CameraRecordingStartImpl.java b/src/main/java/com/dji/sample/control/service/impl/CameraRecordingStartImpl.java index afe7f79..0a9960b 100644 --- a/src/main/java/com/dji/sample/control/service/impl/CameraRecordingStartImpl.java +++ b/src/main/java/com/dji/sample/control/service/impl/CameraRecordingStartImpl.java @@ -1,8 +1,8 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.control.model.enums.CameraModeEnum; -import com.dji.sample.control.model.enums.CameraStateEnum; import com.dji.sample.control.model.param.DronePayloadParam; +import com.dji.sdk.cloudapi.device.CameraModeEnum; +import com.dji.sdk.cloudapi.device.CameraStateEnum; /** * @author sean diff --git a/src/main/java/com/dji/sample/control/service/impl/CameraRecordingStopImpl.java b/src/main/java/com/dji/sample/control/service/impl/CameraRecordingStopImpl.java index 929816b..22e59af 100644 --- a/src/main/java/com/dji/sample/control/service/impl/CameraRecordingStopImpl.java +++ b/src/main/java/com/dji/sample/control/service/impl/CameraRecordingStopImpl.java @@ -1,7 +1,7 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.control.model.enums.CameraStateEnum; import com.dji.sample.control.model.param.DronePayloadParam; +import com.dji.sdk.cloudapi.device.CameraStateEnum; /** * @author sean diff --git a/src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java b/src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java index d9d43a7..530729e 100644 --- a/src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java +++ b/src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java @@ -1,35 +1,33 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.common.error.CommonErrorEnum; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.IMessageSenderService; -import com.dji.sample.component.redis.RedisConst; -import com.dji.sample.component.redis.RedisOpsUtils; -import com.dji.sample.component.websocket.model.BizCodeEnum; -import com.dji.sample.component.websocket.service.ISendMessageService; -import com.dji.sample.control.model.dto.FlyToProgressReceiver; -import com.dji.sample.control.model.dto.ResultNotifyDTO; -import com.dji.sample.control.model.dto.TakeoffProgressReceiver; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.control.model.enums.DroneAuthorityEnum; -import com.dji.sample.control.model.enums.DroneControlMethodEnum; import com.dji.sample.control.model.enums.RemoteDebugMethodEnum; import com.dji.sample.control.model.param.*; import com.dji.sample.control.service.IControlService; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; -import com.dji.sample.manage.model.enums.DockModeCodeEnum; -import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.service.IDevicePayloadService; import com.dji.sample.manage.service.IDeviceRedisService; import com.dji.sample.manage.service.IDeviceService; -import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; -import com.fasterxml.jackson.core.type.TypeReference; +import com.dji.sdk.cloudapi.control.FlyToPointRequest; +import com.dji.sdk.cloudapi.control.PayloadAuthorityGrabRequest; +import com.dji.sdk.cloudapi.control.TakeoffToPointRequest; +import com.dji.sdk.cloudapi.control.api.AbstractControlService; +import com.dji.sdk.cloudapi.debug.DebugMethodEnum; +import com.dji.sdk.cloudapi.debug.api.AbstractDebugService; +import com.dji.sdk.cloudapi.device.DockModeCodeEnum; +import com.dji.sdk.cloudapi.device.DroneModeCodeEnum; +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.cloudapi.wayline.api.AbstractWaylineService; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.messaging.MessageHeaders; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import java.util.Objects; @@ -46,10 +44,7 @@ import java.util.UUID; public class ControlServiceImpl implements IControlService { @Autowired - private IMessageSenderService messageSenderService; - - @Autowired - private ISendMessageService webSocketMessageService; + private IWebSocketMessageService webSocketMessageService; @Autowired private IDeviceService deviceService; @@ -63,6 +58,16 @@ public class ControlServiceImpl implements IControlService { @Autowired private IDevicePayloadService devicePayloadService; + @Autowired + private AbstractControlService abstractControlService; + + @Autowired + private AbstractDebugService abstractDebugService; + + @Autowired + @Qualifier("SDKWaylineService") + private AbstractWaylineService abstractWaylineService; + private RemoteDebugHandler checkDebugCondition(String sn, RemoteDebugParam param, RemoteDebugMethodEnum controlMethodEnum) { RemoteDebugHandler handler = Objects.nonNull(controlMethodEnum.getClazz()) ? mapper.convertValue(Objects.nonNull(param) ? param : new Object(), controlMethodEnum.getClazz()) @@ -70,81 +75,35 @@ public class ControlServiceImpl implements IControlService { if (!handler.canPublish(sn)) { throw new RuntimeException("The current state of the dock does not support this function."); } - if (Objects.nonNull(param) && !handler.valid()) { - throw new RuntimeException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg()); - } return handler; } @Override - public ResponseResult controlDockDebug(String sn, String serviceIdentifier, RemoteDebugParam param) { - RemoteDebugMethodEnum controlMethodEnum = RemoteDebugMethodEnum.find(serviceIdentifier); - if (RemoteDebugMethodEnum.UNKNOWN == controlMethodEnum) { - return ResponseResult.error("The " + serviceIdentifier + " method does not exist."); - } - + public HttpResultResponse controlDockDebug(String sn, RemoteDebugMethodEnum controlMethodEnum, RemoteDebugParam param) { + DebugMethodEnum methodEnum = controlMethodEnum.getDebugMethodEnum(); RemoteDebugHandler data = checkDebugCondition(sn, param, controlMethodEnum); boolean isExist = deviceRedisService.checkDeviceOnline(sn); if (!isExist) { - return ResponseResult.error("The dock is offline."); + return HttpResultResponse.error("The dock is offline."); } - String bid = UUID.randomUUID().toString(); - ServiceReply serviceReply = messageSenderService.publishServicesTopic(sn, serviceIdentifier, data, bid); - - if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { - return ResponseResult.error(serviceReply.getResult(), - "error: " + serviceIdentifier + serviceReply.getResult()); - } - if (controlMethodEnum.getProgress()) { - RedisOpsUtils.setWithExpire(serviceIdentifier + RedisConst.DELIMITER + bid, sn, - RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); - } - return ResponseResult.success(); - } - - /** - * Handles multi-state command progress information. - * @param receiver - * @param headers - * @return - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleControlProgress(CommonTopicReceiver receiver, MessageHeaders headers) { - String key = receiver.getMethod() + RedisConst.DELIMITER + receiver.getBid(); - if (RedisOpsUtils.getExpire(key) <= 0) { - return receiver; - } - String sn = RedisOpsUtils.get(key).toString(); - - EventsReceiver eventsReceiver = mapper.convertValue(receiver.getData(), - new TypeReference>(){}); - eventsReceiver.setBid(receiver.getBid()); - eventsReceiver.setSn(sn); - - log.info("SN: {}, {} ===> Control progress: {}", - sn, receiver.getMethod(), eventsReceiver.getOutput().getProgress().toString()); - - if (eventsReceiver.getResult() != ResponseResult.CODE_SUCCESS) { - log.error("SN: {}, {} ===> Error code: {}", sn, receiver.getMethod(), eventsReceiver.getResult()); - } - - if (eventsReceiver.getOutput().getProgress().getPercent() == 100 || - EventsResultStatusEnum.find(eventsReceiver.getOutput().getStatus()).getEnd()) { - RedisOpsUtils.del(key); + TopicServicesResponse response; + switch (controlMethodEnum) { + case RETURN_HOME: + response = abstractWaylineService.returnHome(SDKManager.getDeviceSDK(sn)); + break; + case RETURN_HOME_CANCEL: + response = abstractWaylineService.returnHomeCancel(SDKManager.getDeviceSDK(sn)); + break; + default: + response = abstractDebugService.remoteDebug(SDKManager.getDeviceSDK(sn), methodEnum, + Objects.nonNull(methodEnum.getClazz()) ? mapper.convertValue(data, methodEnum.getClazz()) : null); } - - Optional deviceOpt = deviceRedisService.getDeviceOnline(sn); - - if (deviceOpt.isEmpty()) { - throw new RuntimeException("The device is offline."); + ServicesReplyData serviceReply = (ServicesReplyData) response.getData(); + if (!serviceReply.getResult().isSuccess()) { + return HttpResultResponse.error(serviceReply.getResult()); } - - DeviceDTO device = deviceOpt.get(); - webSocketMessageService.sendBatch(device.getWorkspaceId(), UserTypeEnum.WEB.getVal(), - receiver.getMethod(), eventsReceiver); - - return receiver; + return HttpResultResponse.success(); } private void checkFlyToCondition(String dockSn) { @@ -154,55 +113,38 @@ public class ControlServiceImpl implements IControlService { throw new RuntimeException("The dock is offline, please restart the dock."); } - DeviceModeCodeEnum deviceMode = deviceService.getDeviceMode(dockOpt.get().getChildDeviceSn()); - if (DeviceModeCodeEnum.MANUAL != deviceMode) { + DroneModeCodeEnum deviceMode = deviceService.getDeviceMode(dockOpt.get().getChildDeviceSn()); + if (DroneModeCodeEnum.MANUAL != deviceMode) { throw new RuntimeException("The current state of the drone does not support this function, please try again later."); } - ResponseResult result = seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); - if (ResponseResult.CODE_SUCCESS != result.getCode()) { + HttpResultResponse result = seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); + if (HttpResultResponse.CODE_SUCCESS != result.getCode()) { throw new IllegalArgumentException(result.getMessage()); } } @Override - public ResponseResult flyToPoint(String sn, FlyToPointParam param) { + public HttpResultResponse flyToPoint(String sn, FlyToPointParam param) { checkFlyToCondition(sn); param.setFlyToId(UUID.randomUUID().toString()); - ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.FLY_TO_POINT.getMethod(), param, param.getFlyToId()); - return ResponseResult.CODE_SUCCESS != reply.getResult() ? - ResponseResult.error("Flying to the target point failed." + reply.getResult()) - : ResponseResult.success(); + TopicServicesResponse response = abstractControlService.flyToPoint( + SDKManager.getDeviceSDK(sn), mapper.convertValue(param, FlyToPointRequest.class)); + ServicesReplyData reply = response.getData(); + return reply.getResult().isSuccess() ? + HttpResultResponse.success() + : HttpResultResponse.error("Flying to the target point failed. " + reply.getResult()); } @Override - public ResponseResult flyToPointStop(String sn) { - ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.FLY_TO_POINT_STOP.getMethod(), null); - return ResponseResult.CODE_SUCCESS != reply.getResult() ? - ResponseResult.error("The drone flying to the target point failed to stop. " + reply.getResult()) - : ResponseResult.success(); - } - - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleFlyToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers) { - String dockSn = receiver.getGateway(); - - Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); - if (deviceOpt.isEmpty()) { - log.error("The dock is offline."); - return null; - } + public HttpResultResponse flyToPointStop(String sn) { + TopicServicesResponse response = abstractControlService.flyToPointStop(SDKManager.getDeviceSDK(sn)); + ServicesReplyData reply = response.getData(); - FlyToProgressReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference(){}); - webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), - BizCodeEnum.FLY_TO_POINT_PROGRESS.getCode(), - ResultNotifyDTO.builder().sn(dockSn) - .message(WaylineErrorCodeEnum.SUCCESS == eventsReceiver.getResult() ? - eventsReceiver.getStatus().getMessage() : eventsReceiver.getResult().getErrorMsg()) - .result(eventsReceiver.getResult().getErrorCode()) - .build()); - return receiver; + return reply.getResult().isSuccess() ? + HttpResultResponse.success() + : HttpResultResponse.error("The drone flying to the target point failed to stop. " + reply.getResult()); } private void checkTakeoffCondition(String dockSn) { @@ -211,69 +153,51 @@ public class ControlServiceImpl implements IControlService { throw new RuntimeException("The current state does not support takeoff."); } - ResponseResult result = seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); - if (ResponseResult.CODE_SUCCESS != result.getCode()) { + HttpResultResponse result = seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); + if (HttpResultResponse.CODE_SUCCESS != result.getCode()) { throw new IllegalArgumentException(result.getMessage()); } } @Override - public ResponseResult takeoffToPoint(String sn, TakeoffToPointParam param) { + public HttpResultResponse takeoffToPoint(String sn, TakeoffToPointParam param) { checkTakeoffCondition(sn); param.setFlightId(UUID.randomUUID().toString()); - ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.TAKE_OFF_TO_POINT.getMethod(), param, param.getFlightId()); - return ResponseResult.CODE_SUCCESS != reply.getResult() ? - ResponseResult.error("The drone failed to take off. " + reply.getResult()) - : ResponseResult.success(); - } - - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleTakeoffToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers) { - String dockSn = receiver.getGateway(); - - Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); - if (deviceOpt.isEmpty()) { - log.error("The dock is offline."); - return null; - } - TakeoffProgressReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference(){}); - - webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), - BizCodeEnum.TAKE_OFF_TO_POINT_PROGRESS.getCode(), - ResultNotifyDTO.builder().sn(dockSn) - .message(WaylineErrorCodeEnum.SUCCESS == eventsReceiver.getResult() ? - eventsReceiver.getStatus().getMessage() : eventsReceiver.getResult().getErrorMsg()) - .result(eventsReceiver.getResult().getErrorCode()) - .build()); - - return receiver; + TopicServicesResponse response = abstractControlService.takeoffToPoint( + SDKManager.getDeviceSDK(sn), mapper.convertValue(param, TakeoffToPointRequest.class)); + ServicesReplyData reply = response.getData(); + return reply.getResult().isSuccess() ? + HttpResultResponse.success() + : HttpResultResponse.error("The drone failed to take off. " + reply.getResult()); } @Override - public ResponseResult seizeAuthority(String sn, DroneAuthorityEnum authority, DronePayloadParam param) { - String method; + public HttpResultResponse seizeAuthority(String sn, DroneAuthorityEnum authority, DronePayloadParam param) { + TopicServicesResponse response; switch (authority) { case FLIGHT: if (deviceService.checkAuthorityFlight(sn)) { - return ResponseResult.success(); + return HttpResultResponse.success(); } - method = DroneControlMethodEnum.FLIGHT_AUTHORITY_GRAB.getMethod(); + response = abstractControlService.flightAuthorityGrab(SDKManager.getDeviceSDK(sn)); break; case PAYLOAD: if (checkPayloadAuthority(sn, param.getPayloadIndex())) { - return ResponseResult.success(); + return HttpResultResponse.success(); } - method = DroneControlMethodEnum.PAYLOAD_AUTHORITY_GRAB.getMethod(); + response = abstractControlService.payloadAuthorityGrab(SDKManager.getDeviceSDK(sn), + new PayloadAuthorityGrabRequest().setPayloadIndex(new PayloadIndex(param.getPayloadIndex()))); break; default: - return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); + return HttpResultResponse.error(CloudSDKErrorEnum.INVALID_PARAMETER); } - ServiceReply serviceReply = messageSenderService.publishServicesTopic(sn, method, param); - return ResponseResult.CODE_SUCCESS != serviceReply.getResult() ? - ResponseResult.error(serviceReply.getResult(), "Method: " + method + " Error Code:" + serviceReply.getResult()) - : ResponseResult.success(); + + ServicesReplyData serviceReply = response.getData(); + return serviceReply.getResult().isSuccess() ? + HttpResultResponse.success() + : HttpResultResponse.error(serviceReply.getResult()); } private Boolean checkPayloadAuthority(String sn, String payloadIndex) { @@ -284,18 +208,20 @@ public class ControlServiceImpl implements IControlService { return devicePayloadService.checkAuthorityPayload(dockOpt.get().getChildDeviceSn(), payloadIndex); } - @Override - public ResponseResult payloadCommands(PayloadCommandsParam param) throws Exception { + public HttpResultResponse payloadCommands(PayloadCommandsParam param) throws Exception { param.getCmd().getClazz() .getDeclaredConstructor(DronePayloadParam.class) .newInstance(param.getData()) .checkCondition(param.getSn()); - ServiceReply serviceReply = messageSenderService.publishServicesTopic(param.getSn(), param.getCmd().getCmd(), param.getData()); - return ResponseResult.CODE_SUCCESS != serviceReply.getResult() ? - ResponseResult.error(serviceReply.getResult(), " Error Code:" + serviceReply.getResult()) - : ResponseResult.success(); - } + TopicServicesResponse response = abstractControlService.payloadControl( + SDKManager.getDeviceSDK(param.getSn()), param.getCmd().getCmd(), + mapper.convertValue(param.getData(), param.getCmd().getCmd().getClazz())); + ServicesReplyData serviceReply = response.getData(); + return serviceReply.getResult().isSuccess() ? + HttpResultResponse.success() + : HttpResultResponse.error(serviceReply.getResult()); + } } diff --git a/src/main/java/com/dji/sample/control/service/impl/DrcServiceImpl.java b/src/main/java/com/dji/sample/control/service/impl/DrcServiceImpl.java index 7e089cc..7aa86f3 100644 --- a/src/main/java/com/dji/sample/control/service/impl/DrcServiceImpl.java +++ b/src/main/java/com/dji/sample/control/service/impl/DrcServiceImpl.java @@ -1,16 +1,12 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.config.MqttConfiguration; -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.IMessageSenderService; +import com.dji.sample.component.mqtt.config.MqttPropertyConfiguration; +import com.dji.sample.component.mqtt.model.EventsReceiver; +import com.dji.sample.component.mqtt.model.MapKeyConst; 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.service.ISendMessageService; -import com.dji.sample.control.model.dto.*; -import com.dji.sample.control.model.enums.DrcMethodEnum; -import com.dji.sample.control.model.enums.DrcStatusErrorEnum; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; +import com.dji.sample.control.model.dto.JwtAclDTO; import com.dji.sample.control.model.enums.DroneAuthorityEnum; import com.dji.sample.control.model.enums.MqttAclAccessEnum; import com.dji.sample.control.model.param.DrcConnectParam; @@ -18,23 +14,29 @@ import com.dji.sample.control.model.param.DrcModeParam; import com.dji.sample.control.service.IControlService; import com.dji.sample.control.service.IDrcService; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.enums.DockModeCodeEnum; -import com.dji.sample.manage.model.enums.UserTypeEnum; -import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; import com.dji.sample.manage.service.IDeviceRedisService; import com.dji.sample.manage.service.IDeviceService; -import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; import com.dji.sample.wayline.model.enums.WaylineTaskStatusEnum; import com.dji.sample.wayline.model.param.UpdateJobParam; +import com.dji.sample.wayline.service.IFlightTaskService; import com.dji.sample.wayline.service.IWaylineJobService; import com.dji.sample.wayline.service.IWaylineRedisService; +import com.dji.sdk.cloudapi.control.DrcModeEnterRequest; +import com.dji.sdk.cloudapi.control.DrcModeMqttBroker; +import com.dji.sdk.cloudapi.control.api.AbstractControlService; +import com.dji.sdk.cloudapi.device.DockModeCodeEnum; +import com.dji.sdk.cloudapi.device.OsdDockDrone; +import com.dji.sdk.cloudapi.wayline.FlighttaskProgress; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.mqtt.TopicConst; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -52,15 +54,15 @@ import java.util.Optional; @Slf4j public class DrcServiceImpl implements IDrcService { - @Autowired - private IMessageSenderService messageSenderService; - @Autowired private ObjectMapper objectMapper; @Autowired private IWaylineJobService waylineJobService; + @Autowired + private IFlightTaskService flighttaskService; + @Autowired private IDeviceService deviceService; @@ -68,7 +70,7 @@ public class DrcServiceImpl implements IDrcService { private ObjectMapper mapper; @Autowired - private ISendMessageService webSocketMessageService; + private IWebSocketMessageService webSocketMessageService; @Autowired private IControlService controlService; @@ -79,6 +81,9 @@ public class DrcServiceImpl implements IDrcService { @Autowired private IWaylineRedisService waylineRedisService; + @Autowired + private AbstractControlService abstractControlService; + @Override public void setDrcModeInRedis(String dockSn, String clientId) { RedisOpsUtils.setWithExpire(RedisConst.DRC_PREFIX + dockSn, clientId, RedisConst.DRC_MODE_ALIVE_SECOND); @@ -95,7 +100,7 @@ public class DrcServiceImpl implements IDrcService { } @Override - public MqttBrokerDTO userDrcAuth(String workspaceId, String userId, String username, DrcConnectParam param) { + public DrcModeMqttBroker userDrcAuth(String workspaceId, String userId, String username, DrcConnectParam param) { // refresh token String clientId = param.getClientId(); @@ -110,7 +115,7 @@ public class DrcServiceImpl implements IDrcService { try { RedisOpsUtils.expireKey(key, RedisConst.DRC_MODE_ALIVE_SECOND); - return MqttConfiguration.getMqttBrokerWithDrc( + return MqttPropertyConfiguration.getMqttBrokerWithDrc( clientId, username, param.getExpireSec(), Collections.emptyMap()); } catch (RuntimeException e) { RedisOpsUtils.del(key); @@ -119,16 +124,16 @@ public class DrcServiceImpl implements IDrcService { } private void checkDrcModeCondition(String workspaceId, String dockSn) { - Optional> runningOpt = waylineRedisService.getRunningWaylineJob(dockSn); + Optional> runningOpt = waylineRedisService.getRunningWaylineJob(dockSn); if (runningOpt.isPresent() && WaylineJobStatusEnum.IN_PROGRESS == waylineJobService.getWaylineState(dockSn)) { - waylineJobService.updateJobStatus(workspaceId, runningOpt.get().getBid(), + flighttaskService.updateJobStatus(workspaceId, runningOpt.get().getBid(), UpdateJobParam.builder().status(WaylineTaskStatusEnum.PAUSE).build()); } DockModeCodeEnum dockMode = deviceService.getDockMode(dockSn); Optional dockOpt = deviceRedisService.getDeviceOnline(dockSn); if (dockOpt.isPresent() && (DockModeCodeEnum.IDLE == dockMode || DockModeCodeEnum.WORKING == dockMode)) { - Optional deviceOsd = deviceRedisService.getDeviceOsd(dockOpt.get().getChildDeviceSn(), OsdSubDeviceReceiver.class); + Optional deviceOsd = deviceRedisService.getDeviceOsd(dockOpt.get().getChildDeviceSn(), OsdDockDrone.class); if (deviceOsd.isEmpty() || deviceOsd.get().getElevation() <= 0) { throw new RuntimeException("The drone is not in the sky and cannot enter command flight mode."); } @@ -136,8 +141,8 @@ public class DrcServiceImpl implements IDrcService { throw new RuntimeException("The current state of the dock does not support entering command flight mode."); } - ResponseResult result = controlService.seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); - if (ResponseResult.CODE_SUCCESS != result.getCode()) { + HttpResultResponse result = controlService.seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); + if (HttpResultResponse.CODE_SUCCESS != result.getCode()) { throw new IllegalArgumentException(result.getMessage()); } @@ -158,19 +163,20 @@ public class DrcServiceImpl implements IDrcService { checkDrcModeCondition(workspaceId, param.getDockSn()); - ServiceReply reply = messageSenderService.publishServicesTopic( - param.getDockSn(), DrcMethodEnum.DRC_MODE_ENTER.getMethod(), - DrcModeDTO.builder() - .mqttBroker(MqttConfiguration.getMqttBrokerWithDrc(param.getDockSn() + "-" + System.currentTimeMillis(), param.getDockSn(), - RedisConst.DRC_MODE_ALIVE_SECOND.longValue(), - Map.of(MapKeyConst.ACL, objectMapper.convertValue(JwtAclDTO.builder() - .pub(List.of(subTopic)) - .sub(List.of(pubTopic)) - .build(), new TypeReference>() {})))) - .build()); - - if (ResponseResult.CODE_SUCCESS != reply.getResult()) { - throw new RuntimeException("SN: " + param.getDockSn() + "; Error Code:" + reply.getResult() + "; Failed to enter command flight control mode, please try again later!"); + TopicServicesResponse reply = abstractControlService.drcModeEnter( + SDKManager.getDeviceSDK(param.getDockSn()), + new DrcModeEnterRequest() + .setMqttBroker(MqttPropertyConfiguration.getMqttBrokerWithDrc(param.getDockSn() + "-" + System.currentTimeMillis(), param.getDockSn(), + RedisConst.DRC_MODE_ALIVE_SECOND.longValue(), + Map.of(MapKeyConst.ACL, objectMapper.convertValue(JwtAclDTO.builder() + .pub(List.of(subTopic)) + .sub(List.of(pubTopic)) + .build(), new TypeReference>() {})))) + .setHsiFrequency(1).setOsdFrequency(10)); + + if (!reply.getData().getResult().isSuccess()) { + throw new RuntimeException("SN: " + param.getDockSn() + "; Error:" + reply.getData().getResult() + + "; Failed to enter command flight control mode, please try again later!"); } refreshAcl(param.getDockSn(), param.getClientId(), pubTopic, subTopic); @@ -185,6 +191,7 @@ public class DrcServiceImpl implements IDrcService { String key = RedisConst.MQTT_ACL_PREFIX + clientId; RedisOpsUtils.hashSet(key, pubTopic, MqttAclAccessEnum.PUB.getValue()); RedisOpsUtils.hashSet(key, subTopic, MqttAclAccessEnum.SUB.getValue()); + RedisOpsUtils.expireKey(key, RedisConst.DRC_MODE_ALIVE_SECOND); } @Override @@ -192,58 +199,20 @@ public class DrcServiceImpl implements IDrcService { if (!deviceService.checkDockDrcMode(param.getDockSn())) { throw new RuntimeException("The dock is not in flight control mode."); } - ServiceReply reply = messageSenderService.publishServicesTopic( - param.getDockSn(), DrcMethodEnum.DRC_MODE_EXIT.getMethod(), ""); - if (ResponseResult.CODE_SUCCESS != reply.getResult()) { - throw new RuntimeException("SN: " + param.getDockSn() + "; Error Code:" + - reply.getResult() + "; Failed to exit command flight control mode, please try again later!"); + TopicServicesResponse reply = + abstractControlService.drcModeExit(SDKManager.getDeviceSDK(param.getDockSn())); + if (!reply.getData().getResult().isSuccess()) { + throw new RuntimeException("SN: " + param.getDockSn() + "; Error:" + + reply.getData().getResult() + "; Failed to exit command flight control mode, please try again later!"); } String jobId = waylineRedisService.getPausedWaylineJobId(param.getDockSn()); if (StringUtils.hasText(jobId)) { - waylineJobService.updateJobStatus(workspaceId, jobId, UpdateJobParam.builder().status(WaylineTaskStatusEnum.RESUME).build()); + flighttaskService.updateJobStatus(workspaceId, jobId, UpdateJobParam.builder().status(WaylineTaskStatusEnum.RESUME).build()); } this.delDrcModeInRedis(param.getDockSn()); RedisOpsUtils.del(RedisConst.MQTT_ACL_PREFIX + param.getClientId()); } - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_DRC_STATUS_NOTIFY, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleDrcStatusNotify(CommonTopicReceiver receiver, MessageHeaders headers) { - String dockSn = receiver.getGateway(); - - Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); - if (deviceOpt.isEmpty()) { - return null; - } - - DrcStatusNotifyReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference(){}); - if (DrcStatusErrorEnum.SUCCESS != eventsReceiver.getResult()) { - webSocketMessageService.sendBatch( - deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.DRC_STATUS_NOTIFY.getCode(), - ResultNotifyDTO.builder().sn(dockSn) - .message(eventsReceiver.getResult().getErrorMsg()) - .result(eventsReceiver.getResult().getErrorCode()).build()); - } - return receiver; - } - - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_DRC_MODE_EXIT_NOTIFY, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleDrcModeExitNotify(CommonTopicReceiver receiver, MessageHeaders headers) { - String dockSn = receiver.getGateway(); - - Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); - if (deviceOpt.isEmpty()) { - return null; - } - - DrcModeReasonReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference(){}); - webSocketMessageService.sendBatch( - deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.JOYSTICK_INVALID_NOTIFY.getCode(), - ResultNotifyDTO.builder().sn(dockSn) - .message(eventsReceiver.getReason().getMessage()) - .result(eventsReceiver.getReason().getVal()).build()); - return receiver; - } - } diff --git a/src/main/java/com/dji/sample/control/service/impl/PayloadCommandsHandler.java b/src/main/java/com/dji/sample/control/service/impl/PayloadCommandsHandler.java index c0eeee6..46d8bd9 100644 --- a/src/main/java/com/dji/sample/control/service/impl/PayloadCommandsHandler.java +++ b/src/main/java/com/dji/sample/control/service/impl/PayloadCommandsHandler.java @@ -1,12 +1,12 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.common.util.SpringBeanUtils; +import com.dji.sample.common.util.SpringBeanUtilsTest; import com.dji.sample.control.model.param.DronePayloadParam; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.receiver.OsdCameraReceiver; -import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; import com.dji.sample.manage.service.IDevicePayloadService; import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sdk.cloudapi.device.OsdCamera; +import com.dji.sdk.cloudapi.device.OsdDockDrone; import java.util.Optional; @@ -19,7 +19,7 @@ public abstract class PayloadCommandsHandler { DronePayloadParam param; - OsdCameraReceiver osdCamera; + OsdCamera osdCamera; PayloadCommandsHandler(DronePayloadParam param) { this.param = param; @@ -30,20 +30,20 @@ public abstract class PayloadCommandsHandler { } public boolean canPublish(String deviceSn) { - Optional deviceOpt = SpringBeanUtils.getBean(IDeviceRedisService.class) - .getDeviceOsd(deviceSn, OsdSubDeviceReceiver.class); + Optional deviceOpt = SpringBeanUtilsTest.getBean(IDeviceRedisService.class) + .getDeviceOsd(deviceSn, OsdDockDrone.class); if (deviceOpt.isEmpty()) { throw new RuntimeException("The device is offline."); } osdCamera = deviceOpt.get().getCameras().stream() - .filter(osdCamera -> param.getPayloadIndex().equals(osdCamera.getPayloadIndex())) + .filter(osdCamera -> param.getPayloadIndex().equals(osdCamera.getPayloadIndex().toString())) .findAny() .orElseThrow(() -> new RuntimeException("Did not receive osd information about the camera, please check the cache data.")); return true; } private String checkDockOnline(String dockSn) { - Optional deviceOpt = SpringBeanUtils.getBean(IDeviceRedisService.class).getDeviceOnline(dockSn); + Optional deviceOpt = SpringBeanUtilsTest.getBean(IDeviceRedisService.class).getDeviceOnline(dockSn); if (deviceOpt.isEmpty()) { throw new RuntimeException("The dock is offline."); } @@ -51,14 +51,14 @@ public abstract class PayloadCommandsHandler { } private void checkDeviceOnline(String deviceSn) { - boolean isOnline = SpringBeanUtils.getBean(IDeviceRedisService.class).checkDeviceOnline(deviceSn); + boolean isOnline = SpringBeanUtilsTest.getBean(IDeviceRedisService.class).checkDeviceOnline(deviceSn); if (!isOnline) { throw new RuntimeException("The device is offline."); } } private void checkAuthority(String deviceSn) { - boolean hasAuthority = SpringBeanUtils.getBean(IDevicePayloadService.class) + boolean hasAuthority = SpringBeanUtilsTest.getBean(IDevicePayloadService.class) .checkAuthorityPayload(deviceSn, param.getPayloadIndex()); if (!hasAuthority) { throw new RuntimeException("The device does not have payload control authority."); diff --git a/src/main/java/com/dji/sample/control/service/impl/RemoteDebugHandler.java b/src/main/java/com/dji/sample/control/service/impl/RemoteDebugHandler.java index ed33e51..5da1688 100644 --- a/src/main/java/com/dji/sample/control/service/impl/RemoteDebugHandler.java +++ b/src/main/java/com/dji/sample/control/service/impl/RemoteDebugHandler.java @@ -1,8 +1,8 @@ package com.dji.sample.control.service.impl; -import com.dji.sample.common.util.SpringBeanUtils; -import com.dji.sample.manage.model.enums.DockModeCodeEnum; +import com.dji.sample.common.util.SpringBeanUtilsTest; import com.dji.sample.manage.service.IDeviceService; +import com.dji.sdk.cloudapi.device.DockModeCodeEnum; /** * @author sean @@ -12,11 +12,11 @@ import com.dji.sample.manage.service.IDeviceService; public class RemoteDebugHandler { public boolean valid() { - return false; + return true; } public boolean canPublish(String sn) { - IDeviceService deviceService = SpringBeanUtils.getBean(IDeviceService.class); + IDeviceService deviceService = SpringBeanUtilsTest.getBean(IDeviceService.class); DockModeCodeEnum dockMode = deviceService.getDockMode(sn); return DockModeCodeEnum.REMOTE_DEBUGGING == dockMode; } diff --git a/src/main/java/com/dji/sample/control/service/impl/SDKControlService.java b/src/main/java/com/dji/sample/control/service/impl/SDKControlService.java new file mode 100644 index 0000000..7878261 --- /dev/null +++ b/src/main/java/com/dji/sample/control/service/impl/SDKControlService.java @@ -0,0 +1,118 @@ +package com.dji.sample.control.service.impl; + +import com.dji.sample.component.websocket.model.BizCodeEnum; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; +import com.dji.sample.control.model.dto.ResultNotifyDTO; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.model.enums.UserTypeEnum; +import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sdk.cloudapi.control.*; +import com.dji.sdk.cloudapi.control.api.AbstractControlService; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/4 + */ +@Service +@Slf4j +public class SDKControlService extends AbstractControlService { + + @Autowired + private IWebSocketMessageService webSocketMessageService; + + @Autowired + private IDeviceRedisService deviceRedisService; + + @Autowired + private ObjectMapper mapper; + + @Override + public TopicEventsResponse flyToPointProgress(TopicEventsRequest request, MessageHeaders headers) { + String dockSn = request.getGateway(); + + Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); + if (deviceOpt.isEmpty()) { + log.error("The dock is offline."); + return null; + } + + FlyToPointProgress eventsReceiver = request.getData(); + webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), + BizCodeEnum.FLY_TO_POINT_PROGRESS.getCode(), + ResultNotifyDTO.builder().sn(dockSn) + .message(eventsReceiver.getResult().toString()) + .result(eventsReceiver.getResult().getCode()) + .build()); + return new TopicEventsResponse().setData(MqttReply.success()); + } + + @Override + public TopicEventsResponse takeoffToPointProgress(TopicEventsRequest request, MessageHeaders headers) { + String dockSn = request.getGateway(); + + Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); + if (deviceOpt.isEmpty()) { + log.error("The dock is offline."); + return null; + } + + TakeoffToPointProgress eventsReceiver = request.getData(); + webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), + BizCodeEnum.TAKE_OFF_TO_POINT_PROGRESS.getCode(), + ResultNotifyDTO.builder().sn(dockSn) + .message(eventsReceiver.getResult().toString()) + .result(eventsReceiver.getResult().getCode()) + .build()); + + return new TopicEventsResponse().setData(MqttReply.success()); + } + + @Override + public TopicEventsResponse drcStatusNotify(TopicEventsRequest request, MessageHeaders headers) { + String dockSn = request.getGateway(); + + Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); + if (deviceOpt.isEmpty()) { + return null; + } + + DrcStatusNotify eventsReceiver = request.getData(); + if (DrcStatusErrorEnum.SUCCESS != eventsReceiver.getResult()) { + webSocketMessageService.sendBatch( + deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.DRC_STATUS_NOTIFY.getCode(), + ResultNotifyDTO.builder().sn(dockSn) + .message(eventsReceiver.getResult().getMessage()) + .result(eventsReceiver.getResult().getCode()).build()); + } + return new TopicEventsResponse().setData(MqttReply.success()); + } + + @Override + public TopicEventsResponse joystickInvalidNotify(TopicEventsRequest request, MessageHeaders headers) { + String dockSn = request.getGateway(); + + Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); + if (deviceOpt.isEmpty()) { + return null; + } + + JoystickInvalidNotify eventsReceiver = request.getData(); + webSocketMessageService.sendBatch( + deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.JOYSTICK_INVALID_NOTIFY.getCode(), + ResultNotifyDTO.builder().sn(dockSn) + .message(eventsReceiver.getReason().getMessage()) + .result(eventsReceiver.getReason().getVal()).build()); + return new TopicEventsResponse().setData(MqttReply.success()); + } +} diff --git a/src/main/java/com/dji/sample/control/service/impl/SDKRemoteDebug.java b/src/main/java/com/dji/sample/control/service/impl/SDKRemoteDebug.java new file mode 100644 index 0000000..41f5f94 --- /dev/null +++ b/src/main/java/com/dji/sample/control/service/impl/SDKRemoteDebug.java @@ -0,0 +1,63 @@ +package com.dji.sample.control.service.impl; + +import com.dji.sample.component.mqtt.model.EventsReceiver; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.model.enums.UserTypeEnum; +import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sdk.cloudapi.debug.RemoteDebugProgress; +import com.dji.sdk.cloudapi.debug.api.AbstractDebugService; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/4 + */ +@Service +@Slf4j +public class SDKRemoteDebug extends AbstractDebugService { + + @Autowired + private IWebSocketMessageService webSocketMessageService; + + @Autowired + private IDeviceRedisService deviceRedisService; + + @Override + public TopicEventsResponse remoteDebugProgress(TopicEventsRequest> request, MessageHeaders headers) { + String sn = request.getGateway(); + + EventsReceiver eventsReceiver = new EventsReceiver() + .setOutput(request.getData().getOutput()).setResult(request.getData().getResult()); + eventsReceiver.setBid(request.getBid()); + eventsReceiver.setSn(sn); + + log.info("SN: {}, {} ===> Control progress: {}", sn, request.getMethod(), eventsReceiver.getOutput().getProgress()); + + if (!eventsReceiver.getResult().isSuccess()) { + log.error("SN: {}, {} ===> Error: {}", sn, request.getMethod(), eventsReceiver.getResult()); + } + + Optional deviceOpt = deviceRedisService.getDeviceOnline(sn); + + if (deviceOpt.isEmpty()) { + throw new RuntimeException("The device is offline."); + } + + DeviceDTO device = deviceOpt.get(); + webSocketMessageService.sendBatch(device.getWorkspaceId(), UserTypeEnum.WEB.getVal(), + request.getMethod(), eventsReceiver); + + return new TopicEventsResponse().setData(MqttReply.success()); + } +} 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 9fcc72d..7a0a7ed 100644 --- a/src/main/java/com/dji/sample/manage/controller/DeviceController.java +++ b/src/main/java/com/dji/sample/manage/controller/DeviceController.java @@ -1,20 +1,15 @@ package com.dji.sample.manage.controller; -import com.dji.sample.common.error.CommonErrorEnum; -import com.dji.sample.common.model.PaginationData; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.ChannelName; -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; -import com.dji.sample.component.mqtt.model.CommonTopicResponse; import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO; -import com.dji.sample.manage.model.enums.DeviceSetPropertyEnum; -import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; import com.dji.sample.manage.service.IDeviceService; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.mqtt.property.PropertySetReplyResultEnum; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -33,56 +28,16 @@ public class DeviceController { @Autowired private IDeviceService deviceService; - /** - * Handles the message that the drone goes online. - * @param receiver The drone information is not empty. - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_ONLINE, outputChannel = ChannelName.OUTBOUND) - public void deviceOnline(CommonTopicReceiver receiver) { - boolean online = deviceService.deviceOnline(receiver.getData()); - if (online) { - // Notify pilot that the drone is online successfully. - deviceService.publishStatusReply(receiver.getData().getSn(), - CommonTopicResponse.builder() - .tid(receiver.getTid()) - .bid(receiver.getBid()) - .timestamp(System.currentTimeMillis()) - .method(receiver.getMethod()) - .build()); - } - } - - /** - * Handles the message that the drone goes offline. - * @param receiver The drone information is empty. - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_OFFLINE, outputChannel = ChannelName.OUTBOUND) - public void deviceOffline(CommonTopicReceiver receiver) { - - boolean offline = deviceService.deviceOffline(receiver.getData()); - if (offline) { - // Notify pilot that the device is offline successfully. - deviceService.publishStatusReply(receiver.getData().getSn(), - CommonTopicResponse.builder() - .tid(receiver.getTid()) - .bid(receiver.getBid()) - .timestamp(System.currentTimeMillis()) - .method(receiver.getMethod()) - .build()); - - } - } - /** * Get the topology list of all online devices in one workspace. * @param workspaceId * @return */ @GetMapping("/{workspace_id}/devices") - public ResponseResult> getDevices(@PathVariable("workspace_id") String workspaceId) { + public HttpResultResponse> getDevices(@PathVariable("workspace_id") String workspaceId) { List devicesList = deviceService.getDevicesTopoForWeb(workspaceId); - return ResponseResult.success(devicesList); + return HttpResultResponse.success(devicesList); } /** @@ -92,10 +47,10 @@ public class DeviceController { * @return */ @PostMapping("/{device_sn}/binding") - public ResponseResult bindDevice(@RequestBody DeviceDTO device, @PathVariable("device_sn") String deviceSn) { + public HttpResultResponse bindDevice(@RequestBody DeviceDTO device, @PathVariable("device_sn") String deviceSn) { device.setDeviceSn(deviceSn); boolean isUpd = deviceService.bindDevice(device); - return isUpd ? ResponseResult.success() : ResponseResult.error(); + return isUpd ? HttpResultResponse.success() : HttpResultResponse.error(); } /** @@ -105,10 +60,10 @@ public class DeviceController { * @return */ @GetMapping("/{workspace_id}/devices/{device_sn}") - public ResponseResult getDevice(@PathVariable("workspace_id") String workspaceId, - @PathVariable("device_sn") String deviceSn) { + public HttpResultResponse 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()); + return deviceOpt.isEmpty() ? HttpResultResponse.error("device not found.") : HttpResultResponse.success(deviceOpt.get()); } /** @@ -119,13 +74,13 @@ public class DeviceController { * @return */ @GetMapping("/{workspace_id}/devices/bound") - public ResponseResult> getBoundDevicesWithDomain( + public HttpResultResponse> getBoundDevicesWithDomain( @PathVariable("workspace_id") String workspaceId, Integer 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); + return HttpResultResponse.success(devices); } /** @@ -134,9 +89,9 @@ public class DeviceController { * @return */ @DeleteMapping("/{device_sn}/unbinding") - public ResponseResult unbindingDevice(@PathVariable("device_sn") String deviceSn) { + public HttpResultResponse unbindingDevice(@PathVariable("device_sn") String deviceSn) { deviceService.unbindDevice(deviceSn); - return ResponseResult.success(); + return HttpResultResponse.success(); } /** @@ -147,12 +102,12 @@ public class DeviceController { * @return */ @PutMapping("/{workspace_id}/devices/{device_sn}") - public ResponseResult updateDevice(@RequestBody DeviceDTO device, - @PathVariable("workspace_id") String workspaceId, - @PathVariable("device_sn") String deviceSn) { + public HttpResultResponse 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(); + return isUpd ? HttpResultResponse.success() : HttpResultResponse.error(); } /** @@ -162,8 +117,8 @@ public class DeviceController { * @return */ @PostMapping("/{workspace_id}/devices/ota") - public ResponseResult createOtaJob(@PathVariable("workspace_id") String workspaceId, - @RequestBody List upgradeDTOS) { + public HttpResultResponse createOtaJob(@PathVariable("workspace_id") String workspaceId, + @RequestBody List upgradeDTOS) { return deviceService.createDeviceOtaJob(workspaceId, upgradeDTOS); } @@ -175,18 +130,15 @@ public class DeviceController { * @return */ @PutMapping("/{workspace_id}/devices/{device_sn}/property") - public ResponseResult devicePropertySet(@PathVariable("workspace_id") String workspaceId, - @PathVariable("device_sn") String dockSn, - @RequestBody JsonNode param) { + public HttpResultResponse devicePropertySet(@PathVariable("workspace_id") String workspaceId, + @PathVariable("device_sn") String dockSn, + @RequestBody JsonNode param) { if (param.size() != 1) { - return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); - } - String property = param.fieldNames().next(); - Optional propertyEnumOpt = DeviceSetPropertyEnum.find(property); - if (propertyEnumOpt.isEmpty()) { - return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); + return HttpResultResponse.error(CloudSDKErrorEnum.INVALID_PARAMETER); } - deviceService.devicePropertySet(workspaceId, dockSn, propertyEnumOpt.get(), param.get(property)); - return ResponseResult.success(); + + int result = deviceService.devicePropertySet(workspaceId, dockSn, param); + return PropertySetReplyResultEnum.SUCCESS.getResult() == result ? + HttpResultResponse.success() : HttpResultResponse.error(result, String.valueOf(result)); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/controller/DeviceFirmwareController.java b/src/main/java/com/dji/sample/manage/controller/DeviceFirmwareController.java index 45a2770..6425bbb 100644 --- a/src/main/java/com/dji/sample/manage/controller/DeviceFirmwareController.java +++ b/src/main/java/com/dji/sample/manage/controller/DeviceFirmwareController.java @@ -1,8 +1,6 @@ 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.DeviceFirmwareDTO; import com.dji.sample.manage.model.dto.DeviceFirmwareNoteDTO; import com.dji.sample.manage.model.dto.FirmwareFileProperties; @@ -10,6 +8,8 @@ import com.dji.sample.manage.model.param.DeviceFirmwareQueryParam; import com.dji.sample.manage.model.param.DeviceFirmwareUpdateParam; import com.dji.sample.manage.model.param.DeviceFirmwareUploadParam; import com.dji.sample.manage.service.IDeviceFirmwareService; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -43,7 +43,7 @@ public class DeviceFirmwareController { * @return */ @GetMapping("/firmware-release-notes/latest") - public ResponseResult> getLatestFirmwareNote(@RequestParam("device_name") List deviceNames) { + public HttpResultResponse> getLatestFirmwareNote(@RequestParam("device_name") List deviceNames) { List releaseNotes = deviceNames.stream() .map(deviceName -> service.getLatestFirmwareReleaseNote(deviceName)) @@ -51,7 +51,7 @@ public class DeviceFirmwareController { .map(Optional::get) .collect(Collectors.toList()); - return ResponseResult.success(releaseNotes); + return HttpResultResponse.success(releaseNotes); } /** @@ -61,11 +61,11 @@ public class DeviceFirmwareController { * @return */ @GetMapping("/{workspace_id}/firmwares") - public ResponseResult> getAllFirmwarePagination( + public HttpResultResponse> getAllFirmwarePagination( @PathVariable("workspace_id") String workspaceId, @Valid DeviceFirmwareQueryParam param) { PaginationData data = service.getAllFirmwarePagination(workspaceId, param); - return ResponseResult.success(data); + return HttpResultResponse.success(data); } /** @@ -77,19 +77,19 @@ public class DeviceFirmwareController { * @return */ @PostMapping("/{workspace_id}/firmwares/file/upload") - public ResponseResult importFirmwareFile(HttpServletRequest request, @PathVariable("workspace_id") String workspaceId, - @NotNull(message = "No file received.") MultipartFile file, - @Valid DeviceFirmwareUploadParam param) { + public HttpResultResponse importFirmwareFile(HttpServletRequest request, @PathVariable("workspace_id") String workspaceId, + @NotNull(message = "No file received.") MultipartFile file, + @Valid DeviceFirmwareUploadParam param) { if (!file.getOriginalFilename().endsWith(FirmwareFileProperties.FIRMWARE_FILE_SUFFIX)) { - return ResponseResult.error("The file format is incorrect."); + return HttpResultResponse.error("The file format is incorrect."); } CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); String creator = customClaim.getUsername(); service.importFirmwareFile(workspaceId, creator, param, file); - return ResponseResult.success(); + return HttpResultResponse.success(); } /** @@ -100,13 +100,13 @@ public class DeviceFirmwareController { * @return */ @PutMapping("/{workspace_id}/firmwares/{firmware_id}") - public ResponseResult changeFirmwareStatus(@PathVariable("workspace_id") String workspaceId, - @PathVariable("firmware_id") String firmwareId, - @Valid @RequestBody DeviceFirmwareUpdateParam param) { + public HttpResultResponse changeFirmwareStatus(@PathVariable("workspace_id") String workspaceId, + @PathVariable("firmware_id") String firmwareId, + @Valid @RequestBody DeviceFirmwareUpdateParam param) { service.updateFirmwareInfo(DeviceFirmwareDTO.builder() .firmwareId(firmwareId).firmwareStatus(param.getStatus()).build()); - return ResponseResult.success(); + return HttpResultResponse.success(); } diff --git a/src/main/java/com/dji/sample/manage/controller/DeviceHmsController.java b/src/main/java/com/dji/sample/manage/controller/DeviceHmsController.java index 0035d5f..7ded209 100644 --- a/src/main/java/com/dji/sample/manage/controller/DeviceHmsController.java +++ b/src/main/java/com/dji/sample/manage/controller/DeviceHmsController.java @@ -1,10 +1,10 @@ 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 com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -34,11 +34,11 @@ public class DeviceHmsController { * @return */ @GetMapping("/{workspace_id}/devices/hms") - public ResponseResult> getHmsInformation(DeviceHmsQueryParam param, - @PathVariable("workspace_id") String workspaceId) { + public HttpResultResponse> getHmsInformation(DeviceHmsQueryParam param, + @PathVariable("workspace_id") String workspaceId) { PaginationData devices = deviceHmsService.getDeviceHmsByParam(param); - return ResponseResult.success(devices); + return HttpResultResponse.success(devices); } /** @@ -47,9 +47,9 @@ public class DeviceHmsController { * @return */ @PutMapping("/{workspace_id}/devices/hms/{device_sn}") - public ResponseResult updateReadHmsByDeviceSn(@PathVariable("device_sn") String deviceSn) { + public HttpResultResponse updateReadHmsByDeviceSn(@PathVariable("device_sn") String deviceSn) { deviceHmsService.updateUnreadHms(deviceSn); - return ResponseResult.success(); + return HttpResultResponse.success(); } /** @@ -58,12 +58,12 @@ public class DeviceHmsController { * @return */ @GetMapping("/{workspace_id}/devices/hms/{device_sn}") - public ResponseResult> getUnreadHmsByDeviceSn(@PathVariable("device_sn") String deviceSn) { + public HttpResultResponse> 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()); + return HttpResultResponse.success(paginationData.getList()); } } diff --git a/src/main/java/com/dji/sample/manage/controller/DeviceLogsController.java b/src/main/java/com/dji/sample/manage/controller/DeviceLogsController.java index 6f9b72f..3e08d53 100644 --- a/src/main/java/com/dji/sample/manage/controller/DeviceLogsController.java +++ b/src/main/java/com/dji/sample/manage/controller/DeviceLogsController.java @@ -1,13 +1,14 @@ 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.DeviceLogsDTO; import com.dji.sample.manage.model.param.DeviceLogsCreateParam; +import com.dji.sample.manage.model.param.DeviceLogsGetParam; import com.dji.sample.manage.model.param.DeviceLogsQueryParam; -import com.dji.sample.manage.model.param.LogsFileUpdateParam; import com.dji.sample.manage.service.IDeviceLogsService; +import com.dji.sdk.cloudapi.log.FileUploadUpdateRequest; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -15,7 +16,6 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.net.URL; -import java.util.List; import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; @@ -40,24 +40,24 @@ public class DeviceLogsController { * @return */ @GetMapping("/{workspace_id}/devices/{device_sn}/logs-uploaded") - public ResponseResult getUploadedLogs(DeviceLogsQueryParam param, @PathVariable("workspace_id") String workspaceId, - @PathVariable("device_sn") String deviceSn) { + public HttpResultResponse getUploadedLogs(DeviceLogsQueryParam param, @PathVariable("workspace_id") String workspaceId, + @PathVariable("device_sn") String deviceSn) { PaginationData data = deviceLogsService.getUploadedLogs(deviceSn, param); - return ResponseResult.success(data); + return HttpResultResponse.success(data); } /** * Get a list of log files that can be uploaded in real time. * @param workspaceId * @param deviceSn - * @param domainList + * @param param * @return */ @GetMapping("/{workspace_id}/devices/{device_sn}/logs") - public ResponseResult getLogsBySn(@PathVariable("workspace_id") String workspaceId, - @PathVariable("device_sn") String deviceSn, - @RequestParam("domain_list") List domainList) { - return deviceLogsService.getRealTimeLogs(deviceSn, domainList); + public HttpResultResponse getLogsBySn(@PathVariable("workspace_id") String workspaceId, + @PathVariable("device_sn") String deviceSn, + DeviceLogsGetParam param) { + return deviceLogsService.getRealTimeLogs(deviceSn, param.getDomainList()); } /** @@ -65,9 +65,9 @@ public class DeviceLogsController { * @return */ @PostMapping("/{workspace_id}/devices/{device_sn}/logs") - public ResponseResult uploadLogs(@PathVariable("workspace_id") String workspaceId, - @PathVariable("device_sn") String deviceSn, - HttpServletRequest request, @RequestBody DeviceLogsCreateParam param) { + public HttpResultResponse uploadLogs(@PathVariable("workspace_id") String workspaceId, + @PathVariable("device_sn") String deviceSn, + HttpServletRequest request, @RequestBody DeviceLogsCreateParam param) { CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); @@ -79,9 +79,9 @@ public class DeviceLogsController { * @return */ @DeleteMapping("/{workspace_id}/devices/{device_sn}/logs") - public ResponseResult cancelUploadedLogs(@PathVariable("workspace_id") String workspaceId, - @PathVariable("device_sn") String deviceSn, - @RequestBody LogsFileUpdateParam param) { + public HttpResultResponse cancelUploadedLogs(@PathVariable("workspace_id") String workspaceId, + @PathVariable("device_sn") String deviceSn, + @RequestBody FileUploadUpdateRequest param) { return deviceLogsService.pushUpdateFile(deviceSn, param); } @@ -91,11 +91,11 @@ public class DeviceLogsController { * @return */ @DeleteMapping("/{workspace_id}/devices/{device_sn}/logs/{logs_id}") - public ResponseResult deleteUploadedLogs(@PathVariable("workspace_id") String workspaceId, - @PathVariable("device_sn") String deviceSn, - @PathVariable("logs_id") String logsId) { + public HttpResultResponse deleteUploadedLogs(@PathVariable("workspace_id") String workspaceId, + @PathVariable("device_sn") String deviceSn, + @PathVariable("logs_id") String logsId) { deviceLogsService.deleteLogs(deviceSn, logsId); - return ResponseResult.success(); + return HttpResultResponse.success(); } /** * Query the download address of the file according to the wayline file id, @@ -106,17 +106,17 @@ public class DeviceLogsController { * @param response */ @GetMapping("/{workspace_id}/logs/{logs_id}/url/{file_id}") - public ResponseResult getFileUrl(@PathVariable(name = "workspace_id") String workspaceId, - @PathVariable(name = "file_id") String fileId, - @PathVariable(name = "logs_id") String logsId, HttpServletResponse response) { + public HttpResultResponse getFileUrl(@PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "file_id") String fileId, + @PathVariable(name = "logs_id") String logsId, HttpServletResponse response) { try { URL url = deviceLogsService.getLogsFileUrl(logsId, fileId); - return ResponseResult.success(url.toString()); + return HttpResultResponse.success(url.toString()); } catch (Exception e) { log.error("Failed to get the logs file download address."); e.printStackTrace(); } - return ResponseResult.error("Failed to get the logs file download address."); + return HttpResultResponse.error("Failed to get the logs file download address."); } } 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 c8bd144..27f1a64 100644 --- a/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java +++ b/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java @@ -1,17 +1,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.ChannelName; import com.dji.sample.manage.model.dto.CapacityDeviceDTO; import com.dji.sample.manage.model.dto.LiveTypeDTO; -import com.dji.sample.manage.model.receiver.LiveCapacityReceiver; import com.dji.sample.manage.service.ILiveStreamService; +import com.dji.sdk.common.HttpResultResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.messaging.MessageHeaders; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; @@ -36,29 +32,19 @@ public class LiveStreamController { @Autowired private ObjectMapper mapper; - /** - * Analyze the live streaming capabilities of drones. - * This data is necessary if drones are required for live streaming. - * @param liveCapacity the capacity of drone and dock - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_CAPACITY) - public void stateCapacity(LiveCapacityReceiver liveCapacity, MessageHeaders headers) { - liveStreamService.saveLiveCapacity(liveCapacity, headers.getTimestamp()); - } - /** * Get live capability data of all drones in the current user's workspace from the database. * @param request * @return live capability */ @GetMapping("/capacity") - public ResponseResult> getLiveCapacity(HttpServletRequest request) { + public HttpResultResponse> getLiveCapacity(HttpServletRequest request) { // Get information about the current user. CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); List liveCapacity = liveStreamService.getLiveCapacity(customClaim.getWorkspaceId()); - return ResponseResult.success(liveCapacity); + return HttpResultResponse.success(liveCapacity); } /** @@ -67,7 +53,7 @@ public class LiveStreamController { * @return */ @PostMapping("/streams/start") - public ResponseResult liveStart(@RequestBody LiveTypeDTO liveParam) { + public HttpResultResponse liveStart(@RequestBody LiveTypeDTO liveParam) { return liveStreamService.liveStart(liveParam); } @@ -77,7 +63,7 @@ public class LiveStreamController { * @return */ @PostMapping("/streams/stop") - public ResponseResult liveStop(@RequestBody LiveTypeDTO liveParam) { + public HttpResultResponse liveStop(@RequestBody LiveTypeDTO liveParam) { return liveStreamService.liveStop(liveParam.getVideoId()); } @@ -87,12 +73,12 @@ public class LiveStreamController { * @return */ @PostMapping("/streams/update") - public ResponseResult liveSetQuality(@RequestBody LiveTypeDTO liveParam) { + public HttpResultResponse liveSetQuality(@RequestBody LiveTypeDTO liveParam) { return liveStreamService.liveSetQuality(liveParam); } @PostMapping("/streams/switch") - public ResponseResult liveLensChange(@RequestBody LiveTypeDTO liveParam) { + public HttpResultResponse liveLensChange(@RequestBody LiveTypeDTO liveParam) { return liveStreamService.liveLensChange(liveParam); } 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 23c84d9..9572dbb 100644 --- a/src/main/java/com/dji/sample/manage/controller/LoginController.java +++ b/src/main/java/com/dji/sample/manage/controller/LoginController.java @@ -1,10 +1,10 @@ package com.dji.sample.manage.controller; import com.dji.sample.common.error.CommonErrorEnum; -import com.dji.sample.common.model.ResponseResult; import com.dji.sample.manage.model.dto.UserDTO; import com.dji.sample.manage.model.dto.UserLoginDTO; import com.dji.sample.manage.service.IUserService; +import com.dji.sdk.common.HttpResultResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PostMapping; @@ -26,7 +26,7 @@ public class LoginController { private IUserService userService; @PostMapping("/login") - public ResponseResult login(@RequestBody UserLoginDTO loginDTO) { + public HttpResultResponse login(@RequestBody UserLoginDTO loginDTO) { String username = loginDTO.getUsername(); String password = loginDTO.getPassword(); @@ -34,15 +34,15 @@ public class LoginController { } @PostMapping("/token/refresh") - public ResponseResult refreshToken(HttpServletRequest request, HttpServletResponse response) { + public HttpResultResponse refreshToken(HttpServletRequest request, HttpServletResponse response) { String token = request.getHeader(PARAM_TOKEN); Optional user = userService.refreshToken(token); if (user.isEmpty()) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); - return ResponseResult.error(CommonErrorEnum.NO_TOKEN.getErrorMsg()); + return HttpResultResponse.error(CommonErrorEnum.NO_TOKEN.getMessage()); } - return ResponseResult.success(user.get()); + return HttpResultResponse.success(user.get()); } } diff --git a/src/main/java/com/dji/sample/manage/controller/TopologyController.java b/src/main/java/com/dji/sample/manage/controller/TopologyController.java index 3f2b80d..10908d2 100644 --- a/src/main/java/com/dji/sample/manage/controller/TopologyController.java +++ b/src/main/java/com/dji/sample/manage/controller/TopologyController.java @@ -1,17 +1,16 @@ package com.dji.sample.manage.controller; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.manage.model.dto.TopologyDTO; import com.dji.sample.manage.service.ITopologyService; +import com.dji.sdk.cloudapi.tsa.TopologyList; +import com.dji.sdk.cloudapi.tsa.TopologyResponse; +import com.dji.sdk.cloudapi.tsa.api.IHttpTsaService; +import com.dji.sdk.common.HttpResultResponse; 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 javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * @author sean @@ -19,22 +18,20 @@ import java.util.concurrent.ConcurrentHashMap; * @date 2021/12/8 */ @RestController -@RequestMapping("${url.manage.prefix}${url.manage.version}/workspaces") -public class TopologyController { +public class TopologyController implements IHttpTsaService { @Autowired private ITopologyService topologyService; + /** * Get the topology list of all devices in the current user workspace for pilot display. * @param workspaceId * @return */ - @GetMapping("/{workspace_id}/devices/topologies") - public ResponseResult>> getDevicesTopologiesForPilot( - @PathVariable(name = "workspace_id") String workspaceId) { - List topologyList = topologyService.getDeviceTopology(workspaceId); - return ResponseResult.success(new ConcurrentHashMap<>(Map.of("list", topologyList))); + @Override + public HttpResultResponse obtainDeviceTopologyList(String workspaceId, HttpServletRequest req, HttpServletResponse rsp) { + List topologyList = topologyService.getDeviceTopology(workspaceId); + return HttpResultResponse.success(new TopologyResponse().setList(topologyList)); } - } 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 91990e7..5536948 100644 --- a/src/main/java/com/dji/sample/manage/controller/UserController.java +++ b/src/main/java/com/dji/sample/manage/controller/UserController.java @@ -1,10 +1,10 @@ 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 com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -26,7 +26,7 @@ public class UserController { * @return */ @GetMapping("/current") - public ResponseResult getCurrentUserInfo(HttpServletRequest request) { + public HttpResultResponse getCurrentUserInfo(HttpServletRequest request) { CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); return userService.getUserByUsername(customClaim.getUsername(), customClaim.getWorkspaceId()); } @@ -39,11 +39,11 @@ public class UserController { * @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) { + public HttpResultResponse> 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); + return HttpResultResponse.success(paginationData); } /** @@ -54,11 +54,11 @@ public class UserController { * @return */ @PutMapping("/{workspace_id}/users/{user_id}") - public ResponseResult updateUser(@RequestBody UserListDTO user, - @PathVariable("workspace_id") String workspaceId, - @PathVariable("user_id") String userId) { + public HttpResultResponse updateUser(@RequestBody UserListDTO user, + @PathVariable("workspace_id") String workspaceId, + @PathVariable("user_id") String userId) { userService.updateUser(workspaceId, userId, user); - return ResponseResult.success(); + return HttpResultResponse.success(); } } diff --git a/src/main/java/com/dji/sample/manage/controller/WorkspaceController.java b/src/main/java/com/dji/sample/manage/controller/WorkspaceController.java index e2f5f43..80110ac 100644 --- a/src/main/java/com/dji/sample/manage/controller/WorkspaceController.java +++ b/src/main/java/com/dji/sample/manage/controller/WorkspaceController.java @@ -1,9 +1,9 @@ package com.dji.sample.manage.controller; import com.dji.sample.common.model.CustomClaim; -import com.dji.sample.common.model.ResponseResult; import com.dji.sample.manage.model.dto.WorkspaceDTO; import com.dji.sample.manage.service.IWorkspaceService; +import com.dji.sdk.common.HttpResultResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -32,10 +32,10 @@ public class WorkspaceController { * @return */ @GetMapping("/current") - public ResponseResult getCurrentWorkspace(HttpServletRequest request) { + public HttpResultResponse getCurrentWorkspace(HttpServletRequest request) { CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); Optional workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(customClaim.getWorkspaceId()); - return workspaceOpt.isEmpty() ? ResponseResult.error() : ResponseResult.success(workspaceOpt.get()); + return workspaceOpt.isEmpty() ? HttpResultResponse.error() : HttpResultResponse.success(workspaceOpt.get()); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/dto/DeviceAuthorityDTO.java b/src/main/java/com/dji/sample/manage/model/dto/DeviceAuthorityDTO.java index 72621e1..c72680f 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/DeviceAuthorityDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/DeviceAuthorityDTO.java @@ -1,6 +1,7 @@ package com.dji.sample.manage.model.dto; import com.dji.sample.control.model.enums.DroneAuthorityEnum; +import com.dji.sdk.cloudapi.device.ControlSourceEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -21,6 +22,6 @@ public class DeviceAuthorityDTO { private DroneAuthorityEnum type; - private String controlSource; + private ControlSourceEnum controlSource; } 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 d4c7399..3d89a92 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,5 +1,11 @@ package com.dji.sample.manage.model.dto; +import com.dji.sample.manage.model.enums.DeviceFirmwareStatusEnum; +import com.dji.sdk.cloudapi.device.ControlSourceEnum; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.device.DeviceSubTypeEnum; +import com.dji.sdk.cloudapi.device.DeviceTypeEnum; +import com.dji.sdk.cloudapi.tsa.DeviceIconUrl; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -25,21 +31,21 @@ public class DeviceDTO { private String workspaceId; - private String controlSource; + private ControlSourceEnum controlSource; private String deviceDesc; private String childDeviceSn; - private Integer domain; + private DeviceDomainEnum domain; - private Integer type; + private DeviceTypeEnum type; - private Integer subType; + private DeviceSubTypeEnum subType; private List payloadsList; - private IconUrlDTO iconUrl; + private DeviceIconUrl iconUrl; private Boolean status; @@ -59,9 +65,11 @@ public class DeviceDTO { private DeviceDTO children; - private Integer firmwareStatus; + private DeviceFirmwareStatusEnum firmwareStatus; private Integer firmwareProgress; private String parentSn; + + private String thingVersion; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/dto/DeviceLogsDTO.java b/src/main/java/com/dji/sample/manage/model/dto/DeviceLogsDTO.java index b3d7b7f..0f5e210 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/DeviceLogsDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/DeviceLogsDTO.java @@ -1,6 +1,6 @@ package com.dji.sample.manage.model.dto; -import com.dji.sample.manage.model.receiver.LogsFileUploadList; +import com.dji.sdk.cloudapi.tsa.TopologyList; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -32,10 +32,10 @@ public class DeviceLogsDTO { private Integer status; - private TopologyDTO deviceTopo; + private TopologyList deviceTopo; private List logsProgress; - private LogsFileUploadList deviceLogs; + private LogsFileUploadListDTO deviceLogs; } diff --git a/src/main/java/com/dji/sample/manage/model/dto/DeviceModelDTO.java b/src/main/java/com/dji/sample/manage/model/dto/DeviceModelDTO.java deleted file mode 100644 index d7559ff..0000000 --- a/src/main/java/com/dji/sample/manage/model/dto/DeviceModelDTO.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dji.sample.manage.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/8 - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class DeviceModelDTO { - - private String key; - - private String domain; - - private String type; - - private String subType; - -} diff --git a/src/main/java/com/dji/sample/manage/model/dto/DevicePayloadDTO.java b/src/main/java/com/dji/sample/manage/model/dto/DevicePayloadDTO.java index 555eb90..abcfb45 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/DevicePayloadDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/DevicePayloadDTO.java @@ -1,7 +1,7 @@ package com.dji.sample.manage.model.dto; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.dji.sdk.cloudapi.device.ControlSourceEnum; +import com.dji.sdk.cloudapi.device.PayloadIndex; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -16,7 +16,6 @@ import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class DevicePayloadDTO { private String payloadSn; @@ -27,7 +26,7 @@ public class DevicePayloadDTO { private String payloadDesc; - private String controlSource; + private ControlSourceEnum controlSource; - private String payloadIndex; + private PayloadIndex payloadIndex; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/dto/DevicePayloadReceiver.java b/src/main/java/com/dji/sample/manage/model/dto/DevicePayloadReceiver.java new file mode 100644 index 0000000..85ecfd9 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/dto/DevicePayloadReceiver.java @@ -0,0 +1,29 @@ +package com.dji.sample.manage.model.dto; + +import com.dji.sdk.cloudapi.device.ControlSourceEnum; +import com.dji.sdk.cloudapi.device.PayloadIndex; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class DevicePayloadReceiver { + + private String deviceSn; + + private ControlSourceEnum controlSource; + + private PayloadIndex payloadIndex; + + private String sn; + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/dto/IconUrlDTO.java b/src/main/java/com/dji/sample/manage/model/dto/IconUrlDTO.java deleted file mode 100644 index effbf1c..0000000 --- a/src/main/java/com/dji/sample/manage/model/dto/IconUrlDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dji.sample.manage.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.3 - * @date 2022/1/5 - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class IconUrlDTO { - - @JsonProperty("normal_icon_url") - private String normalUrl; - - @JsonProperty("selected_icon_url") - private String selectUrl; -} diff --git a/src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java b/src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java index 4a385ec..ebd932c 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java @@ -1,5 +1,8 @@ package com.dji.sample.manage.model.dto; +import com.dji.sdk.cloudapi.livestream.LensChangeVideoTypeEnum; +import com.dji.sdk.cloudapi.livestream.UrlTypeEnum; +import com.dji.sdk.cloudapi.livestream.VideoQualityEnum; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; @@ -13,7 +16,7 @@ import lombok.Data; public class LiveTypeDTO { @JsonProperty("url_type") - private Integer urlType; + private UrlTypeEnum urlType; private String url; @@ -21,8 +24,8 @@ public class LiveTypeDTO { private String videoId; @JsonProperty("video_quality") - private Integer videoQuality; + private VideoQualityEnum videoQuality; - private String videoType; + private LensChangeVideoTypeEnum videoType; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/receiver/LogsFileUpload.java b/src/main/java/com/dji/sample/manage/model/dto/LogsFileUploadDTO.java similarity index 64% rename from src/main/java/com/dji/sample/manage/model/receiver/LogsFileUpload.java rename to src/main/java/com/dji/sample/manage/model/dto/LogsFileUploadDTO.java index 034fcc9..1780b7f 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/LogsFileUpload.java +++ b/src/main/java/com/dji/sample/manage/model/dto/LogsFileUploadDTO.java @@ -1,5 +1,7 @@ -package com.dji.sample.manage.model.receiver; +package com.dji.sample.manage.model.dto; +import com.dji.sdk.cloudapi.log.LogFileIndex; +import com.dji.sdk.cloudapi.log.LogModuleEnum; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -17,14 +19,14 @@ import java.util.List; @AllArgsConstructor @NoArgsConstructor @Builder -public class LogsFileUpload { +public class LogsFileUploadDTO { private String deviceSn; - private List list; + private List list; @JsonProperty("module") - private String deviceModelDomain; + private LogModuleEnum deviceModelDomain; private String objectKey; diff --git a/src/main/java/com/dji/sample/manage/model/receiver/LogsFileUploadList.java b/src/main/java/com/dji/sample/manage/model/dto/LogsFileUploadListDTO.java similarity index 69% rename from src/main/java/com/dji/sample/manage/model/receiver/LogsFileUploadList.java rename to src/main/java/com/dji/sample/manage/model/dto/LogsFileUploadListDTO.java index 0d0c616..be470f8 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/LogsFileUploadList.java +++ b/src/main/java/com/dji/sample/manage/model/dto/LogsFileUploadListDTO.java @@ -1,4 +1,4 @@ -package com.dji.sample.manage.model.receiver; +package com.dji.sample.manage.model.dto; import lombok.AllArgsConstructor; import lombok.Builder; @@ -16,9 +16,9 @@ import java.util.List; @Builder @AllArgsConstructor @NoArgsConstructor -public class LogsFileUploadList { +public class LogsFileUploadListDTO { - private List files; + private List files; private Integer result; } diff --git a/src/main/java/com/dji/sample/manage/model/dto/LogsOutputProgressDTO.java b/src/main/java/com/dji/sample/manage/model/dto/LogsOutputProgressDTO.java index 057c5da..e0b1ad8 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/LogsOutputProgressDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/LogsOutputProgressDTO.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.dto; +import com.dji.sdk.cloudapi.log.FileUploadStatusEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -20,7 +21,7 @@ public class LogsOutputProgressDTO { private String logsId; - private String status; + private FileUploadStatusEnum status; private List files; } diff --git a/src/main/java/com/dji/sample/manage/model/dto/LogsUploadCredentialsDTO.java b/src/main/java/com/dji/sample/manage/model/dto/LogsUploadCredentialsDTO.java index 970fc12..a58041e 100644 --- a/src/main/java/com/dji/sample/manage/model/dto/LogsUploadCredentialsDTO.java +++ b/src/main/java/com/dji/sample/manage/model/dto/LogsUploadCredentialsDTO.java @@ -1,8 +1,9 @@ package com.dji.sample.manage.model.dto; -import com.dji.sample.manage.model.receiver.LogsFileUploadList; -import com.dji.sample.media.model.CredentialsDTO; -import com.dji.sample.media.model.StsCredentialsDTO; +import com.dji.sdk.cloudapi.log.FileUploadStartParam; +import com.dji.sdk.cloudapi.storage.CredentialsToken; +import com.dji.sdk.cloudapi.storage.OssTypeEnum; +import com.dji.sdk.cloudapi.storage.StsCredentialsResponse; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -22,22 +23,23 @@ public class LogsUploadCredentialsDTO { private String bucket; - private CredentialsDTO credentials; + private CredentialsToken credentials; private String endpoint; @JsonProperty("file_store_dir") private String objectKeyPrefix; - private String provider; + private OssTypeEnum provider; + @Builder.Default private String fileType = "text_log"; - private LogsFileUploadList params; + private FileUploadStartParam params; private String region; - public LogsUploadCredentialsDTO(StsCredentialsDTO sts) { + public LogsUploadCredentialsDTO(StsCredentialsResponse sts) { this.bucket = sts.getBucket(); long expire = sts.getCredentials().getExpire(); sts.getCredentials().setExpire(System.currentTimeMillis() + (expire - 60) * 1000); diff --git a/src/main/java/com/dji/sample/manage/model/dto/TelemetryDeviceDTO.java b/src/main/java/com/dji/sample/manage/model/dto/TelemetryDeviceDTO.java deleted file mode 100644 index 1905804..0000000 --- a/src/main/java/com/dji/sample/manage/model/dto/TelemetryDeviceDTO.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.dji.sample.manage.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/8 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class TelemetryDeviceDTO { - - private Double latitude; - - private Double longitude; - - private Double altitude; - - private Float attitudeHead; - - private Double elevation; - - private Float horizontalSpeed; - - private Float verticalSpeed; -} diff --git a/src/main/java/com/dji/sample/manage/model/dto/TopologyDTO.java b/src/main/java/com/dji/sample/manage/model/dto/TopologyDTO.java deleted file mode 100644 index d91cd3a..0000000 --- a/src/main/java/com/dji/sample/manage/model/dto/TopologyDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.dji.sample.manage.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/8 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class TopologyDTO { - - private List hosts; - - private List parents; -} 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 ea709cc..627615d 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 @@ -1,42 +1,148 @@ package com.dji.sample.manage.model.dto; -import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.tsa.DeviceIconUrl; +import com.dji.sdk.cloudapi.tsa.DeviceTopology; +import com.dji.sdk.cloudapi.tsa.TopologyDeviceModel; /** * @author sean * @version 0.2 * @date 2021/12/8 */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -public class TopologyDeviceDTO { +public class TopologyDeviceDTO extends DeviceTopology { - private String sn; + private String model; - private DeviceModelDTO deviceModel; + private Boolean boundStatus; - private Boolean onlineStatus; + private String gatewaySn; - private String deviceCallsign; + private DeviceDomainEnum domain; - private String userId; + public TopologyDeviceDTO() { + } - private String userCallsign; + @Override + public String toString() { + return "TopologyDeviceDTO{" + + "model='" + model + '\'' + + ", boundStatus=" + boundStatus + + ", gatewaySn='" + gatewaySn + '\'' + + ", domain=" + domain + + '}'; + } - private IconUrlDTO iconUrls; + @Override + public String getSn() { + return super.getSn(); + } - private String model; + @Override + public TopologyDeviceDTO setSn(String sn) { + super.setSn(sn); + return this; + } - private Boolean boundStatus; + @Override + public String getDeviceCallsign() { + return super.getDeviceCallsign(); + } - private String gatewaySn; + @Override + public TopologyDeviceDTO setDeviceCallsign(String deviceCallsign) { + super.setDeviceCallsign(deviceCallsign); + return this; + } + + @Override + public TopologyDeviceModel getDeviceModel() { + return super.getDeviceModel(); + } + + @Override + public TopologyDeviceDTO setDeviceModel(TopologyDeviceModel deviceModel) { + super.setDeviceModel(deviceModel); + return this; + } + + @Override + public Boolean getOnlineStatus() { + return super.getOnlineStatus(); + } + + @Override + public TopologyDeviceDTO setOnlineStatus(Boolean onlineStatus) { + super.setOnlineStatus(onlineStatus); + return this; + } + + @Override + public String getUserId() { + return super.getUserId(); + } + + @Override + public TopologyDeviceDTO setUserId(String userId) { + super.setUserId(userId); + return this; + } + + @Override + public String getUserCallsign() { + return super.getUserCallsign(); + } + + @Override + public TopologyDeviceDTO setUserCallsign(String userCallsign) { + super.setUserCallsign(userCallsign); + return this; + } + + @Override + public DeviceIconUrl getIconUrls() { + return super.getIconUrls(); + } + + @Override + public TopologyDeviceDTO setIconUrls(DeviceIconUrl iconUrls) { + super.setIconUrls(iconUrls); + return this; + } + + public String getModel() { + return model; + } + + public TopologyDeviceDTO setModel(String model) { + this.model = model; + return this; + } + + public Boolean getBoundStatus() { + return boundStatus; + } + + public TopologyDeviceDTO setBoundStatus(Boolean boundStatus) { + this.boundStatus = boundStatus; + return this; + } + + public String getGatewaySn() { + return gatewaySn; + } + + public TopologyDeviceDTO setGatewaySn(String gatewaySn) { + this.gatewaySn = gatewaySn; + return this; + } + + public DeviceDomainEnum getDomain() { + return domain; + } - private Integer domain; + public TopologyDeviceDTO setDomain(DeviceDomainEnum domain) { + this.domain = domain; + return this; + } } 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 3714974..de9083b 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 @@ -1,7 +1,5 @@ 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; @@ -16,7 +14,6 @@ import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor @Builder -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class WorkspaceDTO { private Integer id; 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 57ace0d..ae26ed1 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 @@ -44,7 +44,7 @@ public class DeviceEntity implements Serializable { private Integer domain; @TableField(value = "version") - private Integer version; + private String version; @TableField(value = "device_index") private String deviceIndex; diff --git a/src/main/java/com/dji/sample/manage/model/enums/ControlSourceEnum.java b/src/main/java/com/dji/sample/manage/model/enums/ControlSourceEnum.java deleted file mode 100644 index 764bc3c..0000000 --- a/src/main/java/com/dji/sample/manage/model/enums/ControlSourceEnum.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.manage.model.enums; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.Arrays; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/16 - */ -public enum ControlSourceEnum { - - A, B; - - @JsonValue - public String getControlSource() { - return name(); - } - - @JsonCreator - public static ControlSourceEnum find(String controlSource) { - return Arrays.stream(values()).filter(controlSourceEnum -> controlSourceEnum.name().equals(controlSource)).findAny().get(); - } -} diff --git a/src/main/java/com/dji/sample/manage/model/enums/CustomizeConfigScopeEnum.java b/src/main/java/com/dji/sample/manage/model/enums/CustomizeConfigScopeEnum.java new file mode 100644 index 0000000..4634264 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/enums/CustomizeConfigScopeEnum.java @@ -0,0 +1,33 @@ +package com.dji.sample.manage.model.enums; + +import com.dji.sample.manage.service.IRequestsConfigService; +import com.dji.sample.manage.service.impl.ConfigProductServiceImpl; +import com.dji.sdk.cloudapi.config.ConfigScopeEnum; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Optional; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/10 + */ +@Getter +public enum CustomizeConfigScopeEnum { + + PRODUCT(ConfigScopeEnum.PRODUCT, ConfigProductServiceImpl.class); + + ConfigScopeEnum scope; + + Class clazz; + + CustomizeConfigScopeEnum(ConfigScopeEnum scope, Class clazz) { + this.scope = scope; + this.clazz = clazz; + } + + public static Optional find(String scope) { + return Arrays.stream(CustomizeConfigScopeEnum.values()).filter(scopeEnum -> scopeEnum.scope.getScope().equals(scope)).findAny(); + } +} 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 deleted file mode 100644 index 2ce85a2..0000000 --- a/src/main/java/com/dji/sample/manage/model/enums/DeviceDomainEnum.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.dji.sample.manage.model.enums; - -import lombok.Getter; - -import java.util.Arrays; - -/** - * - * @author sean.zhou - * @date 2021/11/15 - * @version 0.1 - */ -@Getter -public enum DeviceDomainEnum { - - SUB_DEVICE(0), - - GATEWAY(2), - - PAYLOAD(1), - - DOCK (3), - - UNKNOWN(-1); - - int val; - - DeviceDomainEnum(int val) { - this.val = val; - } - - public static DeviceDomainEnum find(int val) { - return Arrays.stream(values()).filter(domainEnum -> domainEnum.val == val).findAny().orElse(UNKNOWN); - } -} diff --git a/src/main/java/com/dji/sample/manage/model/enums/DeviceFirmwareStatusEnum.java b/src/main/java/com/dji/sample/manage/model/enums/DeviceFirmwareStatusEnum.java index bccbba7..01beda2 100644 --- a/src/main/java/com/dji/sample/manage/model/enums/DeviceFirmwareStatusEnum.java +++ b/src/main/java/com/dji/sample/manage/model/enums/DeviceFirmwareStatusEnum.java @@ -1,5 +1,7 @@ package com.dji.sample.manage.model.enums; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; import lombok.Getter; import java.util.Arrays; @@ -9,7 +11,6 @@ import java.util.Arrays; * @version 1.2 * @date 2022/8/15 */ -@Getter public enum DeviceFirmwareStatusEnum { /** @@ -36,10 +37,16 @@ public enum DeviceFirmwareStatusEnum { int val; + @JsonValue + public int getVal() { + return val; + } + DeviceFirmwareStatusEnum(int val) { this.val = val; } + @JsonCreator public static DeviceFirmwareStatusEnum find(int val) { return Arrays.stream(DeviceFirmwareStatusEnum.values()) .filter(firmwareStatus -> firmwareStatus.val == val) diff --git a/src/main/java/com/dji/sample/manage/model/enums/DeviceLogsStatusEnum.java b/src/main/java/com/dji/sample/manage/model/enums/DeviceLogsStatusEnum.java index 53d1bfb..0fcbdd8 100644 --- a/src/main/java/com/dji/sample/manage/model/enums/DeviceLogsStatusEnum.java +++ b/src/main/java/com/dji/sample/manage/model/enums/DeviceLogsStatusEnum.java @@ -1,6 +1,6 @@ package com.dji.sample.manage.model.enums; -import com.dji.sample.component.mqtt.model.EventsResultStatusEnum; +import com.dji.sdk.cloudapi.log.FileUploadStatusEnum; import lombok.Getter; import java.util.Arrays; @@ -15,27 +15,28 @@ import java.util.HashSet; @Getter public enum DeviceLogsStatusEnum { - UPLOADING(1, EventsResultStatusEnum.IN_PROGRESS), + UPLOADING(1, FileUploadStatusEnum.FILE_PULL, FileUploadStatusEnum.FILE_ZIP, + FileUploadStatusEnum.FILE_UPLOADING, FileUploadStatusEnum.IN_PROGRESS, FileUploadStatusEnum.PAUSED), - DONE(2, EventsResultStatusEnum.OK), + DONE(2, FileUploadStatusEnum.OK), - CANCELED(3, EventsResultStatusEnum.CANCELED), + CANCELED(3, FileUploadStatusEnum.CANCELED), - FAILED(4, EventsResultStatusEnum.FAILED, EventsResultStatusEnum.REJECTED, EventsResultStatusEnum.TIMEOUT), + FAILED(4, FileUploadStatusEnum.FAILED, FileUploadStatusEnum.REJECTED, FileUploadStatusEnum.TIMEOUT), UNKNOWN(-1); int val; - HashSet status; + HashSet status; - DeviceLogsStatusEnum(int val, EventsResultStatusEnum... status) { + DeviceLogsStatusEnum(int val, FileUploadStatusEnum... status) { this.status = new HashSet<>(); Collections.addAll(this.status, status); this.val = val; } - public static DeviceLogsStatusEnum find(EventsResultStatusEnum status) { + public static DeviceLogsStatusEnum find(FileUploadStatusEnum status) { return Arrays.stream(DeviceLogsStatusEnum.values()).filter(element -> element.status.contains(status)).findAny().orElse(UNKNOWN); } } diff --git a/src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java b/src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java deleted file mode 100644 index 07c6658..0000000 --- a/src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.dji.sample.manage.model.enums; - -import com.dji.sample.manage.model.receiver.*; -import lombok.Getter; - -import java.util.Arrays; -import java.util.Optional; - -/** - * @author sean - * @version 1.3 - * @date 2022/10/27 - */ -@Getter -public enum DeviceSetPropertyEnum { - - NIGHT_LIGHTS_STATE("night_lights_state", NightLightsStateReceiver.class), - - HEIGHT_LIMIT("height_limit", HeightLimitReceiver.class), - - DISTANCE_LIMIT_STATUS("distance_limit_status", DistanceLimitStatusReceiver.class), - - OBSTACLE_AVOIDANCE("obstacle_avoidance", ObstacleAvoidanceReceiver.class), - - RTH_ALTITUDE("rth_altitude", RthAltitudeReceiver.class), - - OUT_OF_CONTROL_ACTION("out_of_control_action", OutOfControlActionReceiver.class); - - String property; - - Class clazz; - - DeviceSetPropertyEnum(String property, Class clazz) { - this.property = property; - this.clazz = clazz; - } - - public static Optional find(String property) { - return Arrays.stream(DeviceSetPropertyEnum.values()).filter(propertyEnum -> propertyEnum.property.equals(property)).findAny(); - } -} diff --git a/src/main/java/com/dji/sample/manage/model/enums/DockDrcStateEnum.java b/src/main/java/com/dji/sample/manage/model/enums/DockDrcStateEnum.java deleted file mode 100644 index 9683f1f..0000000 --- a/src/main/java/com/dji/sample/manage/model/enums/DockDrcStateEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.dji.sample.manage.model.enums; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.Arrays; - -/** - * @author sean - * @version 1.4 - * @date 2023/2/28 - */ -public enum DockDrcStateEnum { - - DISCONNECTED(0), - - CONNECTING(1), - - CONNECTED(2); - - int val; - - DockDrcStateEnum(int val) { - this.val = val; - } - - @JsonValue - public int getVal() { - return val; - } - - @JsonCreator - public static DockDrcStateEnum find(int val) { - return Arrays.stream(values()).filter(drcState -> drcState.getVal() == val).findAny().orElse(DISCONNECTED); - } -} diff --git a/src/main/java/com/dji/sample/manage/model/enums/DroneRcLostActionEnum.java b/src/main/java/com/dji/sample/manage/model/enums/DroneRcLostActionEnum.java deleted file mode 100644 index 116847e..0000000 --- a/src/main/java/com/dji/sample/manage/model/enums/DroneRcLostActionEnum.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.manage.model.enums; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.Arrays; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/1 - */ -public enum DroneRcLostActionEnum { - - HOVER, LAND, RETURN_HOME; - - @JsonValue - public int getVal() { - return ordinal(); - } - - @JsonCreator - public static DroneRcLostActionEnum find(int val) { - return Arrays.stream(values()).filter(controlActionEnum -> controlActionEnum.ordinal() == val).findAny().get(); - } -} diff --git a/src/main/java/com/dji/sample/manage/model/enums/WaylineRcLostActionEnum.java b/src/main/java/com/dji/sample/manage/model/enums/ExitWaylineWhenRcLostActionEnum.java similarity index 59% rename from src/main/java/com/dji/sample/manage/model/enums/WaylineRcLostActionEnum.java rename to src/main/java/com/dji/sample/manage/model/enums/ExitWaylineWhenRcLostActionEnum.java index 3632452..aff6504 100644 --- a/src/main/java/com/dji/sample/manage/model/enums/WaylineRcLostActionEnum.java +++ b/src/main/java/com/dji/sample/manage/model/enums/ExitWaylineWhenRcLostActionEnum.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.enums; +import com.dji.sdk.exception.CloudSDKException; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; @@ -10,7 +11,7 @@ import java.util.Arrays; * @version 1.4 * @date 2023/3/15 */ -public enum WaylineRcLostActionEnum { +public enum ExitWaylineWhenRcLostActionEnum { CONTINUE_WAYLINE, EXECUTE_RC_LOST_ACTION; @@ -20,7 +21,8 @@ public enum WaylineRcLostActionEnum { } @JsonCreator - public static WaylineRcLostActionEnum find(int val) { - return Arrays.stream(values()).filter(lostActionEnum -> lostActionEnum.ordinal() == val).findAny().get(); + public static ExitWaylineWhenRcLostActionEnum find(int val) { + return Arrays.stream(values()).filter(lostActionEnum -> lostActionEnum.ordinal() == val).findAny() + .orElseThrow(() -> new CloudSDKException(ExitWaylineWhenRcLostActionEnum.class, 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 deleted file mode 100644 index 20af72f..0000000 --- a/src/main/java/com/dji/sample/manage/model/enums/HmsEnum.java +++ /dev/null @@ -1,177 +0,0 @@ -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 HmsFaqIdEnum { - - DOCK_TIP("dock_tip_"), - - 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); - } - } - - @Getter - public enum FormatKeyEnum { - - ALARM_ID("alarmid", 0), - - COMPONENT_INDEX("component_index", 1), - - INDEX("index", 2), - - BATTERY_INDEX("battery_index", 3), - - DOCK_COVER_INDEX("dock_cover_index", 4), - - CHARGING_ROD_INDEX("charging_rod_index", 5), - - UNKNOWN("unknown", -1); - - public static final char KEY_START = '%'; - - String key; - int index; - - FormatKeyEnum(String key, int index) { - this.key = key; - this.index = index; - } - - public static FormatKeyEnum find(String key) { - return Arrays.stream(FormatKeyEnum.values()) - .filter(format -> format.getKey().equals(key)) - .findAny().orElse(UNKNOWN); - } - } -} diff --git a/src/main/java/com/dji/sample/manage/model/enums/LogsFileMethodEnum.java b/src/main/java/com/dji/sample/manage/model/enums/LogsFileMethodEnum.java deleted file mode 100644 index 42b1b87..0000000 --- a/src/main/java/com/dji/sample/manage/model/enums/LogsFileMethodEnum.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.manage.model.enums; - -import lombok.Getter; - -/** - * @author sean - * @version 1.3 - * @date 2022/11/14 - */ -@Getter -public enum LogsFileMethodEnum { - - FILE_UPLOAD_LIST("fileupload_list"), - - FILE_UPLOAD_START("fileupload_start"), - - FILE_UPLOAD_UPDATE("fileupload_update"), - - UNKNOWN("unknown"); - - private String method; - - LogsFileMethodEnum(String method) { - this.method = method; - } -} diff --git a/src/main/java/com/dji/sample/manage/model/enums/PropertySetFieldEnum.java b/src/main/java/com/dji/sample/manage/model/enums/PropertySetFieldEnum.java new file mode 100644 index 0000000..a80f0f9 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/enums/PropertySetFieldEnum.java @@ -0,0 +1,68 @@ +package com.dji.sample.manage.model.enums; + +import com.dji.sample.manage.model.receiver.*; +import com.dji.sdk.cloudapi.property.PropertySetEnum; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/10/27 + */ +public enum PropertySetFieldEnum { + + NIGHT_LIGHTS_STATE(PropertySetEnum.NIGHT_LIGHTS_STATE, NightLightsStateReceiver.class), + + HEIGHT_LIMIT(PropertySetEnum.HEIGHT_LIMIT, HeightLimitReceiver.class), + + DISTANCE_LIMIT_STATUS(PropertySetEnum.DISTANCE_LIMIT_STATUS, DistanceLimitStatusReceiver.class), + + OBSTACLE_AVOIDANCE(PropertySetEnum.OBSTACLE_AVOIDANCE, ObstacleAvoidanceReceiver.class), + + RTH_ALTITUDE(PropertySetEnum.RTH_ALTITUDE, RthAltitudeReceiver.class), + + OUT_OF_CONTROL_ACTION(PropertySetEnum.OUT_OF_CONTROL_ACTION, OutOfControlActionReceiver.class), + +// EXIT_WAYLINE_WHEN_RC_LOST(PropertySetEnum.EXIT_WAYLINE_WHEN_RC_LOST, .class), +// +// THERMAL_CURRENT_PALETTE_STYLE(PropertySetEnum.THERMAL_CURRENT_PALETTE_STYLE, .class), +// +// THERMAL_GAIN_MODE(PropertySetEnum.THERMAL_GAIN_MODE, .class), +// +// THERMAL_ISOTHERM_STATE(PropertySetEnum.THERMAL_ISOTHERM_STATE, .class), +// +// THERMAL_ISOTHERM_UPPER_LIMIT(PropertySetEnum.THERMAL_ISOTHERM_UPPER_LIMIT, .class), +// +// THERMAL_ISOTHERM_LOWER_LIMIT(PropertySetEnum.THERMAL_ISOTHERM_LOWER_LIMIT, .class), + + ; + + private final PropertySetEnum property; + + private final Class clazz; + + PropertySetFieldEnum(PropertySetEnum property, Class clazz) { + this.property = property; + this.clazz = clazz; + } + + public PropertySetEnum getProperty() { + return property; + } + + @JsonValue + public String getPropertyName() { + return property.getProperty(); + } + + public Class getClazz() { + return clazz; + } + + public static PropertySetFieldEnum find(String property) { + return Arrays.stream(values()).filter(propertyEnum -> propertyEnum.property.getProperty().equals(property)).findAny() + .orElseThrow(); + } +} diff --git a/src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareQueryParam.java b/src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareQueryParam.java index 6dc4ad5..ffcc8fb 100644 --- a/src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareQueryParam.java +++ b/src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareQueryParam.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.param; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -18,8 +19,10 @@ import javax.validation.constraints.NotNull; @NoArgsConstructor public class DeviceFirmwareQueryParam { + @JsonProperty("device_name") private String deviceName; + @JsonProperty("product_version") private String productVersion; private Boolean status; @@ -28,5 +31,6 @@ public class DeviceFirmwareQueryParam { private Long page; @NotNull + @JsonProperty("page_size") private Long pageSize; } diff --git a/src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareUploadParam.java b/src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareUploadParam.java index f48f4f0..deeb67f 100644 --- a/src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareUploadParam.java +++ b/src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareUploadParam.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.param; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import javax.validation.constraints.NotNull; @@ -14,11 +15,13 @@ import java.util.List; public class DeviceFirmwareUploadParam { @NotNull + @JsonProperty("release_note") private String releaseNote; @NotNull private Boolean status; @NotNull + @JsonProperty("device_name") private List deviceName; } 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 index c35e8bb..35eaca8 100644 --- a/src/main/java/com/dji/sample/manage/model/param/DeviceHmsQueryParam.java +++ b/src/main/java/com/dji/sample/manage/model/param/DeviceHmsQueryParam.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.param; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -18,10 +19,13 @@ import java.util.Set; @NoArgsConstructor public class DeviceHmsQueryParam { + @JsonProperty("device_sn") private Set deviceSn; + @JsonProperty("begin_time") private Long beginTime; + @JsonProperty("end_time") private Long endTime; private String language; @@ -30,9 +34,11 @@ public class DeviceHmsQueryParam { 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/DeviceLogsCreateParam.java b/src/main/java/com/dji/sample/manage/model/param/DeviceLogsCreateParam.java index 0c6aea1..7870d79 100644 --- a/src/main/java/com/dji/sample/manage/model/param/DeviceLogsCreateParam.java +++ b/src/main/java/com/dji/sample/manage/model/param/DeviceLogsCreateParam.java @@ -1,6 +1,6 @@ package com.dji.sample.manage.model.param; -import com.dji.sample.manage.model.receiver.LogsFileUpload; +import com.dji.sdk.cloudapi.log.FileUploadStartFile; import lombok.Data; import java.util.List; @@ -17,5 +17,5 @@ public class DeviceLogsCreateParam { private Long happenTime; - private List files; + private List files; } diff --git a/src/main/java/com/dji/sample/manage/model/param/DeviceLogsGetParam.java b/src/main/java/com/dji/sample/manage/model/param/DeviceLogsGetParam.java new file mode 100644 index 0000000..54325a9 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/model/param/DeviceLogsGetParam.java @@ -0,0 +1,19 @@ +package com.dji.sample.manage.model.param; + +import com.dji.sdk.cloudapi.log.LogModuleEnum; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/7 + */ +@Data +public class DeviceLogsGetParam { + + @JsonProperty("domain_list") + List domainList; +} diff --git a/src/main/java/com/dji/sample/manage/model/param/DeviceLogsQueryParam.java b/src/main/java/com/dji/sample/manage/model/param/DeviceLogsQueryParam.java index c52a777..5674bb8 100644 --- a/src/main/java/com/dji/sample/manage/model/param/DeviceLogsQueryParam.java +++ b/src/main/java/com/dji/sample/manage/model/param/DeviceLogsQueryParam.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.param; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; /** @@ -12,13 +13,17 @@ public class DeviceLogsQueryParam { private Long page; + @JsonProperty("page_size") private Long pageSize; private Integer status; + @JsonProperty("begin_time") private Long beginTime; + @JsonProperty("end_time") private Long endTime; + @JsonProperty("logs_information") private String logsInformation; } diff --git a/src/main/java/com/dji/sample/manage/model/param/DeviceOtaCreateParam.java b/src/main/java/com/dji/sample/manage/model/param/DeviceOtaCreateParam.java deleted file mode 100644 index 12fbb3d..0000000 --- a/src/main/java/com/dji/sample/manage/model/param/DeviceOtaCreateParam.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.dji.sample.manage.model.param; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 1.2 - * @date 2022/8/16 - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class DeviceOtaCreateParam { - - private String sn; - - private String productVersion; - - private String fileUrl; - - private String md5; - - private Long fileSize; - - private Integer firmwareUpgradeType; - - private String fileName; -} 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 deleted file mode 100644 index fa6924e..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/AlternateLandPointReceiver.java +++ /dev/null @@ -1,20 +0,0 @@ -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; - - private Integer isConfigured; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java deleted file mode 100644 index 257316d..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/BackupBatteryReceiver.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; - -/** - * @author sean - * @version 1.3 - * @date 2022/11/3 - */ -@Data -public class BackupBatteryReceiver { - - private Integer voltage; - - private Float temperature; - - @JsonProperty("switch") - private Integer batterySwitch; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java b/src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java index f32273d..c02b546 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/BasicDeviceProperty.java @@ -1,5 +1,7 @@ package com.dji.sample.manage.model.receiver; +import com.dji.sdk.cloudapi.device.OsdDockDrone; + /** * @author sean * @version 1.3 @@ -11,7 +13,7 @@ public abstract class BasicDeviceProperty { return false; } - public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) { + public boolean canPublish(OsdDockDrone osd) { return valid(); } } diff --git a/src/main/java/com/dji/sample/manage/model/receiver/BatteryReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/BatteryReceiver.java deleted file mode 100644 index dc0162f..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/BatteryReceiver.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -import java.util.List; - -/** - * @author sean - * @version 0.3 - * @date 2022/1/27 - */ -@Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class BatteryReceiver { - - private List batteries; - - private Integer capacityPercent; - - private Integer landingPower; - - private Integer remainFlightTime; - - private Integer returnHomePower; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/BatteryStateReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/BatteryStateReceiver.java deleted file mode 100644 index 275adc1..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/BatteryStateReceiver.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -/** - * @author sean.zhou - * @version 0.1 - * @date 2021/11/24 - */ -@Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class BatteryStateReceiver { - - private String firmwareVersion; - - private Integer index; - - private Integer loopTimes; - - private Integer capacityPercent; - - private String sn; - - private Integer subType; - - private Float temperature; - - private Integer type; - - private Integer voltage; - -} \ No newline at end of file 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 deleted file mode 100644 index 7f13246..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/BindDeviceReceiver.java +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index 61c8477..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/BindStatusReceiver.java +++ /dev/null @@ -1,30 +0,0 @@ -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/CapacityCameraReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/CapacityCameraReceiver.java index e5321fb..9747533 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/CapacityCameraReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/CapacityCameraReceiver.java @@ -1,8 +1,6 @@ package com.dji.sample.manage.model.receiver; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.dji.sdk.cloudapi.device.PayloadIndex; import lombok.Data; import java.util.List; @@ -13,16 +11,14 @@ import java.util.List; * @version 0.1 */ @Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class CapacityCameraReceiver { private Integer availableVideoNumber; private Integer coexistVideoNumberMax; - private String cameraIndex; + private PayloadIndex cameraIndex; - @JsonProperty(value = "video_list") - private List videosList; + private List videoList; } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/receiver/CapacityDeviceReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/CapacityDeviceReceiver.java index 750db59..63edf06 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/CapacityDeviceReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/CapacityDeviceReceiver.java @@ -1,7 +1,5 @@ package com.dji.sample.manage.model.receiver; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; import lombok.Data; import java.util.List; @@ -12,7 +10,6 @@ import java.util.List; * @version 0.1 */ @Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class CapacityDeviceReceiver { private String sn; @@ -22,4 +19,5 @@ public class CapacityDeviceReceiver { private Integer coexistVideoNumberMax; private List cameraList; + } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java index 6c17c15..1939641 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/CapacityVideoReceiver.java @@ -1,7 +1,6 @@ package com.dji.sample.manage.model.receiver; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.dji.sdk.cloudapi.livestream.VideoTypeEnum; import lombok.Data; import java.util.List; @@ -12,12 +11,11 @@ import java.util.List; * @version 0.1 */ @Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class CapacityVideoReceiver { private String videoIndex; - private String videoType; + private VideoTypeEnum videoType; - private List switchableVideoTypes; + private List switchableVideoTypes; } \ No newline at end of file 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 fa22b73..423624c 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 @@ -1,8 +1,7 @@ package com.dji.sample.manage.model.receiver; +import com.dji.sample.manage.model.dto.DevicePayloadReceiver; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; import lombok.Data; import java.util.List; @@ -14,16 +13,15 @@ import java.util.List; */ @Data @JsonIgnoreProperties(ignoreUnknown = true) -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class DeviceBasicReceiver { - private String controlSource; - private String deviceSn; - private Double homeLatitude; + private String controlSource; + + private Float homeLatitude; - private Double homeLongitude; + private Float homeLongitude; private Integer lowBatteryWarningThreshold; 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 deleted file mode 100644 index cc0d66c..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/DeviceHmsReceiver.java +++ /dev/null @@ -1,26 +0,0 @@ -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 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 deleted file mode 100644 index 830c9d6..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/DevicePayloadReceiver.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -/** - * @author sean.zhou - * @date 2021/11/18 - * @version 0.1 - */ -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class DevicePayloadReceiver { - - private String deviceSn; - - private String controlSource; - - private String payloadIndex; - - private String sn; - -} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java index 1fb2789..42c9004 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java @@ -1,6 +1,8 @@ package com.dji.sample.manage.model.receiver; -import com.dji.sample.manage.model.enums.StateSwitchEnum; +import com.dji.sdk.cloudapi.device.DockDistanceLimitStatus; +import com.dji.sdk.cloudapi.device.OsdDockDrone; +import com.dji.sdk.cloudapi.device.SwitchActionEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -20,7 +22,7 @@ import java.util.Objects; @NoArgsConstructor public class DistanceLimitStatusReceiver extends BasicDeviceProperty { - private Integer state; + private SwitchActionEnum state; private Integer distanceLimit; @@ -30,30 +32,20 @@ public class DistanceLimitStatusReceiver extends BasicDeviceProperty { @Override public boolean valid() { - boolean valid = Objects.nonNull(state) || Objects.nonNull(distanceLimit); - if (Objects.nonNull(state)) { - valid = StateSwitchEnum.find(state).isPresent(); + if (Objects.isNull(state) && Objects.isNull(distanceLimit)) { + return false; } if (Objects.nonNull(distanceLimit)) { - valid &= distanceLimit >= DISTANCE_MIN && distanceLimit <= DISTANCE_MAX; + return distanceLimit >= DISTANCE_MIN && distanceLimit <= DISTANCE_MAX; } - return valid; + return true; } @Override - public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) { - DistanceLimitStatusReceiver distanceLimitStatus = osd.getDistanceLimitStatus(); - switch (fieldName) { - case "state": - return Objects.isNull(distanceLimitStatus.getState()) || - Objects.nonNull(distanceLimitStatus.getState()) && - distanceLimitStatus.getState().intValue() != this.state; - case "distance_limit": - return Objects.isNull(distanceLimitStatus.getDistanceLimit()) || - Objects.nonNull(distanceLimitStatus.getDistanceLimit()) && - distanceLimitStatus.getDistanceLimit().intValue() != this.distanceLimit; - default: - throw new RuntimeException("Property " + fieldName + " does not exist."); - } + public boolean canPublish(OsdDockDrone osd) { + DockDistanceLimitStatus distanceLimitStatus = osd.getDistanceLimitStatus(); + return (Objects.nonNull(distanceLimitStatus.getState()) && distanceLimitStatus.getState() != this.state) + || (Objects.nonNull(distanceLimitStatus.getDistanceLimit()) + && distanceLimitStatus.getDistanceLimit().intValue() != this.distanceLimit); } } 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 deleted file mode 100644 index bd7745a..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/DockMediaFileDetailReceiver.java +++ /dev/null @@ -1,14 +0,0 @@ -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/DockSubDeviceReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DockSubDeviceReceiver.java deleted file mode 100644 index 4708ea7..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/DockSubDeviceReceiver.java +++ /dev/null @@ -1,20 +0,0 @@ -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/DockWirelessLinkReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java deleted file mode 100644 index 83bfb3f..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/DockWirelessLinkReceiver.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; - -/** - * @author sean - * @version 1.3 - * @date 2022/11/3 - */ -@Data -public class DockWirelessLinkReceiver { - - @JsonProperty("4g_freq_band") - private Float fourGFreqBand; - - @JsonProperty("4g_gnd_quality") - private Integer fourGGndQuality; - - @JsonProperty("4g_link_state") - private Integer fourGLinkState; - - @JsonProperty("4g_quality") - private Integer fourGQuality; - - @JsonProperty("4g_uav_quality") - private Integer fourGUavQuality; - - private Integer dongleNumber; - - private Integer linkWorkmode; - - private Float sdrFreqBand; - - private Integer sdrLinkState; - - private Integer sdrQuality; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java deleted file mode 100644 index 1718aca..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/DroneBatteryMaintenanceInfoReceiver.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import lombok.Data; - -/** - * @author sean - * @version 1.4 - * @date 2022/11/3 - */ -@Data -public class DroneBatteryMaintenanceInfoReceiver { - - private Integer maintenanceState; - - private Long maintenanceTimeLeft; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/DroneChargeStateReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/DroneChargeStateReceiver.java deleted file mode 100644 index 6fe4f49..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/DroneChargeStateReceiver.java +++ /dev/null @@ -1,16 +0,0 @@ -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/FirmwareProgressExtReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/FirmwareProgressExtReceiver.java deleted file mode 100644 index 9818b93..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/FirmwareProgressExtReceiver.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import lombok.Data; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/30 - */ -@Data -public class FirmwareProgressExtReceiver { - - private Long rate; -} 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 index a3c2634..94ed706 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/FirmwareVersionReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/FirmwareVersionReceiver.java @@ -1,6 +1,6 @@ package com.dji.sample.manage.model.receiver; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java index ded4720..a8b8e4f 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.receiver; +import com.dji.sdk.cloudapi.device.OsdDockDrone; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -22,10 +23,15 @@ public class HeightLimitReceiver extends BasicDeviceProperty { private static final int HEIGHT_LIMIT_MIN = 20; - private Integer value; + private Integer heightLimit; @Override public boolean valid() { - return Objects.nonNull(this.value) && this.value >= HEIGHT_LIMIT_MIN && this.value <= HEIGHT_LIMIT_MAX; + return Objects.nonNull(this.heightLimit) && this.heightLimit >= HEIGHT_LIMIT_MIN && this.heightLimit <= HEIGHT_LIMIT_MAX; + } + + @Override + public boolean canPublish(OsdDockDrone osd) { + return heightLimit.intValue() != osd.getHeightLimit(); } } 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 deleted file mode 100644 index 2d35a1e..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/HmsArgsReceiver.java +++ /dev/null @@ -1,18 +0,0 @@ -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/LiveStatusReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/LiveStatusReceiver.java deleted file mode 100644 index 463cbf8..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/LiveStatusReceiver.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -/** - * @author sean.zhou - * @version 0.1 - * @date 2021/11/23 - */ -@Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class LiveStatusReceiver { - - private Long liveTime; - - private Integer liveTrendline; - - private String videoId; - - private Integer videoQuality; -} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/receiver/LiveviewWorldRegionReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/LiveviewWorldRegionReceiver.java deleted file mode 100644 index b6a09e9..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/LiveviewWorldRegionReceiver.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import lombok.Data; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/8 - */ -@Data -public class LiveviewWorldRegionReceiver { - - private Double bottom; - - private Double left; - - private Double right; - - private Double top; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/LogsExtFileReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/LogsExtFileReceiver.java deleted file mode 100644 index 93b15f2..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/LogsExtFileReceiver.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; - -/** - * @author sean - * @version 1.2 - * @date 2022/9/9 - */ -@Data -public class LogsExtFileReceiver { - - @JsonProperty("module") - private String deviceModelDomain; - - private Long size; - - private String deviceSn; - - private String key; - - private String fingerprint; - - private LogsProgressReceiver progress; - -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/LogsFile.java b/src/main/java/com/dji/sample/manage/model/receiver/LogsFile.java deleted file mode 100644 index 29d2fa6..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/LogsFile.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 1.2 - * @date 2022/9/7 - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class LogsFile { - - private Integer bootIndex; - - private Long endTime; - - private Long startTime; - - private Long size; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/LogsProgressReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/LogsProgressReceiver.java deleted file mode 100644 index f2dad0c..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/LogsProgressReceiver.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import lombok.Data; - -/** - * @author sean - * @version 1.2 - * @date 2022/9/9 - */ -@Data -public class LogsProgressReceiver { - - private Integer currentStep; - - private Integer totalStep; - - private Integer progress; - - private Long finishTime; - - private Float uploadRate; - - private String status; - - private Integer result; -} 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 deleted file mode 100644 index 2aac8ae..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/NetworkStateReceiver.java +++ /dev/null @@ -1,18 +0,0 @@ -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/NightLightsStateReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/NightLightsStateReceiver.java index aae250e..c966b26 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/NightLightsStateReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/NightLightsStateReceiver.java @@ -1,10 +1,8 @@ package com.dji.sample.manage.model.receiver; -import com.dji.sample.manage.model.enums.StateSwitchEnum; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import com.dji.sdk.cloudapi.device.OsdDockDrone; +import com.dji.sdk.cloudapi.device.SwitchActionEnum; +import com.fasterxml.jackson.annotation.JsonCreator; import java.util.Objects; @@ -13,16 +11,22 @@ import java.util.Objects; * @version 1.3 * @date 2022/11/25 */ -@EqualsAndHashCode(callSuper = true) -@Data -@AllArgsConstructor -@NoArgsConstructor public class NightLightsStateReceiver extends BasicDeviceProperty { - private Integer value; + private SwitchActionEnum nightLightsState; + + @JsonCreator + public NightLightsStateReceiver(Integer nightLightsState) { + this.nightLightsState = SwitchActionEnum.find(nightLightsState); + } @Override public boolean valid() { - return Objects.nonNull(value) && StateSwitchEnum.find(value).isPresent(); + return Objects.nonNull(nightLightsState); + } + + @Override + public boolean canPublish(OsdDockDrone osd) { + return nightLightsState != osd.getNightLightsState(); } } diff --git a/src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java index 4187d57..cc10413 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java @@ -1,6 +1,8 @@ package com.dji.sample.manage.model.receiver; -import com.dji.sample.manage.model.enums.StateSwitchEnum; +import com.dji.sdk.cloudapi.device.ObstacleAvoidance; +import com.dji.sdk.cloudapi.device.OsdDockDrone; +import com.dji.sdk.cloudapi.device.SwitchActionEnum; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,46 +17,21 @@ import java.util.Objects; @Data public class ObstacleAvoidanceReceiver extends BasicDeviceProperty { - private Integer horizon; + private SwitchActionEnum horizon; - private Integer upside; + private SwitchActionEnum upside; - private Integer downside; + private SwitchActionEnum downside; - @Override public boolean valid() { - boolean valid = Objects.nonNull(this.horizon) || Objects.nonNull(this.upside) || Objects.nonNull(this.downside); - - if (Objects.nonNull(this.horizon)) { - valid = StateSwitchEnum.find(horizon).isPresent(); - } - if (Objects.nonNull(this.upside)) { - valid &= StateSwitchEnum.find(upside).isPresent(); - } - if (Objects.nonNull(this.downside)) { - valid &= StateSwitchEnum.find(downside).isPresent(); - } - return valid; + return Objects.nonNull(this.horizon) || Objects.nonNull(this.upside) || Objects.nonNull(this.downside); } @Override - public boolean canPublish(String fieldName, OsdSubDeviceReceiver osd) { - ObstacleAvoidanceReceiver obstacleAvoidance = osd.getObstacleAvoidance(); - switch (fieldName) { - case "horizon": - return Objects.isNull(obstacleAvoidance.getHorizon()) || - Objects.nonNull(obstacleAvoidance.getHorizon()) && - obstacleAvoidance.getHorizon().intValue() != this.horizon; - case "upside": - return Objects.isNull(obstacleAvoidance.getUpside()) || - Objects.nonNull(obstacleAvoidance.getUpside()) && - obstacleAvoidance.getUpside().intValue() != this.upside; - case "downside": - return Objects.isNull(obstacleAvoidance.getDownside()) || - Objects.nonNull(obstacleAvoidance.getDownside()) && - obstacleAvoidance.getDownside().intValue() != this.downside; - default: - throw new RuntimeException("Property " + fieldName + " does not exist."); - } + public boolean canPublish(OsdDockDrone osd) { + ObstacleAvoidance obstacleAvoidance = osd.getObstacleAvoidance(); + return (Objects.nonNull(obstacleAvoidance.getHorizon()) && horizon != obstacleAvoidance.getHorizon()) + || (Objects.nonNull(obstacleAvoidance.getUpside()) && upside != obstacleAvoidance.getUpside()) + || (Objects.nonNull(obstacleAvoidance.getDownside()) && downside != obstacleAvoidance.getDownside()); } } 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 deleted file mode 100644 index 3b92484..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/OrganizationGetReceiver.java +++ /dev/null @@ -1,16 +0,0 @@ -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/OsdCameraReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OsdCameraReceiver.java deleted file mode 100644 index 96de1bc..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/OsdCameraReceiver.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.dji.sample.control.model.enums.CameraModeEnum; -import com.dji.sample.control.model.enums.CameraStateEnum; -import lombok.Data; - -/** - * @author sean - * @version 1.4 - * @date 2023/3/8 - */ -@Data -public class OsdCameraReceiver { - - private CameraModeEnum cameraMode; - - private LiveviewWorldRegionReceiver liveviewWorldRegion; - - private String payloadIndex; - - private CameraStateEnum photoState; - - private Integer recordTime; - - private CameraStateEnum recordingState; - - private Long remainPhotoNum; - - private Long remainRecordDuration; - - private Float zoomFactor; - - private Float irZoomFactor; -} 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 deleted file mode 100644 index 8826d35..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/OsdDockReceiver.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.dji.sample.manage.model.enums.DockDrcStateEnum; -import com.dji.sample.manage.model.enums.DockModeCodeEnum; -import lombok.Data; - -/** - * @author sean - * @version 1.3 - * @date 2022/11/3 - */ -@Data -public class OsdDockReceiver { - - private NetworkStateReceiver networkState; - - private Integer droneInDock; - - private DroneChargeStateReceiver droneChargeState; - - private Integer rainfall; - - private Float windSpeed; - - private Float environmentTemperature; - - private Float temperature; - - private Integer humidity; - - private Double latitude; - - private Double longitude; - - private Double height; - - private AlternateLandPointReceiver alternateLandPoint; - - private Long firstPowerOn; - - private PositionStateReceiver positionState; - - private StorageReceiver storage; - - private DockModeCodeEnum modeCode; - - private Integer coverState; - - private Integer supplementLightState; - - private Boolean emergencyStopState; - - private Integer airConditionerMode; - - private Integer batteryStoreMode; - - private Integer alarmState; - - private Integer putterState; - - private DockSubDeviceReceiver subDevice; - - private Integer jobNumber; - - private Long accTime; - - private Long activationTime; - - private DeviceMaintainStatusReceiver maintainStatus; - - private Integer electricSupplyVoltage; - - private Integer workingVoltage; - - private Integer workingCurrent; - - private BackupBatteryReceiver backupBattery; - - private DroneBatteryMaintenanceInfoReceiver droneBatteryMaintenanceInfo; - - private Integer flighttaskStepCode; - - private Integer flighttaskPrepareCapacity; - - private DockMediaFileDetailReceiver mediaFileDetail; - - private DockSdrReceiver sdr; - - private DockWirelessLinkReceiver wirelessLink; - - private DockDrcStateEnum drcState; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/OsdGatewayReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OsdGatewayReceiver.java deleted file mode 100644 index 4e0a299..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/OsdGatewayReceiver.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -/** - * @author sean.zhou - * @version 0.1 - * @date 2021/11/23 - */ -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class OsdGatewayReceiver { - - private Double latitude; - - private Double longitude; - - @JsonProperty("capacity_percent") - private Integer remainPower; - - private Integer transmissionSignalQuality; - - private LiveStatusReceiver liveStatus; - - private WirelessLinkStateReceiver wirelessLinkState; -} \ No newline at end of file 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 deleted file mode 100644 index e4125c6..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/OsdPayloadReceiver.java +++ /dev/null @@ -1,33 +0,0 @@ -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 deleted file mode 100644 index 148ad9a..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/OsdSubDeviceReceiver.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; -import com.dji.sample.manage.model.enums.DroneRcLostActionEnum; -import com.dji.sample.manage.model.enums.WaylineRcLostActionEnum; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -import java.util.List; - -/** - * @author sean.zhou - * @version 0.1 - * @date 2021/11/23 - */ -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class OsdSubDeviceReceiver { - - private Float attitudeHead; - - private Double attitudePitch; - - private Double attitudeRoll; - - private Double elevation; - - private BatteryReceiver battery; - - private String firmwareVersion; - - private Integer gear; - - private Double height; - - private Double homeDistance; - - private Float horizontalSpeed; - - private Double latitude; - - private Double longitude; - - private DeviceModeCodeEnum modeCode; - - private Double totalFlightDistance; - - private Double totalFlightTime; - - private Float verticalSpeed; - - private Double windDirection; - - private Float windSpeed; - - private PositionStateReceiver positionState; - - private List payloads; - - private StorageReceiver storage; - - private Integer nightLightsState; - - private Integer heightLimit; - - private DistanceLimitStatusReceiver distanceLimitStatus; - - private ObstacleAvoidanceReceiver obstacleAvoidance; - - private Long activationTime; - - private List cameras; - - private DroneRcLostActionEnum rcLostAction; - - private Integer rthAltitude; - - private Integer totalFlightSorties; - - private WaylineRcLostActionEnum exitWaylineWhenRcLost; -} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/model/receiver/OutOfControlActionReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OutOfControlActionReceiver.java index 836242e..7b00d69 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/OutOfControlActionReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/OutOfControlActionReceiver.java @@ -1,10 +1,8 @@ package com.dji.sample.manage.model.receiver; -import com.dji.sample.manage.model.enums.DroneRcLostActionEnum; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import com.dji.sdk.cloudapi.device.OsdDockDrone; +import com.dji.sdk.cloudapi.device.RcLostActionEnum; +import com.fasterxml.jackson.annotation.JsonCreator; import java.util.Objects; @@ -13,17 +11,22 @@ import java.util.Objects; * @version 1.4 * @date 2023/3/3 */ -@EqualsAndHashCode(callSuper = true) -@Data -@AllArgsConstructor -@NoArgsConstructor public class OutOfControlActionReceiver extends BasicDeviceProperty { - private Integer value; + private RcLostActionEnum rcLostAction; + + @JsonCreator + public OutOfControlActionReceiver(Integer rcLostAction) { + this.rcLostAction = RcLostActionEnum.find(rcLostAction); + } @Override public boolean valid() { - return Objects.nonNull(value) && value >= 0 && value < DroneRcLostActionEnum.values().length; + return Objects.nonNull(rcLostAction); } + @Override + public boolean canPublish(OsdDockDrone osd) { + return rcLostAction != osd.getRcLostAction(); + } } diff --git a/src/main/java/com/dji/sample/manage/model/receiver/OutputLogsExtReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OutputLogsExtReceiver.java deleted file mode 100644 index 84039b7..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/OutputLogsExtReceiver.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import lombok.Data; - -import java.util.List; - -/** - * @author sean - * @version 1.2 - * @date 2022/9/9 - */ -@Data -public class OutputLogsExtReceiver { - - private List files; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/OutputLogsProgressReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/OutputLogsProgressReceiver.java index 3fd7170..9f5a0a6 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/OutputLogsProgressReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/OutputLogsProgressReceiver.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.receiver; +import com.dji.sdk.cloudapi.log.FileUploadProgressExt; import lombok.Data; /** @@ -10,7 +11,7 @@ import lombok.Data; @Data public class OutputLogsProgressReceiver { - private OutputLogsExtReceiver ext; + private FileUploadProgressExt ext; private String status; } 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 deleted file mode 100644 index ad7baec..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/PositionStateReceiver.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -/** - * @author sean - * @version 0.3 - * @date 2022/1/27 - */ -@Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class PositionStateReceiver { - - private Integer isCalibration; - - private Integer gpsNumber; - - private Integer isFixed; - - private Integer quality; - - private Integer rtkNumber; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/RequestConfigReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/RequestConfigReceiver.java deleted file mode 100644 index e468a6f..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/RequestConfigReceiver.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.dji.sample.manage.model.receiver; - -import lombok.Data; - -/** - * @author sean - * @version 1.3 - * @date 2022/11/10 - */ -@Data -public class RequestConfigReceiver { - - private String configType; - - private String configScope; -} diff --git a/src/main/java/com/dji/sample/manage/model/receiver/RthAltitudeReceiver.java b/src/main/java/com/dji/sample/manage/model/receiver/RthAltitudeReceiver.java index 1727944..0d3bd54 100644 --- a/src/main/java/com/dji/sample/manage/model/receiver/RthAltitudeReceiver.java +++ b/src/main/java/com/dji/sample/manage/model/receiver/RthAltitudeReceiver.java @@ -1,5 +1,6 @@ package com.dji.sample.manage.model.receiver; +import com.dji.sdk.cloudapi.device.OsdDockDrone; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -18,7 +19,7 @@ import java.util.Objects; @NoArgsConstructor public class RthAltitudeReceiver extends BasicDeviceProperty { - private Integer value; + private Integer rthAltitude; private static final int RTH_ALTITUDE_MAX = 500; @@ -26,7 +27,11 @@ public class RthAltitudeReceiver extends BasicDeviceProperty { @Override public boolean valid() { - return Objects.nonNull(this.value) && this.value >= RTH_ALTITUDE_MIN && this.value <= RTH_ALTITUDE_MAX; + return Objects.nonNull(rthAltitude) && rthAltitude >= RTH_ALTITUDE_MIN && rthAltitude <= RTH_ALTITUDE_MAX; } + @Override + public boolean canPublish(OsdDockDrone osd) { + return rthAltitude != osd.getRthAltitude().intValue(); + } } 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 deleted file mode 100644 index 6cb4b75..0000000 --- a/src/main/java/com/dji/sample/manage/model/receiver/StorageReceiver.java +++ /dev/null @@ -1,16 +0,0 @@ -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 1efbe3e..797d351 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 @@ -1,7 +1,5 @@ package com.dji.sample.manage.model.receiver; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; import lombok.Data; /** @@ -10,7 +8,6 @@ import lombok.Data; * @date 2021/11/23 */ @Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class WirelessLinkStateReceiver { private Integer downloadQuality; 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 f47185c..df4d3ee 100644 --- a/src/main/java/com/dji/sample/manage/service/ICapacityCameraService.java +++ b/src/main/java/com/dji/sample/manage/service/ICapacityCameraService.java @@ -30,9 +30,8 @@ public interface ICapacityCameraService { * Save the live capability data of the device. * @param capacityCameraReceivers * @param deviceSn - * @param timestamp */ - void saveCapacityCameraReceiverList(List capacityCameraReceivers, String deviceSn, Long timestamp); + void saveCapacityCameraReceiverList(List capacityCameraReceivers, String deviceSn); /** * Convert the received camera capability object into camera data transfer object. diff --git a/src/main/java/com/dji/sample/manage/service/IDeviceFirmwareService.java b/src/main/java/com/dji/sample/manage/service/IDeviceFirmwareService.java index 72f50ab..aab0992 100644 --- a/src/main/java/com/dji/sample/manage/service/IDeviceFirmwareService.java +++ b/src/main/java/com/dji/sample/manage/service/IDeviceFirmwareService.java @@ -1,12 +1,12 @@ package com.dji.sample.manage.service; -import com.dji.sample.common.model.PaginationData; import com.dji.sample.manage.model.dto.DeviceFirmwareDTO; import com.dji.sample.manage.model.dto.DeviceFirmwareNoteDTO; import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO; import com.dji.sample.manage.model.param.DeviceFirmwareQueryParam; import com.dji.sample.manage.model.param.DeviceFirmwareUploadParam; -import com.dji.sample.manage.model.param.DeviceOtaCreateParam; +import com.dji.sdk.cloudapi.firmware.OtaCreateDevice; +import com.dji.sdk.common.PaginationData; import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -43,7 +43,7 @@ public interface IDeviceFirmwareService { * @param upgradeDTOS * @return */ - List getDeviceOtaFirmware(String workspaceId, List upgradeDTOS); + List getDeviceOtaFirmware(String workspaceId, List upgradeDTOS); /** * Query firmware version information by page. diff --git a/src/main/java/com/dji/sample/manage/service/IDeviceHmsService.java b/src/main/java/com/dji/sample/manage/service/IDeviceHmsService.java index f5376c1..afbd681 100644 --- a/src/main/java/com/dji/sample/manage/service/IDeviceHmsService.java +++ b/src/main/java/com/dji/sample/manage/service/IDeviceHmsService.java @@ -1,10 +1,8 @@ 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; +import com.dji.sdk.common.PaginationData; /** * @author sean @@ -13,13 +11,6 @@ import org.springframework.messaging.MessageHeaders; */ public interface IDeviceHmsService { - /** - * Handle hms messages. - * @param receiver - * @param headers - */ - void handleHms(CommonTopicReceiver receiver, MessageHeaders headers); - /** * Query hms data by paging according to query parameters. * @param param diff --git a/src/main/java/com/dji/sample/manage/service/IDeviceLogsService.java b/src/main/java/com/dji/sample/manage/service/IDeviceLogsService.java index aded702..b4f6800 100644 --- a/src/main/java/com/dji/sample/manage/service/IDeviceLogsService.java +++ b/src/main/java/com/dji/sample/manage/service/IDeviceLogsService.java @@ -1,13 +1,12 @@ package com.dji.sample.manage.service; -import com.dji.sample.common.model.PaginationData; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.manage.model.dto.DeviceLogsDTO; import com.dji.sample.manage.model.param.DeviceLogsCreateParam; import com.dji.sample.manage.model.param.DeviceLogsQueryParam; -import com.dji.sample.manage.model.param.LogsFileUpdateParam; -import org.springframework.messaging.MessageHeaders; +import com.dji.sdk.cloudapi.log.FileUploadUpdateRequest; +import com.dji.sdk.cloudapi.log.LogModuleEnum; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import java.net.URL; import java.util.List; @@ -33,7 +32,7 @@ public interface IDeviceLogsService { * @param domainList * @return */ - ResponseResult getRealTimeLogs(String deviceSn, List domainList); + HttpResultResponse getRealTimeLogs(String deviceSn, List domainList); /** * Add device logs. @@ -53,7 +52,7 @@ public interface IDeviceLogsService { * @param param * @return */ - ResponseResult pushFileUpload(String username, String deviceSn, DeviceLogsCreateParam param); + HttpResultResponse pushFileUpload(String username, String deviceSn, DeviceLogsCreateParam param); /** * Push a request to modify the status of the logs file. @@ -61,7 +60,7 @@ public interface IDeviceLogsService { * @param param * @return */ - ResponseResult pushUpdateFile(String deviceSn, LogsFileUpdateParam param); + HttpResultResponse pushUpdateFile(String deviceSn, FileUploadUpdateRequest param); /** * Delete log records. @@ -70,14 +69,6 @@ public interface IDeviceLogsService { */ void deleteLogs(String deviceSn, String logsId); - /** - * Handle logs file upload progress. - * @param receiver - * @param headers - * @return - */ - CommonTopicReceiver handleFileUploadProgress(CommonTopicReceiver receiver, MessageHeaders headers); - /** * Update status, which is updated when the logs upload succeeds or fails. * @param logsId 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 11fd20c..d6abfec 100644 --- a/src/main/java/com/dji/sample/manage/service/IDevicePayloadService.java +++ b/src/main/java/com/dji/sample/manage/service/IDevicePayloadService.java @@ -1,8 +1,9 @@ package com.dji.sample.manage.service; +import com.dji.sample.manage.model.dto.DeviceDTO; 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 com.dji.sample.manage.model.dto.DevicePayloadReceiver; +import com.dji.sdk.cloudapi.device.PayloadFirmwareVersion; import java.util.Collection; import java.util.List; @@ -26,7 +27,7 @@ public interface IDevicePayloadService { * @param payloadReceiverList * @return */ - Boolean savePayloadDTOs(List payloadReceiverList); + Boolean savePayloadDTOs(DeviceDTO drone, List payloadReceiverList); /** * Save a payload data. @@ -52,7 +53,7 @@ public interface IDevicePayloadService { * Update the firmware version information of the payload. * @param receiver */ - void updateFirmwareVersion(FirmwareVersionReceiver receiver); + Boolean updateFirmwareVersion(String droneSn, PayloadFirmwareVersion receiver); /** * Delete payload data based on payload sn. @@ -67,4 +68,6 @@ public interface IDevicePayloadService { * @return */ Boolean checkAuthorityPayload(String deviceSn, String payloadIndex); + + void updatePayloadControl(DeviceDTO drone, List payloads); } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/service/IDeviceRedisService.java b/src/main/java/com/dji/sample/manage/service/IDeviceRedisService.java index 074101a..cd1afc2 100644 --- a/src/main/java/com/dji/sample/manage/service/IDeviceRedisService.java +++ b/src/main/java/com/dji/sample/manage/service/IDeviceRedisService.java @@ -1,9 +1,8 @@ package com.dji.sample.manage.service; -import com.dji.sample.component.mqtt.model.EventsOutputProgressReceiver; import com.dji.sample.component.mqtt.model.EventsReceiver; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.receiver.FirmwareProgressExtReceiver; +import com.dji.sdk.cloudapi.firmware.OtaProgress; import java.util.Optional; import java.util.Set; @@ -42,6 +41,14 @@ public interface IDeviceRedisService { */ Boolean delDeviceOnline(String sn); + /** + * Save the device's osd real-time data. + * @param sn + * @param data + * @return + */ + void setDeviceOsd(String sn, Object data); + /** * Get the device's osd real-time data. * @param sn @@ -50,20 +57,26 @@ public interface IDeviceRedisService { * @return */ Optional getDeviceOsd(String sn, Class clazz); + /** + * Delete the device's osd real-time data. + * @param sn + * @return + */ + Boolean delDeviceOsd(String sn); /** * Save the firmware update progress of the device in redis. * @param sn * @param events */ - void setFirmwareUpgrading(String sn, EventsReceiver> events); + void setFirmwareUpgrading(String sn, EventsReceiver events); /** * Query the firmware update progress of the device in redis. * @param sn * @return */ - Optional>> getFirmwareUpgradingProgress(String sn); + Optional> getFirmwareUpgradingProgress(String sn); /** * Delete the firmware update progress of the device in redis. @@ -92,4 +105,8 @@ public interface IDeviceRedisService { * @return */ Boolean delHmsKeysBySn(String sn); + + void gatewayOffline(String gatewaySn); + + void subDeviceOffline(String deviceSn); } 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 7cf1a17..ec0452c 100644 --- a/src/main/java/com/dji/sample/manage/service/IDeviceService.java +++ b/src/main/java/com/dji/sample/manage/service/IDeviceService.java @@ -1,23 +1,20 @@ package com.dji.sample.manage.service; -import com.dji.sample.common.model.PaginationData; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.CommonTopicResponse; -import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; +import com.dji.sample.component.websocket.model.BizCodeEnum; import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO; import com.dji.sample.manage.model.dto.TopologyDeviceDTO; -import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; -import com.dji.sample.manage.model.enums.DeviceSetPropertyEnum; -import com.dji.sample.manage.model.enums.DockModeCodeEnum; import com.dji.sample.manage.model.param.DeviceQueryParam; -import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; +import com.dji.sdk.cloudapi.device.ControlSourceEnum; +import com.dji.sdk.cloudapi.device.DeviceOsdHost; +import com.dji.sdk.cloudapi.device.DockModeCodeEnum; +import com.dji.sdk.cloudapi.device.DroneModeCodeEnum; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.messaging.Message; -import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Optional; /** @@ -28,44 +25,34 @@ import java.util.Optional; public interface IDeviceService { /** - * The device goes online. - * @param deviceGateway gateway - * @return Whether the online is successful. - */ - Boolean deviceOnline(StatusGatewayReceiver deviceGateway); - - /** - * The device goes offline. - * @param gateway - * @return Whether the offline is successful. + * The aircraft goes offline. + * @param deviceSn aircraft's SN */ - Boolean deviceOffline(StatusGatewayReceiver gateway); + void subDeviceOffline(String deviceSn); /** - * The aircraft goes offline. - * @param deviceSn aircraft's SN - * @return Whether the offline is successful. + * The gateway goes offline. + * @param gatewaySn gateway's SN */ - Boolean subDeviceOffline(String deviceSn); + void gatewayOffline(String gatewaySn); /** - * When the device goes online, it needs to subscribe to topics. - * @param sn device's SN + * Subscribe to the topic of the gateway when the gateway device goes online, and unsubscribe from the topic of the sub-device. + * @param gateway */ - void subscribeTopicOnline(String sn); + void gatewayOnlineSubscribeTopic(GatewayManager gateway); /** - * When the device goes offine, it needs to cancel the subscribed topics. - * @param sn device's SN + * Subscribe to the gateway's and sub-device's topics when the drone comes online. + * @param gateway */ - void unsubscribeTopicOffline(String sn); + void subDeviceOnlineSubscribeTopic(GatewayManager gateway); /** - * Delete all device data according to the SN of the device. - * @param ids device's SN - * @return + * When the gateway device goes offline, unsubscribe from the topics of the gateway and sub-device. + * @param gateway */ - Boolean delDeviceByDeviceSns(List ids); + void offlineUnsubscribeTopic(GatewayManager gateway); /** * Obtain device data according to different query conditions. @@ -74,13 +61,6 @@ public interface IDeviceService { */ List getDevicesByParams(DeviceQueryParam param); - /** - * When you receive a status topic message, you need to reply to it. - * @param sn the target of sn - * @param response - */ - void publishStatusReply(String sn, CommonTopicResponse response); - /** * The business interface on the web side. Get all information about all devices in this workspace. * @param workspaceId @@ -94,13 +74,6 @@ public interface IDeviceService { */ void spliceDeviceTopo(DeviceDTO device); - /** - * Push the topology information to the pilot after one device is online. - * @param sessions The collection of connection objects on the pilot side. - * @param sn - */ - void pushDeviceOnlineTopo(Collection sessions, String sn, String gatewaySn); - /** * Query the information of the device according to the sn of the device. * @param sn device sn @@ -120,26 +93,20 @@ 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 sn + * @param deviceSn */ - void pushDeviceOfflineTopo(String workspaceId, String sn); + void pushDeviceOfflineTopo(String workspaceId, String deviceSn); /** * When the server receives the request of any device online, offline and topology update in the same workspace, * it also broadcasts a push of device online, offline and topology update to PILOT via websocket, * and PILOT will get the device topology list again after receiving the push. * @param workspaceId - * @param deviceSn * @param gatewaySn + * @param deviceSn */ void pushDeviceOnlineTopo(String workspaceId, String gatewaySn, String deviceSn); - /** - * Handle messages from the osd topic. - * @param message osd - */ - void handleOSD(Message message); - /** * Update the device information. * @param deviceDTO @@ -182,24 +149,16 @@ public interface IDeviceService { * @param upgradeDTOS * @return */ - ResponseResult createDeviceOtaJob(String workspaceId, List upgradeDTOS); + HttpResultResponse createDeviceOtaJob(String workspaceId, List upgradeDTOS); /** * Set the property parameters of the drone. * @param workspaceId * @param dockSn - * @param propertyEnum * @param param + * @return */ - void devicePropertySet(String workspaceId, String dockSn, DeviceSetPropertyEnum propertyEnum, JsonNode param); - - /** - * Set one property parameters of the drone. - * @param topic - * @param propertyEnum - * @param value - */ - void deviceOnePropertySet(String topic, DeviceSetPropertyEnum propertyEnum, Map.Entry value); + int devicePropertySet(String workspaceId, String dockSn, JsonNode param); /** * Check the working status of the dock. @@ -213,7 +172,7 @@ public interface IDeviceService { * @param deviceSn * @return */ - DeviceModeCodeEnum getDeviceMode(String deviceSn); + DroneModeCodeEnum getDeviceMode(String deviceSn); /** * Check if the dock is in drc mode. @@ -229,4 +188,13 @@ public interface IDeviceService { */ Boolean checkAuthorityFlight(String gatewaySn); + Integer saveDevice(DeviceDTO device); + + Boolean saveOrUpdateDevice(DeviceDTO device); + + void pushOsdDataToPilot(String workspaceId, String sn, DeviceOsdHost data); + + void pushOsdDataToWeb(String workspaceId, BizCodeEnum codeEnum, String sn, Object data); + + void updateFlightControl(DeviceDTO gateway, ControlSourceEnum controlSource); } \ 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 2fb7a4f..3bd82e4 100644 --- a/src/main/java/com/dji/sample/manage/service/ILiveStreamService.java +++ b/src/main/java/com/dji/sample/manage/service/ILiveStreamService.java @@ -1,9 +1,8 @@ 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.LiveCapacityReceiver; +import com.dji.sdk.common.HttpResultResponse; import java.util.List; @@ -21,38 +20,31 @@ public interface ILiveStreamService { */ List getLiveCapacity(String workspaceId); - /** - * Save live capability data. - * @param liveCapacityReceiver - * @param timestamp - */ - void saveLiveCapacity(LiveCapacityReceiver liveCapacityReceiver, Long timestamp); - /** * Initiate a live streaming by publishing mqtt message. * @param liveParam Parameters needed for on-demand. * @return */ - ResponseResult liveStart(LiveTypeDTO liveParam); + HttpResultResponse liveStart(LiveTypeDTO liveParam); /** * Stop the live streaming by publishing mqtt message. * @param videoId * @return */ - ResponseResult liveStop(String videoId); + HttpResultResponse liveStop(String videoId); /** * Readjust the clarity of the live streaming by publishing mqtt messages. * @param liveParam * @return */ - ResponseResult liveSetQuality(LiveTypeDTO liveParam); + HttpResultResponse liveSetQuality(LiveTypeDTO liveParam); /** * Switches the lens of the device during the live streaming. * @param liveParam * @return */ - ResponseResult liveLensChange(LiveTypeDTO liveParam); + HttpResultResponse liveLensChange(LiveTypeDTO liveParam); } diff --git a/src/main/java/com/dji/sample/manage/service/ILogsFileIndexService.java b/src/main/java/com/dji/sample/manage/service/ILogsFileIndexService.java index d350a06..9d7da29 100644 --- a/src/main/java/com/dji/sample/manage/service/ILogsFileIndexService.java +++ b/src/main/java/com/dji/sample/manage/service/ILogsFileIndexService.java @@ -1,8 +1,8 @@ package com.dji.sample.manage.service; import com.dji.sample.manage.model.dto.LogsFileDTO; -import com.dji.sample.manage.model.receiver.LogsFile; -import com.dji.sample.manage.model.receiver.LogsFileUpload; +import com.dji.sample.manage.model.dto.LogsFileUploadDTO; +import com.dji.sdk.cloudapi.log.LogFileIndex; import java.util.List; import java.util.Optional; @@ -22,21 +22,21 @@ public interface ILogsFileIndexService { * @param fileId * @return */ - Boolean insertFileIndex(LogsFile file, String deviceSn, Integer domain, String fileId); + Boolean insertFileIndex(LogFileIndex file, String deviceSn, Integer domain, String fileId); /** * Query logs file upload information based on the file id. * @param fileId * @return */ - Optional getFileIndexByFileId(String fileId); + Optional getFileIndexByFileId(String fileId); /** * Batch query logs file upload information. * @param fileIds * @return */ - List getFileIndexByFileIds(List fileIds); + List getFileIndexByFileIds(List fileIds); /** * Delete log index data based on file id. diff --git a/src/main/java/com/dji/sample/manage/service/ILogsFileService.java b/src/main/java/com/dji/sample/manage/service/ILogsFileService.java index 24db5db..0384ac1 100644 --- a/src/main/java/com/dji/sample/manage/service/ILogsFileService.java +++ b/src/main/java/com/dji/sample/manage/service/ILogsFileService.java @@ -1,8 +1,9 @@ package com.dji.sample.manage.service; import com.dji.sample.manage.model.dto.LogsFileDTO; -import com.dji.sample.manage.model.receiver.LogsExtFileReceiver; -import com.dji.sample.manage.model.receiver.LogsFileUpload; +import com.dji.sample.manage.model.dto.LogsFileUploadDTO; +import com.dji.sdk.cloudapi.log.FileUploadProgressFile; +import com.dji.sdk.cloudapi.log.FileUploadStartFile; import java.net.URL; import java.util.List; @@ -26,7 +27,7 @@ public interface ILogsFileService { * @param logsId * @return */ - List getLogsFileByLogsId(String logsId); + List getLogsFileByLogsId(String logsId); /** * Added logs file. @@ -34,7 +35,7 @@ public interface ILogsFileService { * @param logsId * @return */ - Boolean insertFile(LogsFileUpload file, String logsId); + Boolean insertFile(FileUploadStartFile file, String logsId); /** * Delete logs files. @@ -47,7 +48,7 @@ public interface ILogsFileService { * @param logsId * @param fileReceiver */ - void updateFile(String logsId, LogsExtFileReceiver fileReceiver); + void updateFile(String logsId, FileUploadProgressFile fileReceiver); /** * Update file upload status. diff --git a/src/main/java/com/dji/sample/manage/service/ITSAService.java b/src/main/java/com/dji/sample/manage/service/ITSAService.java deleted file mode 100644 index e11a903..0000000 --- a/src/main/java/com/dji/sample/manage/service/ITSAService.java +++ /dev/null @@ -1,35 +0,0 @@ -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 - * @date 2022/2/21 - */ -public interface ITSAService { - - /** - * Real-time push telemetry data. - * @param workspaceId - * @param osdData - * @param sn - */ - void pushTelemetryData(String workspaceId, Object osdData, String sn); - - /** - * 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/ITopologyService.java b/src/main/java/com/dji/sample/manage/service/ITopologyService.java index 457e823..012d9c6 100644 --- a/src/main/java/com/dji/sample/manage/service/ITopologyService.java +++ b/src/main/java/com/dji/sample/manage/service/ITopologyService.java @@ -1,6 +1,6 @@ package com.dji.sample.manage.service; -import com.dji.sample.manage.model.dto.TopologyDTO; +import com.dji.sdk.cloudapi.tsa.TopologyList; import java.util.List; import java.util.Optional; @@ -17,12 +17,12 @@ public interface ITopologyService { * @param workspaceId * @return */ - List getDeviceTopology(String workspaceId); + List getDeviceTopology(String workspaceId); /** * Query the topology according to the gateway sn. * @param gatewaySn * @return */ - Optional getDeviceTopologyByGatewaySn(String gatewaySn); + Optional getDeviceTopologyByGatewaySn(String gatewaySn); } 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 133c4c7..7100565 100644 --- a/src/main/java/com/dji/sample/manage/service/IUserService.java +++ b/src/main/java/com/dji/sample/manage/service/IUserService.java @@ -1,9 +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 com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import java.util.Optional; @@ -15,7 +15,7 @@ public interface IUserService { * @param workspaceId * @return */ - ResponseResult getUserByUsername(String username, String workspaceId); + HttpResultResponse getUserByUsername(String username, String workspaceId); /** * Verify the username and password to log in. @@ -24,7 +24,7 @@ public interface IUserService { * @param flag * @return */ - ResponseResult userLogin(String username, String password, Integer flag); + HttpResultResponse userLogin(String username, String password, Integer flag); /** * Create a user object containing a new token. 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 306760d..eb3bcf5 100644 --- a/src/main/java/com/dji/sample/manage/service/IWorkspaceService.java +++ b/src/main/java/com/dji/sample/manage/service/IWorkspaceService.java @@ -1,9 +1,7 @@ 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; @@ -23,9 +21,4 @@ public interface IWorkspaceService { */ Optional getWorkspaceNameByBindCode(String bindCode); - /** - * Handle the request for obtaining the organization information corresponding to the device binding. - * @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 deleted file mode 100644 index 91e390a..0000000 --- a/src/main/java/com/dji/sample/manage/service/impl/AbstractTSAService.java +++ /dev/null @@ -1,63 +0,0 @@ -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.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.databind.ObjectMapper; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.Collection; - -/** - * @author sean - * @version 0.3 - * @date 2022/2/21 - */ -public abstract class AbstractTSAService implements ITSAService { - - protected AbstractTSAService tsaService; - - @Autowired - protected ObjectMapper mapper; - - @Autowired - private IWebSocketManageService webSocketManageService; - - public AbstractTSAService(AbstractTSAService tsaService) { - this.tsaService = tsaService; - } - - @Autowired - protected ISendMessageService sendMessageService; - - @Override - public void pushTelemetryData(String workspaceId, Object osdData, String sn) { - // All connected accounts on the pilot side of this workspace. - Collection pilotSessions = webSocketManageService - .getValueWithWorkspaceAndUserType(workspaceId, UserTypeEnum.PILOT.getVal()); - - TelemetryDTO telemetry = TelemetryDTO.builder() - .sn(sn) - .build(); - CustomWebSocketMessage pilotMessage = CustomWebSocketMessage.builder() - .timestamp(System.currentTimeMillis()) - .bizCode(BizCodeEnum.DEVICE_OSD.getCode()) - .data(telemetry) - .build(); - - this.pushTelemetryData(pilotSessions, pilotMessage, osdData); - } - - public abstract void pushTelemetryData(Collection sessions, - CustomWebSocketMessage message, Object Object); - - 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 bfe6de9..23a061a 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 @@ -3,9 +3,11 @@ package com.dji.sample.manage.service.impl; import com.dji.sample.manage.model.dto.CapacityVideoDTO; import com.dji.sample.manage.model.receiver.CapacityVideoReceiver; import com.dji.sample.manage.service.ICameraVideoService; +import com.dji.sdk.cloudapi.livestream.VideoTypeEnum; import org.springframework.stereotype.Service; import java.util.UUID; +import java.util.stream.Collectors; /** * @author sean.zhou @@ -13,18 +15,20 @@ import java.util.UUID; * @date 2021/11/19 */ @Service -//@Transactional public class CameraVideoServiceImpl implements ICameraVideoService { @Override public CapacityVideoDTO receiver2Dto(CapacityVideoReceiver receiver) { - CapacityVideoDTO.CapacityVideoDTOBuilder builder = CapacityVideoDTO.builder(); - - if (receiver != null) { - builder.id(UUID.randomUUID().toString()) - .index(receiver.getVideoIndex()) - .type(receiver.getVideoType()) - .switchVideoTypes(receiver.getSwitchableVideoTypes()); + if (null == receiver) { + return null; + } + CapacityVideoDTO.CapacityVideoDTOBuilder builder = CapacityVideoDTO.builder() + .id(UUID.randomUUID().toString()) + .index(receiver.getVideoIndex()) + .type(receiver.getVideoType().getType()); + if (null != receiver.getSwitchableVideoTypes()) { + builder.switchVideoTypes(receiver.getSwitchableVideoTypes().stream() + .map(VideoTypeEnum::getType).collect(Collectors.toList())); } 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 ec8ac77..3f0b2da 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 @@ -4,16 +4,17 @@ import com.dji.sample.component.redis.RedisConst; import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.manage.model.dto.CapacityCameraDTO; import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.receiver.CapacityCameraReceiver; import com.dji.sample.manage.service.ICameraVideoService; import com.dji.sample.manage.service.ICapacityCameraService; import com.dji.sample.manage.service.IDeviceDictionaryService; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.device.PayloadIndex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -24,7 +25,6 @@ import java.util.stream.Collectors; * @version 0.1 */ @Service -//@Transactional public class CapacityCameraServiceImpl implements ICapacityCameraService { @Autowired @@ -44,37 +44,32 @@ public class CapacityCameraServiceImpl implements ICapacityCameraService { } @Override - public void saveCapacityCameraReceiverList(List capacityCameraReceivers, String deviceSn, Long timestamp) { + public void saveCapacityCameraReceiverList(List capacityCameraReceivers, String deviceSn) { List capacity = capacityCameraReceivers.stream() .map(this::receiver2Dto).collect(Collectors.toList()); RedisOpsUtils.hashSet(RedisConst.LIVE_CAPACITY, deviceSn, capacity); } - @Override public CapacityCameraDTO receiver2Dto(CapacityCameraReceiver receiver) { CapacityCameraDTO.CapacityCameraDTOBuilder builder = CapacityCameraDTO.builder(); if (receiver == null) { return builder.build(); } - int[] indexArr = Arrays.stream(receiver.getCameraIndex().split("-")) - .map(Integer::valueOf) - .mapToInt(Integer::intValue) - .toArray(); + PayloadIndex cameraIndex = receiver.getCameraIndex(); // The cameraIndex consists of type and subType and the index of the payload hanging on the drone. // type-subType-index - if (indexArr.length == 3) { - Optional dictionaryOpt = dictionaryService - .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), indexArr[0], indexArr[1]); - dictionaryOpt.ifPresent(dictionary -> - builder.name(dictionary.getDeviceName())); - } + Optional dictionaryOpt = dictionaryService.getOneDictionaryInfoByTypeSubType( + DeviceDomainEnum.PAYLOAD.getDomain(), cameraIndex.getType().getType(), cameraIndex.getSubType().getSubType()); + dictionaryOpt.ifPresent(dictionary -> builder.name(dictionary.getDeviceName())); + return builder .id(UUID.randomUUID().toString()) - .videosList(receiver.getVideosList() + .videosList(receiver.getVideoList() .stream() .map(cameraVideoService::receiver2Dto) + .filter(Objects::nonNull) .collect(Collectors.toList())) - .index(receiver.getCameraIndex()) + .index(receiver.getCameraIndex().toString()) .build(); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java index 1747040..edd7114 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java @@ -4,34 +4,36 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; 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.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.impl.MessageSenderServiceImpl; +import com.dji.sample.component.mqtt.model.EventsReceiver; import com.dji.sample.component.oss.model.OssConfiguration; import com.dji.sample.component.oss.service.impl.OssServiceContext; 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.service.IWebSocketManageService; -import com.dji.sample.component.websocket.service.impl.SendMessageServiceImpl; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.manage.dao.IDeviceFirmwareMapper; import com.dji.sample.manage.model.dto.*; import com.dji.sample.manage.model.entity.DeviceFirmwareEntity; import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.model.param.DeviceFirmwareQueryParam; import com.dji.sample.manage.model.param.DeviceFirmwareUploadParam; -import com.dji.sample.manage.model.param.DeviceOtaCreateParam; -import com.dji.sample.manage.model.receiver.FirmwareProgressExtReceiver; import com.dji.sample.manage.service.IDeviceFirmwareService; import com.dji.sample.manage.service.IDeviceRedisService; import com.dji.sample.manage.service.IFirmwareModelService; -import com.fasterxml.jackson.core.type.TypeReference; +import com.dji.sdk.cloudapi.firmware.FirmwareUpgradeTypeEnum; +import com.dji.sdk.cloudapi.firmware.OtaCreateDevice; +import com.dji.sdk.cloudapi.firmware.OtaProgress; +import com.dji.sdk.cloudapi.firmware.OtaProgressStatusEnum; +import com.dji.sdk.cloudapi.firmware.api.AbstractFirmwareService; +import com.dji.sdk.common.Pagination; +import com.dji.sdk.common.PaginationData; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; @@ -58,22 +60,16 @@ import java.util.zip.ZipInputStream; */ @Service @Slf4j -public class DeviceFirmwareServiceImpl implements IDeviceFirmwareService { +public class DeviceFirmwareServiceImpl extends AbstractFirmwareService implements IDeviceFirmwareService { @Autowired private IDeviceFirmwareMapper mapper; - @Autowired - private MessageSenderServiceImpl messageSenderService; - @Autowired private ObjectMapper objectMapper; @Autowired - private SendMessageServiceImpl webSocketMessageService; - - @Autowired - private IWebSocketManageService webSocketManageService; + private IWebSocketMessageService webSocketMessageService; @Autowired private OssServiceContext ossServiceContext; @@ -104,8 +100,8 @@ public class DeviceFirmwareServiceImpl implements IDeviceFirmwareService { } @Override - public List getDeviceOtaFirmware(String workspaceId, List upgradeDTOS) { - List deviceOtaList = new ArrayList<>(); + public List getDeviceOtaFirmware(String workspaceId, List upgradeDTOS) { + List deviceOtaList = new ArrayList<>(); upgradeDTOS.forEach(upgradeDevice -> { boolean exist = deviceRedisService.checkDeviceOnline(upgradeDevice.getSn()); if (!exist) { @@ -116,28 +112,29 @@ public class DeviceFirmwareServiceImpl implements IDeviceFirmwareService { if (firmwareOpt.isEmpty()) { throw new IllegalArgumentException("This firmware version does not exist or is not available."); } - DeviceOtaCreateParam ota = dto2OtaCreateDto(firmwareOpt.get()); + OtaCreateDevice ota = dto2OtaCreateDto(firmwareOpt.get()); ota.setSn(upgradeDevice.getSn()); - ota.setFirmwareUpgradeType(upgradeDevice.getFirmwareUpgradeType()); + ota.setFirmwareUpgradeType(FirmwareUpgradeTypeEnum.find(upgradeDevice.getFirmwareUpgradeType())); deviceOtaList.add(ota); }); return deviceOtaList; } - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_OTA_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleOtaProgress(CommonTopicReceiver receiver, MessageHeaders headers) { - String sn = receiver.getGateway(); + @Override + public TopicEventsResponse otaProgress(TopicEventsRequest> request, MessageHeaders headers) { + String sn = request.getGateway(); + + EventsReceiver eventsReceiver = new EventsReceiver() + .setBid(request.getBid()) + .setOutput(request.getData().getOutput()) + .setResult(request.getData().getResult()); - EventsReceiver> eventsReceiver = objectMapper.convertValue(receiver.getData(), - new TypeReference>>(){}); - eventsReceiver.setBid(receiver.getBid()); - EventsOutputProgressReceiver output = eventsReceiver.getOutput(); log.info("SN: {}, {} ===> Upgrading progress: {}", - sn, receiver.getMethod(), output.getProgress().toString()); + sn, request.getMethod(), eventsReceiver.getOutput().getProgress()); - if (eventsReceiver.getResult() != ResponseResult.CODE_SUCCESS) { - log.error("SN: {}, {} ===> Error code: {}", sn, receiver.getMethod(), eventsReceiver.getResult()); + if (!eventsReceiver.getResult().isSuccess()) { + log.error("SN: {}, {} ===> Error: {}", sn, request.getMethod(), eventsReceiver.getResult()); } Optional deviceOpt = deviceRedisService.getDeviceOnline(sn); @@ -145,16 +142,15 @@ public class DeviceFirmwareServiceImpl implements IDeviceFirmwareService { return null; } - EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus()); + OtaProgressStatusEnum statusEnum = eventsReceiver.getOutput().getStatus(); DeviceDTO device = deviceOpt.get(); - handleProgress(device.getWorkspaceId(), sn, eventsReceiver, statusEnum.getEnd()); - handleProgress(device.getWorkspaceId(), device.getChildDeviceSn(), eventsReceiver, statusEnum.getEnd()); + handleProgress(device.getWorkspaceId(), sn, eventsReceiver, statusEnum.isEnd()); + handleProgress(device.getWorkspaceId(), device.getChildDeviceSn(), eventsReceiver, statusEnum.isEnd()); - return receiver; + return new TopicEventsResponse().setData(MqttReply.success()); } - private void handleProgress(String workspaceId, String sn, - EventsReceiver> events, boolean isEnd) { + private void handleProgress(String workspaceId, String sn, EventsReceiver events, boolean isEnd) { boolean upgrade = deviceRedisService.getFirmwareUpgradingProgress(sn).isPresent(); if (!upgrade) { return; @@ -343,16 +339,15 @@ public class DeviceFirmwareServiceImpl implements IDeviceFirmwareService { .build(); } - private DeviceOtaCreateParam dto2OtaCreateDto(DeviceFirmwareDTO dto) { + private OtaCreateDevice dto2OtaCreateDto(DeviceFirmwareDTO dto) { if (dto == null) { return null; } - return DeviceOtaCreateParam.builder() - .fileSize(dto.getFileSize()) - .fileUrl(ossServiceContext.getObjectUrl(OssConfiguration.bucket, dto.getObjectKey()).toString()) - .fileName(dto.getFileName()) - .md5(dto.getFileMd5()) - .productVersion(dto.getProductVersion()) - .build(); + return new OtaCreateDevice() + .setFileSize(dto.getFileSize()) + .setFileUrl(ossServiceContext.getObjectUrl(OssConfiguration.bucket, dto.getObjectKey()).toString()) + .setFileName(dto.getFileName()) + .setMd5(dto.getFileMd5()) + .setProductVersion(dto.getProductVersion()); } } 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 index e201155..3348c64 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/DeviceHmsServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceHmsServiceImpl.java @@ -3,15 +3,8 @@ 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.websocket.model.BizCodeEnum; -import com.dji.sample.component.websocket.service.impl.SendMessageServiceImpl; -import com.dji.sample.component.websocket.service.impl.WebSocketManageServiceImpl; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.manage.dao.IDeviceHmsMapper; import com.dji.sample.manage.model.common.HmsJsonUtil; import com.dji.sample.manage.model.common.HmsMessage; @@ -19,20 +12,19 @@ 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.DeviceDomainEnum; -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.dji.sample.manage.service.IDeviceRedisService; -import com.fasterxml.jackson.core.type.TypeReference; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.hms.*; +import com.dji.sdk.cloudapi.hms.api.AbstractHmsService; +import com.dji.sdk.common.Pagination; +import com.dji.sdk.common.PaginationData; +import com.dji.sdk.mqtt.events.TopicEventsRequest; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -54,7 +46,7 @@ import java.util.stream.Collectors; @Service @Transactional @Slf4j -public class DeviceHmsServiceImpl implements IDeviceHmsService { +public class DeviceHmsServiceImpl extends AbstractHmsService implements IDeviceHmsService { @Autowired private IDeviceHmsMapper mapper; @@ -63,33 +55,25 @@ public class DeviceHmsServiceImpl implements IDeviceHmsService { private ObjectMapper objectMapper; @Autowired - private SendMessageServiceImpl sendMessageService; - - @Autowired - private WebSocketManageServiceImpl webSocketManageService; + private IWebSocketMessageService sendMessageService; @Autowired private IDeviceRedisService deviceRedisService; private static final Pattern PATTERN_KEY = Pattern.compile( - HmsEnum.FormatKeyEnum.KEY_START + "(" + - Arrays.stream(HmsEnum.FormatKeyEnum.values()) - .map(HmsEnum.FormatKeyEnum::getKey) + Arrays.stream(HmsFormatKeyEnum.values()) + .map(HmsFormatKeyEnum::getKey) .collect(Collectors.joining("|")) + ")"); @Override - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HMS) - public void handleHms(CommonTopicReceiver receiver, MessageHeaders headers) { - String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); - String sn = topic.substring((TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT).length(), - topic.indexOf(TopicConst.EVENTS_SUF)); - + public void hms(TopicEventsRequest response, MessageHeaders headers) { + String sn = response.getFrom(); DeviceHmsEntity entity = DeviceHmsEntity.builder() - .bid(receiver.getBid()) - .tid(receiver.getTid()) - .createTime(receiver.getTimestamp()) + .bid(response.getBid()) + .tid(response.getTid()) + .createTime(response.getTimestamp()) .updateTime(0L) .sn(sn) .build(); @@ -97,8 +81,7 @@ public class DeviceHmsServiceImpl implements IDeviceHmsService { Set hmsMap = deviceRedisService.getAllHmsKeys(sn); List unReadList = new ArrayList<>(); - objectMapper.convertValue(((Map) (receiver.getData())).get(MapKeyConst.LIST), - new TypeReference>() {}) + response.getData().getList() .forEach(hmsReceiver -> { final DeviceHmsEntity hms = entity.clone(); this.fillEntity(hms, hmsReceiver); @@ -133,10 +116,10 @@ public class DeviceHmsServiceImpl implements IDeviceHmsService { .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()), + HmsMessageLanguageEnum.ZH.getLanguage().equals(param.getLanguage()), DeviceHmsEntity::getMessageZh, param.getMessage()) .like(StringUtils.hasText(param.getMessage()) && - HmsEnum.MessageLanguage.EN.getLanguage().equals(param.getLanguage()), + HmsMessageLanguageEnum.EN.getLanguage().equals(param.getLanguage()), DeviceHmsEntity::getMessageEn, param.getMessage()) .orderByDesc(DeviceHmsEntity::getCreateTime); if (param.getPage() == null || param.getPageSize() == null) { @@ -186,23 +169,19 @@ public class DeviceHmsServiceImpl implements IDeviceHmsService { * @param dto * @param receiver */ - private void fillEntity(DeviceHmsEntity dto, DeviceHmsReceiver receiver) { - dto.setLevel(receiver.getLevel()); - dto.setModule(receiver.getModule()); + private void fillEntity(DeviceHmsEntity dto, DeviceHms receiver) { + dto.setLevel(receiver.getLevel().getLevel()); + dto.setModule(receiver.getModule().getModule()); dto.setHmsId(UUID.randomUUID().toString()); - Optional domainEnumOpt = Optional.ofNullable(receiver.getDeviceType()) - .map(type -> type.split("-")).map(type -> type[0]).map(Integer::parseInt).map(DeviceDomainEnum::find); - if (domainEnumOpt.isEmpty()) { - throw new RuntimeException("The device type does not match, please check the data."); - } - if (DeviceDomainEnum.DOCK == domainEnumOpt.get()) { - dto.setHmsKey(HmsEnum.HmsFaqIdEnum.DOCK_TIP.getText() + receiver.getCode()); + DeviceDomainEnum domain = receiver.getDeviceType().getDomain(); + if (DeviceDomainEnum.DOCK == domain) { + dto.setHmsKey(HmsFaqIdEnum.DOCK_TIP.getText() + receiver.getCode()); return; } - StringBuilder key = new StringBuilder(HmsEnum.HmsFaqIdEnum.FPV_TIP.getText()).append(receiver.getCode()); + StringBuilder key = new StringBuilder(HmsFaqIdEnum.FPV_TIP.getText()).append(receiver.getCode()); - if (receiver.getInTheSky() == HmsEnum.IN_THE_SKY.getVal()) { - key.append(HmsEnum.IN_THE_SKY.getText()); + if (receiver.getInTheSky()) { + key.append(HmsInTheSkyEnum.IN_THE_SKY.getText()); } dto.setHmsKey(key.toString()); } @@ -213,7 +192,7 @@ public class DeviceHmsServiceImpl implements IDeviceHmsService { * @param dto * @param args */ - private void fillMessage(DeviceHmsEntity dto, HmsArgsReceiver args) { + private void fillMessage(DeviceHmsEntity dto, DeviceHmsArgs args) { HmsMessage hmsMessage = HmsJsonUtil.get(dto.getHmsKey()); String zh = StringUtils.hasText(hmsMessage.getZh()) ? hmsMessage.getZh() : String.format("未知错误(%s)", dto.getHmsKey()); String en = StringUtils.hasText(hmsMessage.getEn()) ? hmsMessage.getEn() : String.format("Unknown(%s)", dto.getHmsKey());// @@ -228,27 +207,37 @@ public class DeviceHmsServiceImpl implements IDeviceHmsService { * @param hmsArgs * @return */ - private List fillKeyArgs(String l, HmsArgsReceiver hmsArgs) { - List args = new ArrayList<>(); - args.add(Objects.nonNull(hmsArgs.getAlarmId()) ? Long.toHexString(hmsArgs.getAlarmId()) : null); - args.add(Objects.nonNull(hmsArgs.getComponentIndex()) ? String.valueOf(hmsArgs.getComponentIndex() + 1) : null); + private Map fillKeyArgs(String l, DeviceHmsArgs hmsArgs) { + Map args = new HashMap<>(); + args.put(HmsFormatKeyEnum.ALARM_ID.getKey(), Objects.nonNull(hmsArgs.getAlarmId()) ? Long.toHexString(hmsArgs.getAlarmId()) : null); + args.put(HmsFormatKeyEnum.COMPONENT_INDEX.getKey(), + Objects.nonNull(hmsArgs.getComponentIndex()) ? String.valueOf(hmsArgs.getComponentIndex() + 1) : null); if (Objects.nonNull(hmsArgs.getSensorIndex())) { - args.add(String.valueOf(hmsArgs.getSensorIndex() + 1)); + args.put(HmsFormatKeyEnum.INDEX.getKey(), String.valueOf(hmsArgs.getSensorIndex() + 1)); - HmsEnum.HmsBatteryIndexEnum hmsBatteryIndexEnum = HmsEnum.HmsBatteryIndexEnum.find(hmsArgs.getSensorIndex()); - HmsEnum.HmsDockCoverIndexEnum hmsDockCoverIndexEnum = HmsEnum.HmsDockCoverIndexEnum.find(hmsArgs.getSensorIndex()); - HmsEnum.HmsChargingRodIndexEnum hmsChargingRodIndexEnum = HmsEnum.HmsChargingRodIndexEnum.find(hmsArgs.getSensorIndex()); + HmsBatteryIndexEnum hmsBatteryIndexEnum = Optional.ofNullable(hmsArgs.getSensorIndex()) + .filter(arg -> arg <= 1).map(HmsBatteryIndexEnum::find).orElse(null); + HmsDockCoverIndexEnum hmsDockCoverIndexEnum = Optional.ofNullable(hmsArgs.getSensorIndex()) + .filter(arg -> arg <= 1).map(HmsDockCoverIndexEnum::find).orElse(null); + HmsChargingRodIndexEnum hmsChargingRodIndexEnum = Optional.ofNullable(hmsArgs.getSensorIndex()) + .filter(arg -> arg <= 3).map(HmsChargingRodIndexEnum::find).orElse(null); switch (l) { case "zh": - args.add(hmsBatteryIndexEnum.getZh()); - args.add(hmsDockCoverIndexEnum.getZh()); - args.add(hmsChargingRodIndexEnum.getZh()); + args.put(HmsFormatKeyEnum.BATTERY_INDEX.getKey(), Optional.ofNullable(hmsBatteryIndexEnum) + .map(HmsBatteryIndexEnum::getZh).orElse(null)); + args.put(HmsFormatKeyEnum.DOCK_COVER_INDEX.getKey(), Optional.ofNullable(hmsDockCoverIndexEnum) + .map(HmsDockCoverIndexEnum::getZh).orElse(null)); + args.put(HmsFormatKeyEnum.CHARGING_ROD_INDEX.getKey(), Optional.ofNullable(hmsChargingRodIndexEnum) + .map(HmsChargingRodIndexEnum::getZh).orElse(null)); break; case "en": - args.add(hmsBatteryIndexEnum.getEn()); - args.add(hmsDockCoverIndexEnum.getEn()); - args.add(hmsChargingRodIndexEnum.getEn()); + args.put(HmsFormatKeyEnum.BATTERY_INDEX.getKey(), Optional.ofNullable(hmsBatteryIndexEnum) + .map(HmsBatteryIndexEnum::getEn).orElse(null)); + args.put(HmsFormatKeyEnum.DOCK_COVER_INDEX.getKey(), Optional.ofNullable(hmsDockCoverIndexEnum) + .map(HmsDockCoverIndexEnum::getEn).orElse(null)); + args.put(HmsFormatKeyEnum.CHARGING_ROD_INDEX.getKey(), Optional.ofNullable(hmsChargingRodIndexEnum) + .map(HmsChargingRodIndexEnum::getEn).orElse(null)); break; default: break; @@ -265,17 +254,15 @@ public class DeviceHmsServiceImpl implements IDeviceHmsService { * @param hmsArgs * @return */ - private String format(String l, String format, HmsArgsReceiver hmsArgs) { - List args = fillKeyArgs(l, hmsArgs); + private String format(String l, String format, DeviceHmsArgs hmsArgs) { + Map args = fillKeyArgs(l, hmsArgs); List list = parse(format); StringBuilder sb = new StringBuilder(); for (String word : list) { if (!StringUtils.hasText(word)) { continue; } - HmsEnum.FormatKeyEnum keyEnum = HmsEnum.FormatKeyEnum.find(word.substring(1)); - sb.append(HmsEnum.FormatKeyEnum.KEY_START != word.charAt(0) || HmsEnum.FormatKeyEnum.UNKNOWN == keyEnum ? - word : args.get(keyEnum.getIndex())); + sb.append(args.getOrDefault(word, word)); } return sb.toString(); } diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java index a5aca9a..799de27 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java @@ -3,35 +3,39 @@ 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.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.IMessageSenderService; +import com.dji.sample.component.mqtt.model.EventsReceiver; 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.service.ISendMessageService; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.manage.dao.IDeviceLogsMapper; import com.dji.sample.manage.model.dto.*; import com.dji.sample.manage.model.entity.DeviceLogsEntity; -import com.dji.sample.manage.model.enums.*; +import com.dji.sample.manage.model.enums.DeviceLogsStatusEnum; +import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.model.param.DeviceLogsCreateParam; import com.dji.sample.manage.model.param.DeviceLogsQueryParam; -import com.dji.sample.manage.model.param.LogsFileUpdateParam; -import com.dji.sample.manage.model.receiver.*; import com.dji.sample.manage.service.IDeviceLogsService; import com.dji.sample.manage.service.IDeviceRedisService; import com.dji.sample.manage.service.ILogsFileService; import com.dji.sample.manage.service.ITopologyService; -import com.dji.sample.media.model.StsCredentialsDTO; import com.dji.sample.storage.service.IStorageService; -import com.fasterxml.jackson.core.type.TypeReference; +import com.dji.sdk.cloudapi.log.*; +import com.dji.sdk.cloudapi.log.api.AbstractLogService; +import com.dji.sdk.cloudapi.storage.StsCredentialsResponse; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.Pagination; +import com.dji.sdk.common.PaginationData; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -53,7 +57,7 @@ import java.util.stream.Collectors; @Service @Transactional @Slf4j -public class DeviceLogsServiceImpl implements IDeviceLogsService { +public class DeviceLogsServiceImpl extends AbstractLogService implements IDeviceLogsService { private static final String LOGS_FILE_SUFFIX = ".tar"; @@ -63,9 +67,6 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService { @Autowired private ITopologyService topologyService; - @Autowired - private IMessageSenderService messageSenderService; - @Autowired private ILogsFileService logsFileService; @@ -76,11 +77,14 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService { private ObjectMapper objectMapper; @Autowired - private ISendMessageService webSocketMessageService; + private IWebSocketMessageService webSocketMessageService; @Autowired private IDeviceRedisService deviceRedisService; + @Autowired + private AbstractLogService abstractLogService; + @Override public PaginationData getUploadedLogs(String deviceSn, DeviceLogsQueryParam param) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() @@ -100,22 +104,20 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService { } @Override - public ResponseResult getRealTimeLogs(String deviceSn, List domainList) { + public HttpResultResponse getRealTimeLogs(String deviceSn, List domainList) { boolean exist = deviceRedisService.checkDeviceOnline(deviceSn); if (!exist) { - return ResponseResult.error("Device is offline."); + return HttpResultResponse.error("Device is offline."); } - ServiceReply> data = messageSenderService.publishServicesTopic( - new TypeReference>() {}, deviceSn, LogsFileMethodEnum.FILE_UPLOAD_LIST.getMethod(), - Map.of(MapKeyConst.MODULE_LIST, domainList)); - - for (LogsFileUpload file : data.getOutput()) { + TopicServicesResponse> response = abstractLogService + .fileuploadList(SDKManager.getDeviceSDK(deviceSn), new FileUploadListRequest().setModuleList(domainList)); + for (FileUploadListFile file : response.getData().getOutput().getFiles()) { if (file.getDeviceSn().isBlank()) { file.setDeviceSn(deviceSn); } } - return ResponseResult.success(new LogsFileUploadList(data.getOutput(), data.getResult())); + return HttpResultResponse.success(response.getData().getOutput()); } @Override @@ -132,7 +134,7 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService { if (!insert) { return ""; } - for (LogsFileUpload file : param.getFiles()) { + for (FileUploadStartFile file : param.getFiles()) { insert = logsFileService.insertFile(file, entity.getLogsId()); if (!insert) { return ""; @@ -144,47 +146,46 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService { @Override - public ResponseResult pushFileUpload(String username, String deviceSn, DeviceLogsCreateParam param) { - StsCredentialsDTO stsCredentials = storageService.getSTSCredentials(); + public HttpResultResponse pushFileUpload(String username, String deviceSn, DeviceLogsCreateParam param) { + StsCredentialsResponse stsCredentials = storageService.getSTSCredentials(); + stsCredentials.getCredentials().setExpire(System.currentTimeMillis() + (stsCredentials.getCredentials().getExpire() - 60) * 1000); LogsUploadCredentialsDTO credentialsDTO = new LogsUploadCredentialsDTO(stsCredentials); // Set the storage name of the file. - List files = param.getFiles(); + List files = param.getFiles(); files.forEach(file -> file.setObjectKey(credentialsDTO.getObjectKeyPrefix() + "/" + UUID.randomUUID().toString() + LOGS_FILE_SUFFIX)); - credentialsDTO.setParams(LogsFileUploadList.builder().files(files).build()); - String bid = UUID.randomUUID().toString(); - ServiceReply reply = messageSenderService.publishServicesTopic( - deviceSn, LogsFileMethodEnum.FILE_UPLOAD_START.getMethod(), credentialsDTO, bid); + credentialsDTO.setParams(new FileUploadStartParam().setFiles(files)); - if (ResponseResult.CODE_SUCCESS != reply.getResult()) { - return ResponseResult.error(String.valueOf(reply.getResult())); - } + TopicServicesResponse response = abstractLogService.fileuploadStart( + SDKManager.getDeviceSDK(deviceSn), new FileUploadStartRequest() + .setCredentials(stsCredentials.getCredentials()) + .setBucket(stsCredentials.getBucket()) + .setEndpoint(stsCredentials.getEndpoint()) + .setFileStoreDir(stsCredentials.getObjectKeyPrefix()) + .setProvider(stsCredentials.getProvider()) + .setRegion(stsCredentials.getRegion()) + .setParams(new FileUploadStartParam().setFiles(files))); - String logsId = this.insertDeviceLogs(bid, username, deviceSn, param); - if (!bid.equals(logsId)) { - return ResponseResult.error("Database insert failed."); + if (!response.getData().getResult().isSuccess()) { + return HttpResultResponse.error(response.getData().getResult()); } + String id = this.insertDeviceLogs(response.getBid(), username, deviceSn, param); + // Save the status of the log upload. - RedisOpsUtils.hashSet(RedisConst.LOGS_FILE_PREFIX + deviceSn, bid, LogsOutputProgressDTO.builder().logsId(logsId).build()); - return ResponseResult.success(); + RedisOpsUtils.hashSet(RedisConst.LOGS_FILE_PREFIX + deviceSn, id, LogsOutputProgressDTO.builder().logsId(id).build()); + return HttpResultResponse.success(); } @Override - public ResponseResult pushUpdateFile(String deviceSn, LogsFileUpdateParam param) { - LogsFileUpdateMethodEnum method = LogsFileUpdateMethodEnum.find(param.getStatus()); - if (LogsFileUpdateMethodEnum.UNKNOWN == method) { - return ResponseResult.error("Illegal param"); - } - ServiceReply reply = messageSenderService.publishServicesTopic( - deviceSn, LogsFileMethodEnum.FILE_UPLOAD_UPDATE.getMethod(), param); + public HttpResultResponse pushUpdateFile(String deviceSn, FileUploadUpdateRequest param) { + TopicServicesResponse response = abstractLogService.fileuploadUpdate(SDKManager.getDeviceSDK(deviceSn), param); - if (ResponseResult.CODE_SUCCESS != reply.getResult()) { - return ResponseResult.error("Error Code : " + reply.getResult()); + if (!response.getData().getResult().isSuccess()) { + return HttpResultResponse.error(response.getData().getResult()); } - - return ResponseResult.success(); + return HttpResultResponse.success(); } @Override @@ -194,92 +195,82 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService { logsFileService.deleteFileByLogsId(logsId); } - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) @Override - public CommonTopicReceiver handleFileUploadProgress(CommonTopicReceiver receiver, MessageHeaders headers) { - String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); - String sn = topic.substring((TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT).length(), - topic.indexOf(TopicConst.EVENTS_SUF)); - - EventsReceiver eventsReceiver = objectMapper.convertValue(receiver.getData(), - new TypeReference>(){}); - + public TopicEventsResponse fileuploadProgress(TopicEventsRequest> request, MessageHeaders headers) { EventsReceiver webSocketData = new EventsReceiver<>(); - webSocketData.setBid(receiver.getBid()); - webSocketData.setSn(sn); + webSocketData.setBid(request.getBid()); + webSocketData.setSn(request.getGateway()); - Optional deviceOpt = deviceRedisService.getDeviceOnline(sn); + Optional deviceOpt = deviceRedisService.getDeviceOnline(request.getGateway()); if (deviceOpt.isEmpty()) { return null; } DeviceDTO device = deviceOpt.get(); + String key = RedisConst.LOGS_FILE_PREFIX + request.getGateway(); try { - OutputLogsProgressReceiver output = eventsReceiver.getOutput(); - EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus()); + FileUploadProgress output = request.getData().getOutput(); log.info("Logs upload progress: {}", output.toString()); - String key = RedisConst.LOGS_FILE_PREFIX + sn; LogsOutputProgressDTO progress; boolean exist = RedisOpsUtils.checkExist(key); - if (!exist && !statusEnum.getEnd()) { - progress = LogsOutputProgressDTO.builder().logsId(receiver.getBid()).build(); - RedisOpsUtils.hashSet(key, receiver.getBid(), progress); + if (!exist && !output.getStatus().isEnd()) { + progress = LogsOutputProgressDTO.builder().logsId(request.getBid()).build(); + RedisOpsUtils.hashSet(key, request.getBid(), progress); } else if (exist) { - progress = (LogsOutputProgressDTO) RedisOpsUtils.hashGet(key, receiver.getBid()); + progress = (LogsOutputProgressDTO) RedisOpsUtils.hashGet(key, request.getBid()); } else { progress = LogsOutputProgressDTO.builder().build(); } progress.setStatus(output.getStatus()); // If the logs file is empty, delete the cache of this task. - List fileReceivers = output.getExt().getFiles(); + List fileReceivers = output.getExt().getFiles(); if (CollectionUtils.isEmpty(fileReceivers)) { - RedisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); + RedisOpsUtils.del(key); } // refresh cache. List fileProgressList = new ArrayList<>(); fileReceivers.forEach(file -> { - LogsProgressReceiver logsProgress = file.getProgress(); + LogFileProgress logsProgress = file.getProgress(); if (!StringUtils.hasText(file.getDeviceSn())) { - if (String.valueOf(DeviceDomainEnum.DOCK.getVal()).equals(file.getDeviceModelDomain())) { - file.setDeviceSn(sn); - } else if (String.valueOf(DeviceDomainEnum.SUB_DEVICE.getVal()).equals(file.getDeviceModelDomain())) { + if (LogModuleEnum.DOCK == file.getModule()) { + file.setDeviceSn(request.getGateway()); + } else if (LogModuleEnum.DRONE == file.getModule()) { file.setDeviceSn(device.getChildDeviceSn()); } } fileProgressList.add(LogsProgressDTO.builder() .deviceSn(file.getDeviceSn()) - .deviceModelDomain(file.getDeviceModelDomain()) + .deviceModelDomain(file.getModule().getDomain()) .result(logsProgress.getResult()) - .status(logsProgress.getStatus()) + .status(logsProgress.getStatus().getStatus()) .uploadRate(logsProgress.getUploadRate()) .progress(((logsProgress.getCurrentStep() - 1) * 100 + logsProgress.getProgress()) / logsProgress.getTotalStep()) .build()); }); progress.setFiles(fileProgressList); webSocketData.setOutput(progress); - RedisOpsUtils.hashSet(RedisConst.LOGS_FILE_PREFIX + sn, receiver.getBid(), progress); + RedisOpsUtils.hashSet(RedisConst.LOGS_FILE_PREFIX + request.getGateway(), request.getBid(), progress); // Delete the cache at the end of the task. - if (statusEnum.getEnd()) { - RedisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); - this.updateLogsStatus(receiver.getBid(), DeviceLogsStatusEnum.find(statusEnum).getVal()); + if (output.getStatus().isEnd()) { + RedisOpsUtils.del(key); + updateLogsStatus(request.getBid(), DeviceLogsStatusEnum.find(output.getStatus()).getVal()); - fileReceivers.forEach(file -> logsFileService.updateFile(receiver.getBid(), file)); + fileReceivers.forEach(file -> logsFileService.updateFile(request.getBid(), file)); } } catch (NullPointerException e) { - this.updateLogsStatus(receiver.getBid(), DeviceLogsStatusEnum.FAILED.getVal()); - - RedisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); + this.updateLogsStatus(request.getBid(), DeviceLogsStatusEnum.FAILED.getVal()); + RedisOpsUtils.del(key); } webSocketMessageService.sendBatch(device.getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.FILE_UPLOAD_PROGRESS.getCode(), webSocketData); - return receiver; + return new TopicEventsResponse().setData(MqttReply.success()); } @Override @@ -315,7 +306,7 @@ public class DeviceLogsServiceImpl implements IDeviceLogsService { .status(entity.getStatus()) .logsInformation(entity.getLogsInfo()) .userName(entity.getUsername()) - .deviceLogs(LogsFileUploadList.builder().files(logsFileService.getLogsFileByLogsId(entity.getLogsId())).build()) + .deviceLogs(LogsFileUploadListDTO.builder().files(logsFileService.getLogsFileByLogsId(entity.getLogsId())).build()) .logsProgress(Objects.requireNonNullElse(progress, new LogsOutputProgressDTO()).getFiles()) .deviceTopo(topologyService.getDeviceTopologyByGatewaySn(entity.getDeviceSn()).orElse(null)) .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 deleted file mode 100644 index e5a596e..0000000 --- a/src/main/java/com/dji/sample/manage/service/impl/DeviceOSDServiceImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.dji.sample.manage.service.impl; - -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; -import com.dji.sample.component.redis.RedisConst; -import com.dji.sample.component.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.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 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.Collection; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * @author sean - * @version 0.3 - * @date 2022/2/21 - */ -@Service -@Slf4j -public class DeviceOSDServiceImpl extends AbstractTSAService { - - protected DeviceOSDServiceImpl(@Autowired @Qualifier("dockOSDServiceImpl") AbstractTSAService tsaService) { - super(tsaService); - } - - @Override - public void pushTelemetryData(Collection sessions, - CustomWebSocketMessage message, Object osdData) { - if (osdData instanceof OsdSubDeviceReceiver) { - OsdSubDeviceReceiver data = (OsdSubDeviceReceiver) osdData; - TelemetryDTO telemetry = message.getData(); - telemetry.setHost(TelemetryDeviceDTO.builder() - .latitude(data.getLatitude()) - .longitude(data.getLongitude()) - .altitude(data.getElevation()) - .attitudeHead(data.getAttitudeHead()) - .elevation(data.getElevation()) - .horizontalSpeed(data.getHorizontalSpeed()) - .verticalSpeed(data.getVerticalSpeed()) - .build()); - - this.sendMessageService.sendBatch(sessions, message); - } - } - @Override - public void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, - Collection webSessions, - CustomWebSocketMessage wsMessage) { - if (DeviceDomainEnum.SUB_DEVICE.getVal() == device.getDomain()) { - wsMessage.setBizCode(BizCodeEnum.DEVICE_OSD.getCode()); - - 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.getPayloadIndex(), Map.of()), - OsdPayloadReceiver.class)) - .collect(Collectors.toList())); - - } catch (NullPointerException e) { - log.warn("Please remount the payload, or restart the drone. Otherwise the data of the payload will not be received."); - } - - RedisOpsUtils.setWithExpire(RedisConst.OSD_PREFIX + device.getDeviceSn(), data, RedisConst.DEVICE_ALIVE_SECOND); - 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 04169bd..5d2e795 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 @@ -2,31 +2,20 @@ 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.mqtt.model.ChannelName; -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.service.ISendMessageService; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.control.model.enums.DroneAuthorityEnum; import com.dji.sample.manage.dao.IDevicePayloadMapper; -import com.dji.sample.manage.model.dto.DeviceAuthorityDTO; -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.dto.*; import com.dji.sample.manage.model.entity.DevicePayloadEntity; -import com.dji.sample.manage.model.enums.ControlSourceEnum; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.enums.UserTypeEnum; -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; import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sdk.cloudapi.device.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; @@ -54,7 +43,7 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { private ICapacityCameraService capacityCameraService; @Autowired - private ISendMessageService sendMessageService; + private IWebSocketMessageService sendMessageService; @Autowired private IDeviceRedisService deviceRedisService; @@ -80,31 +69,19 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { } @Override - public Boolean savePayloadDTOs(List payloadReceiverList) { - if (payloadReceiverList.isEmpty()) { - return true; - } - - String deviceSn = payloadReceiverList.get(0).getDeviceSn(); - Optional deviceOpt = deviceRedisService.getDeviceOnline(deviceSn); - if (deviceOpt.isEmpty()) { - return false; - } - DeviceDTO device = deviceOpt.get(); - List payloads = new ArrayList<>(); - - Map controlMap = CollectionUtils.isEmpty(device.getPayloadsList()) ? - Collections.emptyMap() : - device.getPayloadsList().stream() - .collect(Collectors.toMap(DevicePayloadDTO::getPayloadIndex, DevicePayloadDTO::getControlSource)); + public Boolean savePayloadDTOs(DeviceDTO device, List payloadReceiverList) { + Map controlMap = CollectionUtils.isEmpty(device.getPayloadsList()) ? + Collections.emptyMap() : device.getPayloadsList().stream() + .collect(Collectors.toMap(DevicePayloadDTO::getPayloadSn, DevicePayloadDTO::getControlSource)); for (DevicePayloadReceiver payloadReceiver : payloadReceiverList) { + payloadReceiver.setDeviceSn(device.getDeviceSn()); int payloadId = this.saveOnePayloadDTO(payloadReceiver); if (payloadId <= 0) { + log.error("Payload data saving failed."); return false; } - payloads.add(this.receiver2Dto(payloadReceiver)); - if (!controlMap.getOrDefault(payloadReceiver.getPayloadIndex(), "").equals(payloadReceiver.getControlSource())) { + if (controlMap.get(payloadReceiver.getSn()) != payloadReceiver.getControlSource()) { sendMessageService.sendBatch(device.getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.CONTROL_SOURCE_CHANGE.getCode(), DeviceAuthorityDTO.builder() @@ -115,18 +92,15 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { } } - if (payloads.isEmpty()) { - payloads = this.getDevicePayloadEntitiesByDeviceSn(deviceSn); - } + List payloads = this.getDevicePayloadEntitiesByDeviceSn(device.getDeviceSn()); device.setPayloadsList(payloads); - deviceRedisService.setDeviceOnline(device); return true; } @Override public Integer saveOnePayloadDTO(DevicePayloadReceiver payloadReceiver) { - return this.saveOnePayloadEntity(payloadDTOConvertToEntity(payloadReceiver)); + return this.saveOnePayloadEntity(receiverConvertToEntity(payloadReceiver)); } @Override @@ -150,46 +124,45 @@ 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())); + public Boolean updateFirmwareVersion(String droneSn, PayloadFirmwareVersion receiver) { + return mapper.update(DevicePayloadEntity.builder() + .firmwareVersion(receiver.getFirmwareVersion()).build(), + new LambdaUpdateWrapper() + .eq(DevicePayloadEntity::getDeviceSn, droneSn) + .eq(DevicePayloadEntity::getPayloadSn, droneSn + "-" + receiver.getPosition().getPosition()) + ) > 0; } /** * Handle payload data for devices. - * @param payloadReceiverList - * @param headers + * @param drone + * @param payloads */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_PAYLOAD) - public void handleDeviceBasicPayload(List payloadReceiverList, MessageHeaders headers) { - if (payloadReceiverList.isEmpty()) { + public void updatePayloadControl(DeviceDTO drone, List payloads) { + boolean match = payloads.stream().peek(p -> p.setSn(Objects.requireNonNullElse(p.getSn(), + p.getDeviceSn() + "-" + p.getPayloadIndex().getPosition().getPosition()))) + .anyMatch(p -> ControlSourceEnum.UNKNOWN == p.getControlSource()); + if (match) { return; } - String deviceSn = payloadReceiverList.get(0).getDeviceSn(); - String key = RedisConst.STATE_PAYLOAD_PREFIX + deviceSn; - // Solve timing problems - long last = (long) Objects.requireNonNullElse(RedisOpsUtils.get(key), 0L); - long timestamp = headers.getTimestamp(); - if (last > timestamp) { + + if (payloads.isEmpty()) { + drone.setPayloadsList(null); + this.deletePayloadsByDeviceSn(List.of(drone.getDeviceSn())); + deviceRedisService.setDeviceOnline(drone); return; } // Filter unsaved payload information. - Set payloadSns = this.getDevicePayloadEntitiesByDeviceSn(payloadReceiverList.get(0).getDeviceSn()) + Set payloadSns = this.getDevicePayloadEntitiesByDeviceSn(drone.getDeviceSn()) .stream().map(DevicePayloadDTO::getPayloadSn).collect(Collectors.toSet()); - Set newPayloadSns = payloadReceiverList.stream().map(DevicePayloadReceiver::getSn).collect(Collectors.toSet()); + Set newPayloadSns = payloads.stream().map(DevicePayloadReceiver::getSn).collect(Collectors.toSet()); payloadSns.removeAll(newPayloadSns); this.deletePayloadsByPayloadsSn(payloadSns); // Save the new payload information. - boolean isSave = this.savePayloadDTOs(payloadReceiverList); - if (isSave) { - RedisOpsUtils.setWithExpire(key, timestamp, RedisConst.DEVICE_ALIVE_SECOND); - } + boolean isSave = this.savePayloadDTOs(drone, payloads); log.debug("The result of saving the payloads is {}.", isSave); } @@ -205,14 +178,14 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { @Override public Boolean checkAuthorityPayload(String deviceSn, String payloadIndex) { return deviceRedisService.getDeviceOnline(deviceSn).flatMap(device -> - Optional.of(DeviceDomainEnum.SUB_DEVICE.getVal() == device.getDomain() + Optional.of(DeviceDomainEnum.DRONE == device.getDomain() && !CollectionUtils.isEmpty(device.getPayloadsList()) - && ControlSourceEnum.A.getControlSource() - .equals(device.getPayloadsList().stream() - .filter(payload -> payloadIndex.equals(payload.getPayloadIndex())) + && ControlSourceEnum.A == + device.getPayloadsList().stream() + .filter(payload -> payloadIndex.equals(payload.getPayloadIndex().toString())) .map(DevicePayloadDTO::getControlSource).findAny() - .orElse(ControlSourceEnum.B.getControlSource())))).orElse(true); - + .orElse(ControlSourceEnum.B))) + .orElse(true); } /** @@ -227,8 +200,11 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { .payloadName(entity.getPayloadName()) .payloadDesc(entity.getPayloadDesc()) .index(entity.getPayloadIndex()) - .payloadIndex(entity.getPayloadType() + "-" + entity.getSubType() + "-" + entity.getPayloadIndex()) - .controlSource(entity.getControlSource()); + .payloadIndex(new PayloadIndex() + .setType(DeviceTypeEnum.find(entity.getPayloadType())) + .setSubType(DeviceSubTypeEnum.find(entity.getSubType())) + .setPosition(PayloadPositionEnum.find(entity.getPayloadIndex()))) + .controlSource(ControlSourceEnum.find(entity.getControlSource())); } return builder.build(); } @@ -238,7 +214,7 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { * @param dto payload * @return */ - private DevicePayloadEntity payloadDTOConvertToEntity(DevicePayloadReceiver dto) { + private DevicePayloadEntity receiverConvertToEntity(DevicePayloadReceiver dto) { if (dto == null) { return new DevicePayloadEntity(); } @@ -246,27 +222,17 @@ public class DevicePayloadServiceImpl implements IDevicePayloadService { // The cameraIndex consists of type and subType and the index of the payload hanging on the drone. // type-subType-index - String[] payloadIndexArr = dto.getPayloadIndex().split("-"); - try { - int[] arr = Arrays.stream(payloadIndexArr) - .mapToInt(Integer::parseInt) - .toArray(); - - Optional dictionaryOpt = dictionaryService - .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), arr[0], arr[1]); - dictionaryOpt.ifPresent(dictionary -> - builder.payloadName(dictionary.getDeviceName()) - .payloadDesc(dictionary.getDeviceDesc())); - - builder.payloadType(arr[0]) - .subType(arr[1]) - .payloadIndex(arr[2]) - .controlSource(dto.getControlSource()); - } catch (NumberFormatException e) { - builder.payloadType(-1) - .subType(-1) - .payloadIndex(-1); - } + Optional dictionaryOpt = dictionaryService.getOneDictionaryInfoByTypeSubType( + DeviceDomainEnum.PAYLOAD.getDomain(), dto.getPayloadIndex().getType().getType(), + dto.getPayloadIndex().getSubType().getSubType()); + dictionaryOpt.ifPresent(dictionary -> + builder.payloadName(dictionary.getDeviceName()) + .payloadDesc(dictionary.getDeviceDesc())); + + builder.payloadType(dto.getPayloadIndex().getType().getType()) + .subType(dto.getPayloadIndex().getSubType().getSubType()) + .payloadIndex(dto.getPayloadIndex().getPosition().getPosition()) + .controlSource(dto.getControlSource().getControlSource()); return builder .payloadSn(dto.getSn()) diff --git a/src/main/java/com/dji/sample/manage/service/impl/DeviceRedisServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/DeviceRedisServiceImpl.java index 4629892..4d01f56 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/DeviceRedisServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/DeviceRedisServiceImpl.java @@ -1,12 +1,13 @@ package com.dji.sample.manage.service.impl; -import com.dji.sample.component.mqtt.model.EventsOutputProgressReceiver; import com.dji.sample.component.mqtt.model.EventsReceiver; 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.receiver.FirmwareProgressExtReceiver; +import com.dji.sample.manage.service.ICapacityCameraService; import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sdk.cloudapi.firmware.OtaProgress; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Optional; @@ -21,6 +22,9 @@ import java.util.stream.Collectors; @Service public class DeviceRedisServiceImpl implements IDeviceRedisService { + @Autowired + private ICapacityCameraService capacityCameraService; + @Override public Boolean checkDeviceOnline(String sn) { String key = RedisConst.DEVICE_ONLINE_PREFIX + sn; @@ -42,19 +46,29 @@ public class DeviceRedisServiceImpl implements IDeviceRedisService { return RedisOpsUtils.del(RedisConst.DEVICE_ONLINE_PREFIX + sn); } + @Override + public void setDeviceOsd(String sn, Object data) { + RedisOpsUtils.setWithExpire(RedisConst.OSD_PREFIX + sn, data, RedisConst.DEVICE_ALIVE_SECOND); + } + @Override public Optional getDeviceOsd(String sn, Class clazz) { return Optional.ofNullable(clazz.cast(RedisOpsUtils.get(RedisConst.OSD_PREFIX + sn))); } @Override - public void setFirmwareUpgrading(String sn, EventsReceiver> events) { + public Boolean delDeviceOsd(String sn) { + return RedisOpsUtils.del(RedisConst.OSD_PREFIX + sn); + } + + @Override + public void setFirmwareUpgrading(String sn, EventsReceiver events) { RedisOpsUtils.setWithExpire(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn, events, RedisConst.DEVICE_ALIVE_SECOND * 20); } @Override - public Optional>> getFirmwareUpgradingProgress(String sn) { - return Optional.ofNullable((EventsReceiver>) RedisOpsUtils.get(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn)); + public Optional> getFirmwareUpgradingProgress(String sn) { + return Optional.ofNullable((EventsReceiver) RedisOpsUtils.get(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn)); } @Override @@ -77,4 +91,19 @@ public class DeviceRedisServiceImpl implements IDeviceRedisService { public Boolean delHmsKeysBySn(String sn) { return RedisOpsUtils.del(RedisConst.HMS_PREFIX + sn); } + + @Override + public void gatewayOffline(String gatewaySn) { + delDeviceOnline(gatewaySn); + delHmsKeysBySn(gatewaySn); + capacityCameraService.deleteCapacityCameraByDeviceSn(gatewaySn); + } + + @Override + public void subDeviceOffline(String deviceSn) { + delDeviceOnline(deviceSn); + delDeviceOsd(deviceSn); + delHmsKeysBySn(deviceSn); + capacityCameraService.deleteCapacityCameraByDeviceSn(deviceSn); + } } 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 40b5314..f5bbe95 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 @@ -4,53 +4,55 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.dji.sample.common.error.CommonErrorEnum; -import com.dji.sample.common.model.Pagination; -import com.dji.sample.common.model.PaginationData; -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.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.mqtt.model.EventsReceiver; 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.component.websocket.service.IWebSocketMessageService; import com.dji.sample.control.model.enums.DroneAuthorityEnum; import com.dji.sample.manage.dao.IDeviceMapper; import com.dji.sample.manage.model.dto.*; import com.dji.sample.manage.model.entity.DeviceEntity; -import com.dji.sample.manage.model.enums.*; -import com.dji.sample.manage.model.param.DeviceOtaCreateParam; +import com.dji.sample.manage.model.enums.DeviceFirmwareStatusEnum; +import com.dji.sample.manage.model.enums.PropertySetFieldEnum; +import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.model.param.DeviceQueryParam; -import com.dji.sample.manage.model.receiver.*; +import com.dji.sample.manage.model.receiver.BasicDeviceProperty; import com.dji.sample.manage.service.*; -import com.fasterxml.jackson.core.type.TypeReference; +import com.dji.sdk.cloudapi.device.*; +import com.dji.sdk.cloudapi.firmware.*; +import com.dji.sdk.cloudapi.firmware.api.AbstractFirmwareService; +import com.dji.sdk.cloudapi.property.api.AbstractPropertyService; +import com.dji.sdk.cloudapi.tsa.DeviceIconUrl; +import com.dji.sdk.cloudapi.tsa.TopologyDeviceModel; +import com.dji.sdk.common.*; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.MqttGatewayPublish; +import com.dji.sdk.mqtt.events.EventsSubscribe; +import com.dji.sdk.mqtt.osd.OsdSubscribe; +import com.dji.sdk.mqtt.property.PropertySetReplyResultEnum; +import com.dji.sdk.mqtt.property.PropertySetSubscribe; +import com.dji.sdk.mqtt.requests.RequestsSubscribe; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.ServicesSubscribe; +import com.dji.sdk.mqtt.services.TopicServicesResponse; +import com.dji.sdk.mqtt.state.StateSubscribe; +import com.dji.sdk.mqtt.status.StatusSubscribe; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.integration.mqtt.support.MqttHeaders; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import java.io.IOException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; -import static com.dji.sample.component.mqtt.model.TopicConst.*; - /** * * @author sean.zhou @@ -63,7 +65,7 @@ import static com.dji.sample.component.mqtt.model.TopicConst.*; public class DeviceServiceImpl implements IDeviceService { @Autowired - private IMessageSenderService messageSender; + private MqttGatewayPublish messageSender; @Autowired private IDeviceMapper mapper; @@ -81,14 +83,11 @@ public class DeviceServiceImpl implements IDeviceService { private IDevicePayloadService payloadService; @Autowired - private ISendMessageService sendMessageService; + private IWebSocketMessageService webSocketMessageService; @Autowired private ObjectMapper objectMapper; - @Autowired - private IWebSocketManageService webSocketManageService; - @Autowired private IDeviceFirmwareService deviceFirmwareService; @@ -99,180 +98,99 @@ public class DeviceServiceImpl implements IDeviceService { private IDeviceRedisService deviceRedisService; @Autowired - @Qualifier("gatewayOSDServiceImpl") - private ITSAService tsaService; + private StatusSubscribe statusSubscribe; - private static final List INIT_TOPICS_SUFFIX = List.of( - OSD_SUF, STATE_SUF, SERVICES_SUF + _REPLY_SUF, EVENTS_SUF, PROPERTY_SUF + SET_SUF + _REPLY_SUF); + @Autowired + private StateSubscribe stateSubscribe; - @Override - public Boolean deviceOffline(StatusGatewayReceiver gateway) { - String gatewaySn = gateway.getSn(); - this.subscribeTopicOnline(gatewaySn); + @Autowired + private OsdSubscribe osdSubscribe; - // Only the remote controller is logged in and the aircraft is not connected. - Optional deviceOpt = deviceRedisService.getDeviceOnline(gatewaySn); - if (deviceOpt.isEmpty()) { - Optional gatewayOpt = this.getDeviceBySn(gatewaySn); - if (gatewayOpt.isPresent()) { - DeviceDTO value = gatewayOpt.get(); - value.setChildDeviceSn(null); - deviceRedisService.setDeviceOnline(value); - this.pushDeviceOnlineTopo(value.getWorkspaceId(), gatewaySn, gatewaySn); - return true; - } - - // When connecting for the first time - DeviceEntity gatewayDevice = deviceGatewayConvertToDeviceEntity(gateway); - return onlineSaveDevice(gatewayDevice, null, null).isPresent(); - } + @Autowired + private ServicesSubscribe servicesSubscribe; - DeviceDTO deviceDTO = deviceOpt.get(); - String deviceSn = deviceDTO.getChildDeviceSn(); - if (!StringUtils.hasText(deviceSn)) { - return true; - } + @Autowired + private EventsSubscribe eventsSubscribe; - return subDeviceOffline(deviceSn); - } + @Autowired + private RequestsSubscribe requestsSubscribe; - @Override - public Boolean subDeviceOffline(String deviceSn) { + @Autowired + private PropertySetSubscribe propertySetSubscribe; + + @Autowired + private AbstractPropertyService abstractPropertyService; + @Autowired + private AbstractFirmwareService abstractFirmwareService; + + @Override + public void subDeviceOffline(String deviceSn) { // If no information about this device exists in the cache, the drone is considered to be offline. Optional deviceOpt = deviceRedisService.getDeviceOnline(deviceSn); if (deviceOpt.isEmpty()) { log.debug("The drone is already offline."); - return true; + return; } - DeviceDTO device = deviceOpt.get(); - // Cancel drone-related subscriptions. - this.unsubscribeTopicOffline(deviceSn); - - capacityCameraService.deleteCapacityCameraByDeviceSn(deviceSn); - deviceRedisService.delDeviceOnline(deviceSn); - RedisOpsUtils.del(RedisConst.OSD_PREFIX + deviceSn); - deviceRedisService.delHmsKeysBySn(deviceSn); + try { + gatewayOnlineSubscribeTopic(SDKManager.getDeviceSDK(String.valueOf(deviceOpt.get().getParentSn()))); + } catch (CloudSDKException e) { + log.debug("The gateway is already offline.", e); + } + deviceRedisService.subDeviceOffline(deviceSn); // Publish the latest device topology information in the current workspace. - this.pushDeviceOfflineTopo(device.getWorkspaceId(), deviceSn); - + pushDeviceOfflineTopo(deviceOpt.get().getWorkspaceId(), deviceSn); log.debug("{} offline.", deviceSn); - return true; } @Override - public Boolean deviceOnline(StatusGatewayReceiver deviceGateway) { - String deviceSn = deviceGateway.getSubDevices().get(0).getSn(); - - Optional deviceOpt = deviceRedisService.getDeviceOnline(deviceSn); - Optional gatewayOpt = deviceRedisService.getDeviceOnline(deviceGateway.getSn()); - - if (deviceOpt.isPresent() && gatewayOpt.isPresent()) { - 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 = deviceOpt.get().getWorkspaceId(); - if (StringUtils.hasText(workspaceId)) { - this.subscribeTopicOnline(deviceSn); - this.subscribeTopicOnline(deviceGateway.getSn()); - } - log.warn("{} is already online.", deviceSn); - return true; - } - - 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); - Optional gatewayEntityOpt = onlineSaveDevice(gateway, deviceSn, null); - if (gatewayEntityOpt.isEmpty()) { - log.error("Failed to go online, please check the status data or code logic."); - return false; - } - - DeviceEntity subDevice = subDeviceConvertToDeviceEntity(deviceGateway.getSubDevices().get(0)); - Optional subDeviceEntityOpt = onlineSaveDevice(subDevice, null, gateway.getDeviceSn()); - if (subDeviceEntityOpt.isEmpty()) { - log.error("Failed to go online, please check the status data or code logic."); - return false; - } - - subDevice = subDeviceEntityOpt.get(); - gateway = gatewayEntityOpt.get(); - - // dock go online - if (DeviceDomainEnum.DOCK.getVal() == deviceGateway.getDomain() && !subDevice.getBoundStatus()) { - // Directly bind the drone of the dock to the same workspace as the dock. - bindDevice(DeviceDTO.builder().deviceSn(deviceSn).workspaceId(gateway.getWorkspaceId()).build()); - subDevice.setWorkspaceId(gateway.getWorkspaceId()); + public void gatewayOffline(String gatewaySn) { + // If no information about this device exists in the cache, the drone is considered to be offline. + Optional deviceOpt = deviceRedisService.getDeviceOnline(gatewaySn); + if (deviceOpt.isEmpty()) { + log.debug("The gateway is already offline."); + return; } - // Subscribe to topic related to drone devices. - this.subscribeTopicOnline(deviceGateway.getSn()); - this.subscribeTopicOnline(deviceSn); - this.pushDeviceOnlineTopo(subDevice.getWorkspaceId(), deviceGateway.getSn(), deviceSn); - - log.debug("{} online.", subDevice.getDeviceSn()); - return true; - } - - @Override - public void subscribeTopicOnline(String sn) { - String[] subscribedTopic = topicService.getSubscribedTopic(); - for (String s : subscribedTopic) { - // If you have already subscribed to the topic of the device, you do not need to subscribe again. - if (s.contains(sn)) { - return; - } - } - String prefix = THING_MODEL_PRE + PRODUCT + sn; - INIT_TOPICS_SUFFIX.forEach(suffix -> topicService.subscribe(prefix + suffix)); + deviceRedisService.subDeviceOffline(deviceOpt.get().getChildDeviceSn()); + deviceRedisService.gatewayOffline(gatewaySn); + offlineUnsubscribeTopic(SDKManager.getDeviceSDK(gatewaySn)); + // Publish the latest device topology information in the current workspace. + pushDeviceOfflineTopo(deviceOpt.get().getWorkspaceId(), gatewaySn); + log.debug("{} offline.", gatewaySn); } @Override - public void unsubscribeTopicOffline(String sn) { - String prefix = THING_MODEL_PRE + PRODUCT + sn; - INIT_TOPICS_SUFFIX.forEach(suffix -> topicService.unsubscribe(prefix + suffix)); + public void gatewayOnlineSubscribeTopic(GatewayManager gateway) { + statusSubscribe.subscribe(gateway); + stateSubscribe.subscribe(gateway, true); + osdSubscribe.subscribe(gateway, true); + servicesSubscribe.subscribe(gateway); + eventsSubscribe.subscribe(gateway, true); + requestsSubscribe.subscribe(gateway); + propertySetSubscribe.subscribe(gateway); } @Override - public Boolean delDeviceByDeviceSns(List ids) { - if (CollectionUtils.isEmpty(ids)) { - return true; - } - return mapper.delete(new LambdaQueryWrapper() - .in(DeviceEntity::getDeviceSn, ids)) - > 0; + public void subDeviceOnlineSubscribeTopic(GatewayManager gateway) { + statusSubscribe.subscribe(gateway); + stateSubscribe.subscribe(gateway, false); + osdSubscribe.subscribe(gateway, false); + servicesSubscribe.subscribe(gateway); + eventsSubscribe.subscribe(gateway, false); + requestsSubscribe.subscribe(gateway); + propertySetSubscribe.subscribe(gateway); } @Override - public void publishStatusReply(String sn, CommonTopicResponse response) { - Map result = new ConcurrentHashMap<>(1); - result.put("result", 0); - response.setData(result); - - messageSender.publish( - new StringBuilder() - .append(BASIC_PRE) - .append(PRODUCT) - .append(sn) - .append(STATUS_SUF) - .append(_REPLY_SUF) - .toString(), - response); + public void offlineUnsubscribeTopic(GatewayManager gateway) { + statusSubscribe.unsubscribe(gateway); + stateSubscribe.unsubscribe(gateway); + osdSubscribe.unsubscribe(gateway); + servicesSubscribe.unsubscribe(gateway); + eventsSubscribe.unsubscribe(gateway); + requestsSubscribe.unsubscribe(gateway); + propertySetSubscribe.unsubscribe(gateway); } @Override @@ -307,11 +225,11 @@ public class DeviceServiceImpl implements IDeviceService { List devicesList = this.getDevicesByParams( DeviceQueryParam.builder() .workspaceId(workspaceId) - .domains(List.of(DeviceDomainEnum.GATEWAY.getVal(), DeviceDomainEnum.DOCK.getVal())) + .domains(List.of(DeviceDomainEnum.REMOTER_CONTROL.getDomain(), DeviceDomainEnum.DOCK.getDomain())) .build()); devicesList.stream() - .filter(gateway -> DeviceDomainEnum.DOCK.getVal() == gateway.getDomain() || + .filter(gateway -> DeviceDomainEnum.DOCK == gateway.getDomain() || deviceRedisService.checkDeviceOnline(gateway.getDeviceSn())) .forEach(this::spliceDeviceTopo); @@ -338,7 +256,7 @@ public class DeviceServiceImpl implements IDeviceService { @Override public Optional getDeviceTopoForPilot(String sn) { - if (sn.isBlank()) { + if (!StringUtils.hasText(sn)) { return Optional.empty(); } List topologyDeviceList = this.getDevicesByParams( @@ -354,263 +272,78 @@ public class DeviceServiceImpl implements IDeviceService { return Optional.of(topologyDeviceList.get(0)); } - @Override - public void pushDeviceOnlineTopo(Collection sessions, String sn, String gatewaySn) { - - CustomWebSocketMessage pilotMessage = - CustomWebSocketMessage.builder() - .timestamp(System.currentTimeMillis()) - .bizCode(BizCodeEnum.DEVICE_ONLINE.getCode()) - .data(new TopologyDeviceDTO()) - .build(); - - this.getDeviceTopoForPilot(sn) - .ifPresent(pilotMessage::setData); - pilotMessage.getData().setOnlineStatus(deviceRedisService.checkDeviceOnline(sn)); - pilotMessage.getData().setGatewaySn(gatewaySn.equals(sn) ? "" : gatewaySn); - - sendMessageService.sendBatch(sessions, pilotMessage); - } - @Override public TopologyDeviceDTO deviceConvertToTopologyDTO(DeviceDTO device) { - TopologyDeviceDTO.TopologyDeviceDTOBuilder builder = TopologyDeviceDTO.builder(); - - if (device != null) { - builder.sn(device.getDeviceSn()) - .deviceCallsign(device.getNickname()) - .deviceModel(DeviceModelDTO.builder() - .domain(String.valueOf(device.getDomain())) - .subType(String.valueOf(device.getSubType())) - .type(String.valueOf(device.getType())) - .key(device.getDomain() + "-" + device.getType() + "-" + device.getSubType()) - .build()) - .iconUrls(device.getIconUrl()) - .onlineStatus(deviceRedisService.checkDeviceOnline(device.getDeviceSn())) - .boundStatus(device.getBoundStatus()) - .model(device.getDeviceName()) - .userId(device.getUserId()) - .domain(device.getDomain()) - .build(); + if (device == null) { + return null; } - return builder.build(); + return new TopologyDeviceDTO() + .setSn(device.getDeviceSn()) + .setDeviceCallsign(device.getNickname()) + .setDeviceModel(new TopologyDeviceModel() + .setDomain(device.getDomain()) + .setSubType(device.getSubType()) + .setType(device.getType()) + .setDeviceModelKey(DeviceEnum.find(device.getDomain(), device.getType(), device.getSubType()))) + .setIconUrls(device.getIconUrl()) + .setOnlineStatus(deviceRedisService.checkDeviceOnline(device.getDeviceSn())) + .setUserCallsign(device.getNickname()) + .setBoundStatus(device.getBoundStatus()) + .setModel(device.getDeviceName()) + .setUserId(device.getUserId()) + .setDomain(device.getDomain()) + .setGatewaySn(device.getParentSn()); } @Override - public void pushDeviceOnlineTopo(String workspaceId, String gatewaySn, String deviceSn) { - - // All connected accounts in this workspace. - Collection allSessions = webSocketManageService.getValueWithWorkspace(workspaceId); - - if (!gatewaySn.equals(deviceSn)) { - this.pushDeviceOnlineTopo(allSessions, deviceSn, gatewaySn); - this.pushDeviceUpdateTopo(allSessions, deviceSn); - } - this.pushDeviceOnlineTopo(allSessions, gatewaySn, gatewaySn); - this.pushDeviceUpdateTopo(allSessions, gatewaySn); + public void pushDeviceOfflineTopo(String workspaceId, String deviceSn) { + webSocketMessageService.sendBatch( + workspaceId, null, com.dji.sdk.websocket.BizCodeEnum.DEVICE_OFFLINE.getCode(), + new TopologyDeviceDTO().setSn(deviceSn).setOnlineStatus(false)); } @Override - public void pushDeviceOfflineTopo(String workspaceId, String sn) { - // All connected accounts of this workspace. - Collection allSessions = webSocketManageService - .getValueWithWorkspace(workspaceId); - - this.pushDeviceOfflineTopo(allSessions, sn); - this.pushDeviceUpdateTopo(allSessions, sn); + public void pushDeviceOnlineTopo(String workspaceId, String gatewaySn, String deviceSn) { + webSocketMessageService.sendBatch( + workspaceId, null, com.dji.sdk.websocket.BizCodeEnum.DEVICE_ONLINE.getCode(), + getDeviceTopoForPilot(deviceSn).orElseGet(TopologyDeviceDTO::new).setGatewaySn(gatewaySn)); } @Override - @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD) - public void handleOSD(Message message) { - String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString(); - byte[] payload = (byte[])message.getPayload(); - CommonTopicReceiver receiver; - try { - String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), - topic.indexOf(OSD_SUF)); - - Optional deviceOpt = deviceRedisService.getDeviceOnline(from); - - if (deviceOpt.isEmpty()) { - deviceOpt = this.getDeviceBySn(from); - if (deviceOpt.isEmpty()) { - log.error("Please restart the drone."); - return; - } - - if (!StringUtils.hasText(deviceOpt.get().getWorkspaceId())) { - this.unsubscribeTopicOffline(from); - return; - } - deviceRedisService.setDeviceOnline(deviceOpt.get()); - this.subscribeTopicOnline(from); - } - DeviceDTO device = deviceOpt.get(); - deviceRedisService.setDeviceOnline(device); - - receiver = objectMapper.readValue(payload, CommonTopicReceiver.class); - - CustomWebSocketMessage wsMessage = CustomWebSocketMessage.builder() - .timestamp(System.currentTimeMillis()) - .data(TelemetryDTO.builder().sn(from).build()) - .build(); - - Collection webSessions = webSocketManageService - .getValueWithWorkspaceAndUserType( - device.getWorkspaceId(), UserTypeEnum.WEB.getVal()); - - - tsaService.handleOSD(receiver, device, webSessions, wsMessage); - - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Notify the pilot side that there is an update of the device topology. - * @param sessions - * @param deviceSn - */ - private void pushDeviceUpdateTopo(Collection sessions, String deviceSn) { - - CustomWebSocketMessage pilotMessage = - CustomWebSocketMessage.builder() - .timestamp(System.currentTimeMillis()) - .bizCode(BizCodeEnum.DEVICE_UPDATE_TOPO.getCode()) - .data(new TopologyDeviceDTO()) - .build(); - sendMessageService.sendBatch(sessions, pilotMessage); + public void pushOsdDataToPilot(String workspaceId, String sn, DeviceOsdHost data) { + webSocketMessageService.sendBatch( + workspaceId, UserTypeEnum.PILOT.getVal(), com.dji.sdk.websocket.BizCodeEnum.DEVICE_OSD.getCode(), + new DeviceOsdWsResponse() + .setSn(sn) + .setHost(data)); } - /** - * Notify the pilot side that device is offline and needs to reacquire topology information. - * @param sessions - * @param sn - */ - private void pushDeviceOfflineTopo(Collection sessions, String sn) { - CustomWebSocketMessage pilotMessage = - CustomWebSocketMessage.builder() - .timestamp(System.currentTimeMillis()) - .bizCode(BizCodeEnum.DEVICE_OFFLINE.getCode()) - .data(TopologyDeviceDTO.builder() - .sn(sn) - .onlineStatus(false) - .build()) - .build(); - sendMessageService.sendBatch(sessions, pilotMessage); + @Override + public void pushOsdDataToWeb(String workspaceId, BizCodeEnum codeEnum, String sn, Object data) { + webSocketMessageService.sendBatch( + workspaceId, UserTypeEnum.WEB.getVal(), codeEnum.getCode(), TelemetryDTO.builder().sn(sn).host(data).build()); } /** * Save the device information and update the information directly if the device already exists. - * @param entity + * @param device * @return */ - private Optional saveDevice(DeviceEntity entity) { - DeviceEntity deviceEntity = mapper.selectOne( + public Boolean saveOrUpdateDevice(DeviceDTO device) { + int count = mapper.selectCount( new LambdaQueryWrapper() - .eq(DeviceEntity::getDeviceSn, entity.getDeviceSn())); - // Update the information directly if the device already exists. - if (deviceEntity != null) { - if (deviceEntity.getDeviceName().equals(entity.getNickname())) { - entity.setNickname(null); - } - entity.setId(deviceEntity.getId()); - mapper.updateById(entity); - fillNullField(entity, deviceEntity); - return Optional.of(entity); - } - return mapper.insert(entity) > 0 ? Optional.of(entity) : Optional.empty(); - } - - private void fillNullField(DeviceEntity entity, DeviceEntity oldEntity) { - if (Objects.isNull(entity) || Objects.isNull(oldEntity)) { - return; - } - if (Objects.isNull(entity.getWorkspaceId())) { - entity.setWorkspaceId(oldEntity.getWorkspaceId()); - } - if (Objects.isNull(entity.getUserId())) { - entity.setUserId(oldEntity.getUserId()); - } - if (Objects.isNull(entity.getChildSn())) { - entity.setChildSn(oldEntity.getChildSn()); - } - if (Objects.isNull(entity.getBoundStatus())) { - entity.setBoundStatus(oldEntity.getBoundStatus()); - } - if (Objects.isNull(entity.getBoundTime())) { - entity.setBoundTime(oldEntity.getBoundTime()); - } - if (Objects.isNull(entity.getFirmwareVersion())) { - entity.setFirmwareVersion(oldEntity.getFirmwareVersion()); - } - if (Objects.isNull(entity.getDeviceIndex())) { - entity.setDeviceIndex(oldEntity.getDeviceIndex()); - } - } - - /** - * Convert the received gateway device object into a database entity object. - * @param gateway - * @return - */ - private DeviceEntity deviceGatewayConvertToDeviceEntity(StatusGatewayReceiver gateway) { - if (gateway == null) { - return new DeviceEntity(); - } - DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder(); - - // Query the model information of this gateway device. - Optional dictionaryOpt = dictionaryService - .getOneDictionaryInfoByTypeSubType(Objects.nonNull(gateway.getDomain()) ? - gateway.getDomain() : DeviceDomainEnum.GATEWAY.getVal(), - gateway.getType(), gateway.getSubType()); - - dictionaryOpt.ifPresent(entity -> - builder.deviceName(entity.getDeviceName()) - .nickname(entity.getDeviceName()) - .deviceDesc(entity.getDeviceDesc())); - - return builder - .deviceSn(gateway.getSn()) - .subType(gateway.getSubType()) - .deviceType(gateway.getType()) - .version(gateway.getVersion()) - .domain(gateway.getDomain() != null ? - gateway.getDomain() : DeviceDomainEnum.GATEWAY.getVal()) - .deviceIndex(gateway.getSubDevices().isEmpty() ? null : gateway.getSubDevices().get(0).getIndex()) - .build(); + .eq(DeviceEntity::getDeviceSn, device.getDeviceSn())); + return count > 0 ? updateDevice(device) : saveDevice(device) > 0; } /** - * Convert the received drone device object into a database entity object. + * Save the device information. * @param device * @return */ - private DeviceEntity subDeviceConvertToDeviceEntity(StatusSubDeviceReceiver device) { - if (device == null) { - return new DeviceEntity(); - } - DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder(); - - // Query the model information of this drone device. - Optional dictionaryOpt = dictionaryService - .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.SUB_DEVICE.getVal(), 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()) - .version(device.getVersion()) - .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) - .build(); + public Integer saveDevice(DeviceDTO device) { + DeviceEntity entity = deviceDTO2Entity(device); + return mapper.insert(entity) > 0 ? entity.getId() : -1; } /** @@ -622,34 +355,39 @@ public class DeviceServiceImpl implements IDeviceService { if (entity == null) { return null; } - DeviceDTO deviceDTO = DeviceDTO.builder() - .deviceSn(entity.getDeviceSn()) - .childDeviceSn(entity.getChildSn()) - .deviceName(entity.getDeviceName()) - .deviceDesc(entity.getDeviceDesc()) - .controlSource(entity.getDeviceIndex()) - .workspaceId(entity.getWorkspaceId()) - .type(entity.getDeviceType()) - .subType(entity.getSubType()) - .domain(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("") : "") - .firmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal()).build(); - + DeviceDTO.DeviceDTOBuilder builder = DeviceDTO.builder(); + try { + builder + .deviceSn(entity.getDeviceSn()) + .childDeviceSn(entity.getChildSn()) + .deviceName(entity.getDeviceName()) + .deviceDesc(entity.getDeviceDesc()) + .controlSource(ControlSourceEnum.find(entity.getDeviceIndex())) + .workspaceId(entity.getWorkspaceId()) + .type(DeviceTypeEnum.find(entity.getDeviceType())) + .subType(DeviceSubTypeEnum.find(entity.getSubType())) + .domain(DeviceDomainEnum.find(entity.getDomain())) + .iconUrl(new DeviceIconUrl() + .setNormalIconUrl(entity.getUrlNormal()) + .setSelectIconUrl(entity.getUrlSelect())) + .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("") : "") + .firmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE) + .thingVersion(entity.getVersion()).build(); + } catch (CloudSDKException e) { + log.error(e.getLocalizedMessage() + "Entity: {}", entity); + } + DeviceDTO deviceDTO = builder.build(); addFirmwareStatus(deviceDTO, entity); return deviceDTO; } @@ -659,13 +397,12 @@ public class DeviceServiceImpl implements IDeviceService { return; } // Query whether the device is updating firmware. - Optional>> progressOpt = - deviceRedisService.getFirmwareUpgradingProgress(entity.getDeviceSn()); + Optional> progressOpt = deviceRedisService.getFirmwareUpgradingProgress(entity.getDeviceSn()); if (progressOpt.isPresent()) { - deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.UPGRADING.getVal()); + deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.UPGRADING); deviceDTO.setFirmwareProgress(progressOpt.map(EventsReceiver::getOutput) - .map(EventsOutputProgressReceiver::getProgress) - .map(OutputProgressReceiver::getPercent) + .map(OtaProgress::getProgress) + .map(OtaProgressData::getPercent) .orElse(0)); return; } @@ -674,16 +411,16 @@ public class DeviceServiceImpl implements IDeviceService { // to see if it needs to be upgraded. Optional firmwareReleaseNoteOpt = deviceFirmwareService.getLatestFirmwareReleaseNote(entity.getDeviceName()); if (firmwareReleaseNoteOpt.isEmpty()) { - deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal()); + deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE); return; } if (entity.getFirmwareVersion().equals(firmwareReleaseNoteOpt.get().getProductVersion())) { - deviceDTO.setFirmwareStatus(Objects.requireNonNullElse(entity.getCompatibleStatus(), true) ? - DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal() : - DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE.getVal()); + deviceDTO.setFirmwareStatus(entity.getCompatibleStatus() ? + DeviceFirmwareStatusEnum.NOT_UPGRADE : + DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE); return; } - deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.NORMAL_UPGRADE.getVal()); + deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.NORMAL_UPGRADE); } @Override @@ -698,7 +435,7 @@ public class DeviceServiceImpl implements IDeviceService { device.setBoundStatus(true); device.setBoundTime(LocalDateTime.now()); - boolean isUpd = this.saveDevice(this.deviceDTO2Entity(device)).isPresent(); + boolean isUpd = this.updateDevice(device); if (!isUpd) { return false; } @@ -712,111 +449,20 @@ public class DeviceServiceImpl implements IDeviceService { redisDevice.setWorkspaceId(device.getWorkspaceId()); deviceRedisService.setDeviceOnline(redisDevice); - if (DeviceDomainEnum.GATEWAY.getVal() == redisDevice.getDomain()) { - this.pushDeviceOnlineTopo(webSocketManageService.getValueWithWorkspace(device.getWorkspaceId()), - device.getDeviceSn(), device.getDeviceSn()); - } - if (DeviceDomainEnum.SUB_DEVICE.getVal() == redisDevice.getDomain()) { - DeviceDTO subDevice = this.getDevicesByParams(DeviceQueryParam.builder() - .childSn(device.getChildDeviceSn()) - .build()).get(0); - this.pushDeviceOnlineTopo(webSocketManageService.getValueWithWorkspace(device.getWorkspaceId()), - device.getDeviceSn(), subDevice.getDeviceSn()); + String gatewaySn, deviceSn; + if (DeviceDomainEnum.REMOTER_CONTROL == redisDevice.getDomain()) { + gatewaySn = device.getDeviceSn(); + deviceSn = redisDevice.getChildDeviceSn(); + } else { + gatewaySn = redisDevice.getParentSn(); + deviceSn = device.getDeviceSn(); } - this.subscribeTopicOnline(device.getDeviceSn()); + pushDeviceOnlineTopo(device.getWorkspaceId(), gatewaySn, deviceSn); + subDeviceOnlineSubscribeTopic(SDKManager.getDeviceSDK(gatewaySn)); return true; } - /** - * Handle dock binding status requests. - * @param receiver - * @param headers - */ - @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()); - - } - - /** - * Handle dock binding requests. - * @param receiver - * @param headers - */ - @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; - } - } - - Optional dockEntityOpt = this.bindDevice2Entity(DeviceDomainEnum.DOCK.getVal(), dock); - Optional droneEntityOpt = this.bindDevice2Entity(DeviceDomainEnum.SUB_DEVICE.getVal(), 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, Integer domain) { @@ -874,36 +520,11 @@ public class DeviceServiceImpl implements IDeviceService { return Optional.of(device); } - /** - * Update the firmware version information of the device or payload. - * @param receiver - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_FIRMWARE_VERSION) - public void updateFirmwareVersion(FirmwareVersionReceiver receiver) { - // If the reported version is empty, it will not be processed to prevent misleading page. - if (!StringUtils.hasText(receiver.getFirmwareVersion())) { - return; - } - - if (receiver.getDomain() == DeviceDomainEnum.SUB_DEVICE) { - final DeviceDTO device = DeviceDTO.builder() - .deviceSn(receiver.getSn()) - .firmwareVersion(receiver.getFirmwareVersion()) - .firmwareStatus(receiver.getCompatibleStatus() == null ? - null : DeviceFirmwareStatusEnum.CompatibleStatusEnum.INCONSISTENT.getVal() != receiver.getCompatibleStatus() ? - DeviceFirmwareStatusEnum.UNKNOWN.getVal() : DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE.getVal()) - .build(); - this.updateDevice(device); - return; - } - payloadService.updateFirmwareVersion(receiver); - } - @Override - public ResponseResult createDeviceOtaJob(String workspaceId, List upgradeDTOS) { - List deviceOtaFirmwares = deviceFirmwareService.getDeviceOtaFirmware(workspaceId, upgradeDTOS); + public HttpResultResponse createDeviceOtaJob(String workspaceId, List upgradeDTOS) { + List deviceOtaFirmwares = deviceFirmwareService.getDeviceOtaFirmware(workspaceId, upgradeDTOS); if (deviceOtaFirmwares.isEmpty()) { - return ResponseResult.error(); + return HttpResultResponse.error(); } Optional deviceOpt = deviceRedisService.getDeviceOnline(deviceOtaFirmwares.get(0).getSn()); @@ -911,22 +532,22 @@ public class DeviceServiceImpl implements IDeviceService { throw new RuntimeException("Device is offline."); } DeviceDTO device = deviceOpt.get(); - String gatewaySn = DeviceDomainEnum.DOCK.getVal() == device.getDomain() ? device.getDeviceSn() : device.getParentSn(); + String gatewaySn = DeviceDomainEnum.DOCK == device.getDomain() ? device.getDeviceSn() : device.getParentSn(); checkOtaConditions(gatewaySn); - String bid = UUID.randomUUID().toString(); - ServiceReply serviceReply = messageSender.publishServicesTopic( - gatewaySn, FirmwareMethodEnum.OTA_CREATE.getMethod(), Map.of(MapKeyConst.DEVICES, deviceOtaFirmwares), bid); - if (serviceReply.getResult() != ResponseResult.CODE_SUCCESS) { - return ResponseResult.error(serviceReply.getResult(), "Firmware Error Code: " + serviceReply.getResult()); + TopicServicesResponse> response = abstractFirmwareService.otaCreate( + SDKManager.getDeviceSDK(gatewaySn), new OtaCreateRequest().setDevices(deviceOtaFirmwares)); + ServicesReplyData serviceReply = response.getData(); + String bid = response.getBid(); + if (!serviceReply.getResult().isSuccess()) { + return HttpResultResponse.error(serviceReply.getResult()); } // Record the device state that needs to be updated. deviceOtaFirmwares.forEach(deviceOta -> deviceRedisService.setFirmwareUpgrading(deviceOta.getSn(), - EventsReceiver.>builder() - .bid(bid).sn(deviceOta.getSn()).build())); - return ResponseResult.success(); + EventsReceiver.builder().bid(bid).sn(deviceOta.getSn()).build())); + return HttpResultResponse.success(); } /** @@ -934,7 +555,7 @@ public class DeviceServiceImpl implements IDeviceService { * @param dockSn */ private void checkOtaConditions(String dockSn) { - Optional deviceOpt = deviceRedisService.getDeviceOsd(dockSn, OsdDockReceiver.class); + Optional deviceOpt = deviceRedisService.getDeviceOsd(dockSn, OsdDock.class); if (deviceOpt.isEmpty()) { throw new RuntimeException("Dock is offline."); } @@ -950,100 +571,81 @@ public class DeviceServiceImpl implements IDeviceService { } @Override - public void devicePropertySet(String workspaceId, String dockSn, DeviceSetPropertyEnum propertyEnum, JsonNode param) { + public int devicePropertySet(String workspaceId, String dockSn, JsonNode param) { + String property = param.fieldNames().next(); + PropertySetFieldEnum propertyEnum = PropertySetFieldEnum.find(property); + Optional dockOpt = deviceRedisService.getDeviceOnline(dockSn); if (dockOpt.isEmpty()) { throw new RuntimeException("Dock is offline."); } String childSn = dockOpt.get().getChildDeviceSn(); - boolean deviceOnline = deviceRedisService.checkDeviceOnline(childSn); - Optional osdOpt = deviceRedisService.getDeviceOsd(childSn, OsdSubDeviceReceiver.class); - if (!deviceOnline || osdOpt.isEmpty()) { + Optional osdOpt = deviceRedisService.getDeviceOsd(childSn, OsdDockDrone.class); + if (osdOpt.isEmpty()) { throw new RuntimeException("Device is offline."); } // Make sure the data is valid. - BasicDeviceProperty basicDeviceProperty = objectMapper.convertValue(param, propertyEnum.getClazz()); + BasicDeviceProperty basicDeviceProperty = objectMapper.convertValue(param.get(property), propertyEnum.getClazz()); boolean valid = basicDeviceProperty.valid(); if (!valid) { - throw new IllegalArgumentException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg()); - } - - String topic = THING_MODEL_PRE + PRODUCT + dockSn + PROPERTY_SUF + SET_SUF; - if (!param.isObject()) { - this.deviceOnePropertySet(topic, propertyEnum, Map.entry(propertyEnum.getProperty(), param)); - return; + throw new IllegalArgumentException(CommonErrorEnum.ILLEGAL_ARGUMENT.getMessage()); } - // If there are multiple parameters, set them separately. - for (Iterator> filed = param.fields(); filed.hasNext(); ) { - Map.Entry node = filed.next(); - boolean isPublish = basicDeviceProperty.canPublish(node.getKey(), osdOpt.get()); - if (!isPublish) { - continue; - } - this.deviceOnePropertySet(topic, propertyEnum, Map.entry(propertyEnum.getProperty(), node)); + boolean isPublish = basicDeviceProperty.canPublish(osdOpt.get()); + if (!isPublish) { + return PropertySetReplyResultEnum.SUCCESS.getResult(); } - - } - - @Override - public void deviceOnePropertySet(String topic, DeviceSetPropertyEnum propertyEnum, Map.Entry value) { - if (Objects.isNull(value) || Objects.isNull(value.getValue())) { - throw new IllegalArgumentException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg()); - } - - Map reply = messageSender.publishWithReply( - Map.class, topic, - CommonTopicResponse.builder() - .bid(UUID.randomUUID().toString()) - .tid(UUID.randomUUID().toString()) - .timestamp(System.currentTimeMillis()) - .data(value) - .build()); - - while (true) { - reply = (Map) reply.get(value.getKey()); - if (value.getValue() instanceof JsonNode) { - break; - } - value = (Map.Entry) value.getValue(); - } - - SetReply setReply = objectMapper.convertValue(reply, SetReply.class); - if (SetReplyStatusResultEnum.SUCCESS.getVal() != setReply.getResult()) { - throw new RuntimeException("Failed to set " + value.getKey() + "; Error Code: " + setReply.getResult()); - } - + BaseModel baseModel = objectMapper.convertValue(param, propertyEnum.getProperty().getClazz()); + PropertySetReplyResultEnum result = abstractPropertyService.propertySet( + SDKManager.getDeviceSDK(dockSn), propertyEnum.getProperty(), baseModel); + return result.getResult(); } @Override public DockModeCodeEnum getDockMode(String dockSn) { - return deviceRedisService.getDeviceOsd(dockSn, OsdDockReceiver.class) - .map(OsdDockReceiver::getModeCode).orElse(DockModeCodeEnum.DISCONNECTED); + return deviceRedisService.getDeviceOsd(dockSn, OsdDock.class) + .map(OsdDock::getModeCode).orElse(null); } @Override - public DeviceModeCodeEnum getDeviceMode(String deviceSn) { - return deviceRedisService.getDeviceOsd(deviceSn, OsdSubDeviceReceiver.class) - .map(OsdSubDeviceReceiver::getModeCode).orElse(DeviceModeCodeEnum.DISCONNECTED); + public DroneModeCodeEnum getDeviceMode(String deviceSn) { + return deviceRedisService.getDeviceOsd(deviceSn, OsdDockDrone.class) + .map(OsdDockDrone::getModeCode).orElse(DroneModeCodeEnum.DISCONNECTED); } @Override public Boolean checkDockDrcMode(String dockSn) { - return deviceRedisService.getDeviceOsd(dockSn, OsdDockReceiver.class) - .map(OsdDockReceiver::getDrcState) - .orElse(DockDrcStateEnum.DISCONNECTED) != DockDrcStateEnum.DISCONNECTED; + return deviceRedisService.getDeviceOsd(dockSn, OsdDock.class) + .map(OsdDock::getDrcState) + .orElse(DrcStateEnum.DISCONNECTED) != DrcStateEnum.DISCONNECTED; } @Override public Boolean checkAuthorityFlight(String gatewaySn) { return deviceRedisService.getDeviceOnline(gatewaySn).flatMap(gateway -> - Optional.of((DeviceDomainEnum.DOCK.getVal() == gateway.getDomain() - || DeviceDomainEnum.GATEWAY.getVal() == gateway.getDomain()) - && ControlSourceEnum.A.getControlSource().equals(gateway.getControlSource()))) + Optional.of((DeviceDomainEnum.DOCK == gateway.getDomain() + || DeviceDomainEnum.REMOTER_CONTROL == gateway.getDomain()) + && ControlSourceEnum.A == gateway.getControlSource())) .orElse(true); } + @Override + public void updateFlightControl(DeviceDTO gateway, ControlSourceEnum controlSource) { + if (controlSource == gateway.getControlSource()) { + return; + } + gateway.setControlSource(controlSource); + deviceRedisService.setDeviceOnline(gateway); + + webSocketMessageService.sendBatch(gateway.getWorkspaceId(), UserTypeEnum.WEB.getVal(), + BizCodeEnum.CONTROL_SOURCE_CHANGE.getCode(), + DeviceAuthorityDTO.builder() + .controlSource(gateway.getControlSource()) + .sn(gateway.getDeviceSn()) + .type(DroneAuthorityEnum.FLIGHT) + .build()); + } + /** * Convert device data transfer object into database entity object. * @param dto @@ -1056,142 +658,26 @@ public class DeviceServiceImpl implements IDeviceService { } return builder.deviceSn(dto.getDeviceSn()) + .deviceIndex(Optional.ofNullable(dto.getControlSource()) + .map(ControlSourceEnum::getControlSource).orElse(null)) + .deviceName(dto.getDeviceName()) + .version(dto.getThingVersion()) .userId(dto.getUserId()) .nickname(dto.getNickname()) .workspaceId(dto.getWorkspaceId()) .boundStatus(dto.getBoundStatus()) + .domain(Optional.ofNullable(dto.getDomain()).map(DeviceDomainEnum::getDomain).orElse(null)) + .deviceType(Optional.ofNullable(dto.getType()).map(DeviceTypeEnum::getType).orElse(null)) + .subType(Optional.ofNullable(dto.getSubType()).map(DeviceSubTypeEnum::getSubType).orElse(null)) .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(dto.getDomain()) .firmwareVersion(dto.getFirmwareVersion()) .compatibleStatus(dto.getFirmwareStatus() == null ? null : - DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE != DeviceFirmwareStatusEnum.find(dto.getFirmwareStatus())) - .build(); - } - - /** - * Convert device binding data object into database entity object. - * - * @param domain - * @param receiver - * @return - */ - private Optional bindDevice2Entity(Integer domain, BindDeviceReceiver receiver) { - if (receiver == null) { - return Optional.empty(); - } - int[] droneKey = Arrays.stream(receiver.getDeviceModelKey().split("-")).mapToInt(Integer::parseInt).toArray(); - Optional dictionaryOpt = dictionaryService.getOneDictionaryInfoByTypeSubType(domain, 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()) + DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE != dto.getFirmwareStatus()) + .deviceDesc(dto.getDeviceDesc()) .build(); } - - private Optional onlineSaveDevice(DeviceEntity device, String childSn, String parentSn) { - - Optional deviceOpt = this.getDeviceBySn(device.getDeviceSn()); - if (deviceOpt.isEmpty()) { - // Set the icon of the gateway device displayed in the pilot's map, required in the TSA module. - device.setUrlNormal(IconUrlEnum.NORMAL_PERSON.getUrl()); - // Set the icon of the gateway device displayed in the pilot's map when it is selected, required in the TSA module. - device.setUrlSelect(IconUrlEnum.SELECT_PERSON.getUrl()); - device.setBoundStatus(false); - } else { - DeviceDTO oldDevice = deviceOpt.get(); - device.setNickname(oldDevice.getNickname()); - device.setBoundStatus(oldDevice.getBoundStatus()); - } - - device.setChildSn(childSn); - device.setLoginTime(System.currentTimeMillis()); - - Optional saveDeviceOpt = this.saveDevice(device); - if (saveDeviceOpt.isEmpty()) { - return saveDeviceOpt; - } - - DeviceDTO redisDevice = this.deviceEntityConvertToDTO(saveDeviceOpt.get()); - redisDevice.setParentSn(parentSn); - - deviceRedisService.setDeviceOnline(redisDevice); - - return saveDeviceOpt; - } - - /** - * Handles messages in the state topic about basic drone data. - * - * Note: Only the data of the drone payload is handled here. You can handle other data from the drone - * according to your business needs. - * @param deviceBasic basic drone data - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_BASIC, outputChannel = ChannelName.INBOUND_STATE_PAYLOAD) - public List stateBasic(DeviceBasicReceiver deviceBasic) { - Optional deviceOpt = deviceRedisService.getDeviceOnline(deviceBasic.getDeviceSn()); - if (deviceOpt.isEmpty()) { - return deviceBasic.getPayloads(); - } - Optional dockOpt = deviceRedisService.getDeviceOnline(deviceOpt.get().getParentSn()); - if (dockOpt.isEmpty()) { - return deviceBasic.getPayloads(); - } - DeviceDTO dock = dockOpt.get(); - if (!deviceBasic.getControlSource().equals(dock.getControlSource())) { - dock.setControlSource(deviceBasic.getControlSource()); - deviceRedisService.setDeviceOnline(dock); - - sendMessageService.sendBatch(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal(), - BizCodeEnum.CONTROL_SOURCE_CHANGE.getCode(), - DeviceAuthorityDTO.builder() - .controlSource(dock.getControlSource()) - .sn(dock.getDeviceSn()) - .type(DroneAuthorityEnum.FLIGHT) - .build()); - - } - return deviceBasic.getPayloads(); - } } \ 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 deleted file mode 100644 index afb9fc0..0000000 --- a/src/main/java/com/dji/sample/manage/service/impl/DockOSDServiceImpl.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.dji.sample.manage.service.impl; - -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; -import com.dji.sample.component.redis.RedisConst; -import com.dji.sample.component.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.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 org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.Objects; - -/** - * @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.getVal() == device.getDomain()) { - wsMessage.setBizCode(BizCodeEnum.DOCK_OSD.getCode()); - OsdDockReceiver data = mapper.convertValue(receiver.getData(), OsdDockReceiver.class); - wsMessage.getData().setHost(data); - sendMessageService.sendBatch(webSessions, wsMessage); - String key = RedisConst.OSD_PREFIX + device.getDeviceSn(); - OsdDockReceiver redisData = (OsdDockReceiver) RedisOpsUtils.get(key); - if (Objects.nonNull(data.getModeCode())) { - if (Objects.nonNull(redisData)) { - data.setDrcState(redisData.getDrcState()); - } - RedisOpsUtils.setWithExpire(key, data, RedisConst.DEVICE_ALIVE_SECOND); - return; - } - - if (Objects.nonNull(data.getDrcState()) && Objects.nonNull(redisData)) { - redisData.setDrcState(data.getDrcState()); - RedisOpsUtils.setWithExpire(key, redisData, RedisConst.DEVICE_ALIVE_SECOND); - } - } - } -} 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 deleted file mode 100644 index eb788b2..0000000 --- a/src/main/java/com/dji/sample/manage/service/impl/GatewayOSDServiceImpl.java +++ /dev/null @@ -1,64 +0,0 @@ -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.dto.TelemetryDeviceDTO; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; -import com.dji.sample.manage.model.receiver.OsdGatewayReceiver; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; - -import java.util.Collection; - -/** - * @author sean - * @version 0.3 - * @date 2022/2/21 - */ -@Service -public class GatewayOSDServiceImpl extends AbstractTSAService { - - public GatewayOSDServiceImpl(@Autowired @Qualifier("deviceOSDServiceImpl") AbstractTSAService tsaService) { - super(tsaService); - } - - @Override - public void pushTelemetryData(Collection sessions, - CustomWebSocketMessage message, Object osdData) { - if (osdData instanceof OsdGatewayReceiver) { - OsdGatewayReceiver data = (OsdGatewayReceiver) osdData; - TelemetryDTO telemetry = message.getData(); - telemetry.setHost(TelemetryDeviceDTO.builder() - .latitude(data.getLatitude()) - .longitude(data.getLongitude()) - .build()); - this.sendMessageService.sendBatch(sessions, message); - return; - } - tsaService.pushTelemetryData(sessions, message, osdData); - } - - @Override - public void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, - Collection webSessions, - CustomWebSocketMessage wsMessage) { - if (DeviceDomainEnum.GATEWAY.getVal() == device.getDomain()) { - - wsMessage.setBizCode(BizCodeEnum.GATEWAY_OSD.getCode()); - OsdGatewayReceiver data = mapper.convertValue(receiver.getData(), OsdGatewayReceiver.class); - wsMessage.getData().setHost(data); - - this.sendMessageService.sendBatch(webSessions, wsMessage); - - this.pushTelemetryData(device.getWorkspaceId(), data, device.getDeviceSn()); - return; - } - - 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 5d2c8cd..2b1f590 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 @@ -1,33 +1,27 @@ 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.CommonTopicResponse; -import com.dji.sample.component.mqtt.model.ServiceReply; -import com.dji.sample.component.mqtt.service.IMessageSenderService; -import com.dji.sample.component.redis.RedisConst; -import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.manage.model.dto.*; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; -import com.dji.sample.manage.model.enums.LiveStreamMethodEnum; -import com.dji.sample.manage.model.enums.LiveUrlTypeEnum; -import com.dji.sample.manage.model.enums.LiveVideoQualityEnum; import com.dji.sample.manage.model.param.DeviceQueryParam; -import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; -import com.dji.sample.manage.model.receiver.LiveCapacityReceiver; import com.dji.sample.manage.service.*; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.device.VideoId; +import com.dji.sdk.cloudapi.livestream.*; +import com.dji.sdk.cloudapi.livestream.api.AbstractLivestreamService; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.lang.reflect.Field; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; -import static com.dji.sample.component.mqtt.model.TopicConst.*; - /** * @author sean.zhou * @date 2021/11/22 @@ -47,10 +41,10 @@ public class LiveStreamServiceImpl implements ILiveStreamService { private IWorkspaceService workspaceService; @Autowired - private IMessageSenderService messageSender; + private IDeviceRedisService deviceRedisService; @Autowired - private IDeviceRedisService deviceRedisService; + private AbstractLivestreamService abstractLivestreamService; @Override public List getLiveCapacity(String workspaceId) { @@ -59,7 +53,7 @@ public class LiveStreamServiceImpl implements ILiveStreamService { List devicesList = deviceService.getDevicesByParams( DeviceQueryParam.builder() .workspaceId(workspaceId) - .domains(List.of(DeviceDomainEnum.SUB_DEVICE.getVal(), DeviceDomainEnum.DOCK.getVal())) + .domains(List.of(DeviceDomainEnum.DRONE.getDomain(), DeviceDomainEnum.DOCK.getDomain())) .build()); // Query the live capability of each drone. @@ -74,42 +68,29 @@ public class LiveStreamServiceImpl implements ILiveStreamService { } @Override - public void saveLiveCapacity(LiveCapacityReceiver liveCapacityReceiver, Long timestamp) { - // Solve timing problems - for (CapacityDeviceReceiver capacityDeviceReceiver : liveCapacityReceiver.getDeviceList()) { - long last = (long) Objects.requireNonNullElse( - RedisOpsUtils.get(RedisConst.LIVE_CAPACITY + capacityDeviceReceiver.getSn()), 0L); - if (last > timestamp) { - return; - } - capacityCameraService.saveCapacityCameraReceiverList( - capacityDeviceReceiver.getCameraList(), - capacityDeviceReceiver.getSn(), timestamp); - } - } - - @Override - public ResponseResult liveStart(LiveTypeDTO liveParam) { + public HttpResultResponse liveStart(LiveTypeDTO liveParam) { // Check if this lens is available live. - ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId()); - if (ResponseResult.CODE_SUCCESS != responseResult.getCode()) { + HttpResultResponse responseResult = this.checkBeforeLive(liveParam.getVideoId()); + if (HttpResultResponse.CODE_SUCCESS != responseResult.getCode()) { return responseResult; } - DeviceDTO data = (DeviceDTO)responseResult.getData(); - // target topic - String respTopic = THING_MODEL_PRE + PRODUCT + - data.getDeviceSn() + SERVICES_SUF; - ServiceReply receiveReply = this.publishLiveStart(respTopic, liveParam); + TopicServicesResponse> response = abstractLivestreamService.liveStartPush( + SDKManager.getDeviceSDK(responseResult.getData().getDeviceSn()), new LiveStartPushRequest() + .setUrl(liveParam.getUrl()) + .setUrlType(liveParam.getUrlType()) + .setVideoId(new VideoId(liveParam.getVideoId())) + .setVideoQuality(liveParam.getVideoQuality())); - if (ResponseResult.CODE_SUCCESS != receiveReply.getResult()) { - return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult())); + if (!response.getData().getResult().isSuccess()) { + return HttpResultResponse.error(response.getData().getResult()); } - LiveUrlTypeEnum urlType = LiveUrlTypeEnum.find(liveParam.getUrlType()); LiveDTO live = new LiveDTO(); - switch (urlType) { + switch (liveParam.getUrlType()) { + case AGORA: + break; case RTMP: live.setUrl(liveParam.getUrl().replace("rtmp", "webrtc")); break; @@ -125,89 +106,68 @@ public class LiveStreamServiceImpl implements ILiveStreamService { .toString()); break; case RTSP: - String url = receiveReply.getOutput().toString(); + String url = response.getData().getOutput(); this.resolveUrlUser(url, live); break; - case UNKNOWN: - return ResponseResult.error(LiveErrorEnum.URL_TYPE_NOT_SUPPORTED); + default: + return HttpResultResponse.error(LiveErrorCodeEnum.URL_TYPE_NOT_SUPPORTED); } - return ResponseResult.success(live); + return HttpResultResponse.success(live); } @Override - public ResponseResult liveStop(String videoId) { - ResponseResult responseResult = this.checkBeforeLive(videoId); - if (responseResult.getCode() != 0) { + public HttpResultResponse liveStop(String videoId) { + HttpResultResponse responseResult = this.checkBeforeLive(videoId); + if (HttpResultResponse.CODE_SUCCESS != responseResult.getCode()) { return responseResult; } - String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; - - ServiceReply receiveReply = this.publishLiveStop(respTopic, videoId); - if (receiveReply.getResult() != 0) { - return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult())); + TopicServicesResponse response = abstractLivestreamService.liveStopPush( + SDKManager.getDeviceSDK(responseResult.getData().getDeviceSn()), new LiveStopPushRequest() + .setVideoId(new VideoId(videoId))); + if (!response.getData().getResult().isSuccess()) { + return HttpResultResponse.error(response.getData().getResult()); } - return ResponseResult.success(); + return HttpResultResponse.success(); } @Override - public ResponseResult liveSetQuality(LiveTypeDTO liveParam) { - if (liveParam.getVideoQuality() == null || - LiveVideoQualityEnum.UNKNOWN == LiveVideoQualityEnum.find(liveParam.getVideoQuality())) { - return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS); - } - - ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId()); - + public HttpResultResponse liveSetQuality(LiveTypeDTO liveParam) { + HttpResultResponse responseResult = this.checkBeforeLive(liveParam.getVideoId()); if (responseResult.getCode() != 0) { return responseResult; } - String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; - - ServiceReply receiveReply = this.publishLiveSetQuality(respTopic, liveParam); - if (ResponseResult.CODE_SUCCESS != receiveReply.getResult()) { - return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult())); + TopicServicesResponse response = abstractLivestreamService.liveSetQuality( + SDKManager.getDeviceSDK(responseResult.getData().getDeviceSn()), new LiveSetQualityRequest() + .setVideoQuality(liveParam.getVideoQuality()) + .setVideoId(new VideoId(liveParam.getVideoId()))); + if (!response.getData().getResult().isSuccess()) { + return HttpResultResponse.error(response.getData().getResult()); } - return ResponseResult.success(); + return HttpResultResponse.success(); } @Override - public ResponseResult liveLensChange(LiveTypeDTO liveParam) { - if (!StringUtils.hasText(liveParam.getVideoType())) { - return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS); - } - - ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId()); - if (ResponseResult.CODE_SUCCESS != responseResult.getCode()) { + public HttpResultResponse liveLensChange(LiveTypeDTO liveParam) { + HttpResultResponse responseResult = this.checkBeforeLive(liveParam.getVideoId()); + if (HttpResultResponse.CODE_SUCCESS != responseResult.getCode()) { return responseResult; } - if (DeviceDomainEnum.GATEWAY.getVal() == responseResult.getData().getDomain()) { - return ResponseResult.error(LiveErrorEnum.FUNCTION_NOT_SUPPORT); - } - String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF; + TopicServicesResponse response = abstractLivestreamService.liveLensChange( + SDKManager.getDeviceSDK(responseResult.getData().getDeviceSn()), new LiveLensChangeRequest() + .setVideoType(liveParam.getVideoType()) + .setVideoId(new VideoId(liveParam.getVideoId()))); - ServiceReply receiveReply = this.publishLiveLensChange(respTopic, liveParam); - - if (ResponseResult.CODE_SUCCESS != receiveReply.getResult()) { - return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult())); + if (!response.getData().getResult().isSuccess()) { + return HttpResultResponse.error(response.getData().getResult()); } - return ResponseResult.success(); - } - - private ServiceReply publishLiveLensChange(String respTopic, LiveTypeDTO liveParam) { - CommonTopicResponse response = new CommonTopicResponse<>(); - response.setTid(UUID.randomUUID().toString()); - response.setBid(UUID.randomUUID().toString()); - response.setMethod(LiveStreamMethodEnum.LIVE_LENS_CHANGE.getMethod()); - response.setData(liveParam); - - return messageSender.publishWithReply(ServiceReply.class, respTopic, response); + return HttpResultResponse.success(); } /** @@ -215,34 +175,34 @@ public class LiveStreamServiceImpl implements ILiveStreamService { * @param videoId * @return */ - private ResponseResult checkBeforeLive(String videoId) { + private HttpResultResponse checkBeforeLive(String videoId) { if (!StringUtils.hasText(videoId)) { - return ResponseResult.error(LiveErrorEnum.ERROR_PARAMETERS); + return HttpResultResponse.error(LiveErrorCodeEnum.ERROR_PARAMETERS); } 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); + return HttpResultResponse.error(LiveErrorCodeEnum.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); + return HttpResultResponse.error(LiveErrorCodeEnum.NO_AIRCRAFT); } - if (DeviceDomainEnum.DOCK.getVal() == deviceOpt.get().getDomain()) { - return ResponseResult.success(deviceOpt.get()); + if (DeviceDomainEnum.DOCK == deviceOpt.get().getDomain()) { + return HttpResultResponse.success(deviceOpt.get()); } List gatewayList = deviceService.getDevicesByParams( DeviceQueryParam.builder() .childSn(videoIdArr[0]) .build()); if (gatewayList.isEmpty()) { - return ResponseResult.error(LiveErrorEnum.NO_FLIGHT_CONTROL); + return HttpResultResponse.error(LiveErrorCodeEnum.NO_FLIGHT_CONTROL); } - return ResponseResult.success(gatewayList.get(0)); + return HttpResultResponse.success(gatewayList.get(0)); } /** @@ -290,57 +250,4 @@ public class LiveStreamServiceImpl implements ILiveStreamService { } return gb28181; } - - /** - * Send a message to the pilot via mqtt to start the live streaming. - * @param topic - * @param liveParam - * @return - */ - private ServiceReply publishLiveStart(String topic, LiveTypeDTO liveParam) { - CommonTopicResponse response = new CommonTopicResponse<>(); - response.setTid(UUID.randomUUID().toString()); - response.setBid(UUID.randomUUID().toString()); - response.setData(liveParam); - response.setMethod(LiveStreamMethodEnum.LIVE_START_PUSH.getMethod()); - - return messageSender.publishWithReply(ServiceReply.class, topic, response); - } - - /** - * Send a message to the pilot via mqtt to set quality. - * @param respTopic - * @param liveParam - * @return - */ - private ServiceReply 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(LiveStreamMethodEnum.LIVE_SET_QUALITY.getMethod()); - response.setData(data); - - return messageSender.publishWithReply(ServiceReply.class, respTopic, response); - } - - /** - * Send a message to the pilot via mqtt to stop the live streaming. - * @param topic - * @param videoId - * @return - */ - private ServiceReply 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(LiveStreamMethodEnum.LIVE_STOP_PUSH.getMethod()); - - return messageSender.publishWithReply(ServiceReply.class, topic, response); - } - } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java index 5eeb5a1..22673f3 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/LogsFileIndexServiceImpl.java @@ -4,10 +4,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.dji.sample.manage.dao.ILogsFileIndexMapper; import com.dji.sample.manage.model.dto.LogsFileDTO; +import com.dji.sample.manage.model.dto.LogsFileUploadDTO; import com.dji.sample.manage.model.entity.LogsFileIndexEntity; -import com.dji.sample.manage.model.receiver.LogsFile; -import com.dji.sample.manage.model.receiver.LogsFileUpload; import com.dji.sample.manage.service.ILogsFileIndexService; +import com.dji.sdk.cloudapi.log.LogFileIndex; +import com.dji.sdk.cloudapi.log.LogModuleEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,11 +33,11 @@ public class LogsFileIndexServiceImpl implements ILogsFileIndexService { private ILogsFileIndexMapper mapper; @Override - public Boolean insertFileIndex(LogsFile file, String deviceSn, Integer domain, String fileId) { - if (Objects.isNull(file)) { + public Boolean insertFileIndex(LogFileIndex file, String deviceSn, Integer domain, String fileId) { + LogsFileIndexEntity entity = this.logsFile2Entity(file); + if (Objects.isNull(entity)) { return false; } - LogsFileIndexEntity entity = this.logsFile2Entity(file); entity.setDomain(domain); entity.setDeviceSn(deviceSn); entity.setFileId(fileId); @@ -45,10 +46,10 @@ public class LogsFileIndexServiceImpl implements ILogsFileIndexService { } @Override - public List getFileIndexByFileIds(List files) { - List list = new ArrayList<>(); + public List getFileIndexByFileIds(List files) { + List list = new ArrayList<>(); files.forEach(file -> { - Optional fileOpt = this.getFileIndexByFileId(file.getFileId()); + Optional fileOpt = this.getFileIndexByFileId(file.getFileId()); fileOpt.ifPresent(fileUpload -> { fileUpload.setObjectKey(file.getStatus() ? file.getObjectKey() : null); list.add(fileUpload); @@ -64,37 +65,36 @@ public class LogsFileIndexServiceImpl implements ILogsFileIndexService { } @Override - public Optional getFileIndexByFileId(String fileId) { + public Optional getFileIndexByFileId(String fileId) { List logsFileIndexList = mapper.selectList( new LambdaQueryWrapper().eq(LogsFileIndexEntity::getFileId, fileId)); if (CollectionUtils.isEmpty(logsFileIndexList)) { return Optional.empty(); } LogsFileIndexEntity entity = logsFileIndexList.get(0); - List logsFileList = logsFileIndexList.stream().map(this::entity2LogsFile).collect(Collectors.toList()); - return Optional.of(LogsFileUpload.builder() + List logsFileList = logsFileIndexList.stream().map(this::entity2LogsFile).collect(Collectors.toList()); + return Optional.of(LogsFileUploadDTO.builder() .deviceSn(entity.getDeviceSn()) - .deviceModelDomain(String.valueOf(entity.getDomain())) + .deviceModelDomain(LogModuleEnum.find(String.valueOf(entity.getDomain()))) .list(logsFileList) .fileId(fileId) .build()); } - private LogsFile entity2LogsFile(LogsFileIndexEntity entity) { + private LogFileIndex entity2LogsFile(LogsFileIndexEntity entity) { if (Objects.isNull(entity)) { return null; } - return LogsFile.builder() - .bootIndex(entity.getBootIndex()) - .startTime(entity.getStartTime()) - .endTime(entity.getEndTime()) - .size(entity.getSize()) - .build(); + return new LogFileIndex() + .setBootIndex(entity.getBootIndex()) + .setStartTime(entity.getStartTime()) + .setEndTime(entity.getEndTime()) + .setSize(entity.getSize()); } - private LogsFileIndexEntity logsFile2Entity(LogsFile file) { + private LogsFileIndexEntity logsFile2Entity(LogFileIndex file) { if (Objects.isNull(file)) { return null; } diff --git a/src/main/java/com/dji/sample/manage/service/impl/LogsFileServiceImpl.java b/src/main/java/com/dji/sample/manage/service/impl/LogsFileServiceImpl.java index 88bc2f4..4a839b9 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/LogsFileServiceImpl.java +++ b/src/main/java/com/dji/sample/manage/service/impl/LogsFileServiceImpl.java @@ -2,17 +2,18 @@ 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.mqtt.model.EventsResultStatusEnum; import com.dji.sample.component.oss.model.OssConfiguration; import com.dji.sample.component.oss.service.impl.OssServiceContext; import com.dji.sample.manage.dao.ILogsFileMapper; import com.dji.sample.manage.model.dto.LogsFileDTO; +import com.dji.sample.manage.model.dto.LogsFileUploadDTO; import com.dji.sample.manage.model.entity.LogsFileEntity; -import com.dji.sample.manage.model.receiver.LogsExtFileReceiver; -import com.dji.sample.manage.model.receiver.LogsFile; -import com.dji.sample.manage.model.receiver.LogsFileUpload; import com.dji.sample.manage.service.ILogsFileIndexService; import com.dji.sample.manage.service.ILogsFileService; +import com.dji.sdk.cloudapi.log.FileUploadProgressFile; +import com.dji.sdk.cloudapi.log.FileUploadStartFile; +import com.dji.sdk.cloudapi.log.FileUploadStatusEnum; +import com.dji.sdk.cloudapi.log.LogFileIndex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -71,7 +72,7 @@ public class LogsFileServiceImpl implements ILogsFileService { } @Override - public List getLogsFileByLogsId(String logsId) { + public List getLogsFileByLogsId(String logsId) { List logsFiles = this.getLogsFileInfoByLogsId(logsId); if (CollectionUtils.isEmpty(logsFiles)) { return new ArrayList<>(); @@ -80,7 +81,7 @@ public class LogsFileServiceImpl implements ILogsFileService { } @Override - public Boolean insertFile(LogsFileUpload file, String logsId) { + public Boolean insertFile(FileUploadStartFile file, String logsId) { LogsFileEntity entity = LogsFileEntity.builder() .logsId(logsId) .fileId(UUID.randomUUID().toString()) @@ -92,8 +93,8 @@ public class LogsFileServiceImpl implements ILogsFileService { if (!insert) { return false; } - for (LogsFile logsFile : file.getList()) { - insert = logsFileIndexService.insertFileIndex(logsFile, file.getDeviceSn(), Integer.valueOf(file.getDeviceModelDomain()), entity.getFileId()); + for (LogFileIndex logsFile : file.getList()) { + insert = logsFileIndexService.insertFileIndex(logsFile, file.getDeviceSn(), Integer.valueOf(file.getModule().getDomain()), entity.getFileId()); if (!insert) { return false; } @@ -120,7 +121,7 @@ public class LogsFileServiceImpl implements ILogsFileService { } @Override - public void updateFile(String logsId, LogsExtFileReceiver fileReceiver) { + public void updateFile(String logsId, FileUploadProgressFile fileReceiver) { List logsFiles = this.getLogsFileInfoByLogsId(logsId); if (CollectionUtils.isEmpty(logsFiles)) { return; @@ -146,15 +147,15 @@ public class LogsFileServiceImpl implements ILogsFileService { return ossService.getObjectUrl(OssConfiguration.bucket, logsFile.getObjectKey()); } - private LogsFileEntity receiver2Entity(LogsExtFileReceiver receiver) { + private LogsFileEntity receiver2Entity(FileUploadProgressFile receiver) { if (Objects.isNull(receiver)) { return null; } return LogsFileEntity.builder() .fingerprint(receiver.getFingerprint()) .size(receiver.getSize()) - .status(Objects.nonNull(receiver.getProgress()) && - EventsResultStatusEnum.OK.getDesc().equals(receiver.getProgress().getStatus())) + .status(Objects.nonNull(receiver.getProgress()) + && FileUploadStatusEnum.OK == receiver.getProgress().getStatus()) .name(receiver.getKey().substring(receiver.getKey().lastIndexOf("/") + 1)).build(); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/manage/service/impl/RequestConfigContext.java b/src/main/java/com/dji/sample/manage/service/impl/RequestConfigContext.java index 5665145..85c448e 100644 --- a/src/main/java/com/dji/sample/manage/service/impl/RequestConfigContext.java +++ b/src/main/java/com/dji/sample/manage/service/impl/RequestConfigContext.java @@ -1,14 +1,15 @@ package com.dji.sample.manage.service.impl; -import com.dji.sample.common.util.SpringBeanUtils; -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.IMessageSenderService; -import com.dji.sample.manage.model.receiver.RequestConfigReceiver; -import com.dji.sample.manage.service.IRequestsConfigService; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.integration.mqtt.support.MqttHeaders; +import com.dji.sample.common.error.CommonErrorEnum; +import com.dji.sample.common.util.SpringBeanUtilsTest; +import com.dji.sample.manage.model.dto.ProductConfigDTO; +import com.dji.sample.manage.model.enums.CustomizeConfigScopeEnum; +import com.dji.sdk.cloudapi.config.ProductConfigResponse; +import com.dji.sdk.cloudapi.config.RequestsConfigRequest; +import com.dji.sdk.cloudapi.config.api.AbstractConfigService; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; @@ -20,37 +21,22 @@ import java.util.Optional; * @date 2022/11/10 */ @Service -public class RequestConfigContext { +public class RequestConfigContext extends AbstractConfigService { - @Autowired - private IMessageSenderService messageSenderService; - - @Autowired - private ObjectMapper objectMapper; - - - /** - * Handles the config method of the requests topic. - * @param receiver - * @param headers - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_CONFIG, outputChannel = ChannelName.OUTBOUND) - void handleConfig(CommonTopicReceiver receiver, MessageHeaders headers) { - RequestConfigReceiver configReceiver = objectMapper.convertValue(receiver.getData(), RequestConfigReceiver.class); - Optional scopeEnumOpt = ConfigScopeEnum.find(configReceiver.getConfigScope()); - String topic = headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF; - CommonTopicResponse.CommonTopicResponseBuilder build = CommonTopicResponse.builder() - .tid(receiver.getTid()) - .bid(receiver.getBid()) - .timestamp(System.currentTimeMillis()) - .method(receiver.getMethod()); + @Override + public TopicRequestsResponse requestsConfig(TopicRequestsRequest request, MessageHeaders headers) { + RequestsConfigRequest configReceiver = request.getData(); + Optional scopeEnumOpt = CustomizeConfigScopeEnum.find(configReceiver.getConfigScope().getScope()); if (scopeEnumOpt.isEmpty()) { - messageSenderService.publish(topic, build.build()); - return; + return new TopicRequestsResponse().setData(MqttReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); } - IRequestsConfigService requestsConfigService = SpringBeanUtils.getBean(scopeEnumOpt.get().getClazz()); - build.data(requestsConfigService.getConfig()); - messageSenderService.publish(topic, build.build()); + ProductConfigDTO config = (ProductConfigDTO) SpringBeanUtilsTest.getBean(scopeEnumOpt.get().getClazz()).getConfig(); + return new TopicRequestsResponse().setData( + new ProductConfigResponse() + .setNtpServerHost(config.getNtpServerHost()) + .setAppId(config.getAppId()) + .setAppKey(config.getAppKey()) + .setAppLicense(config.getAppLicense())); } } diff --git a/src/main/java/com/dji/sample/manage/service/impl/SDKDeviceService.java b/src/main/java/com/dji/sample/manage/service/impl/SDKDeviceService.java new file mode 100644 index 0000000..81db422 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/service/impl/SDKDeviceService.java @@ -0,0 +1,497 @@ +package com.dji.sample.manage.service.impl; + +import com.dji.sample.component.websocket.model.BizCodeEnum; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.model.dto.DevicePayloadReceiver; +import com.dji.sample.manage.model.enums.DeviceFirmwareStatusEnum; +import com.dji.sample.manage.model.param.DeviceQueryParam; +import com.dji.sample.manage.service.IDeviceDictionaryService; +import com.dji.sample.manage.service.IDevicePayloadService; +import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sample.manage.service.IDeviceService; +import com.dji.sdk.cloudapi.device.*; +import com.dji.sdk.cloudapi.device.api.AbstractDeviceService; +import com.dji.sdk.cloudapi.tsa.DeviceIconUrl; +import com.dji.sdk.cloudapi.tsa.IconUrlEnum; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.osd.TopicOsdRequest; +import com.dji.sdk.mqtt.state.TopicStateRequest; +import com.dji.sdk.mqtt.status.TopicStatusRequest; +import com.dji.sdk.mqtt.status.TopicStatusResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/4 + */ +@Service +@Slf4j +public class SDKDeviceService extends AbstractDeviceService { + + @Autowired + private IDeviceRedisService deviceRedisService; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceDictionaryService dictionaryService; + + @Autowired + private IWebSocketMessageService webSocketMessageService; + + @Autowired + private IDevicePayloadService devicePayloadService; + + @Override + public TopicStatusResponse updateTopoOnline(TopicStatusRequest request, MessageHeaders headers) { + UpdateTopoSubDevice updateTopoSubDevice = request.getData().getSubDevices().get(0); + String deviceSn = updateTopoSubDevice.getSn(); + + Optional deviceOpt = deviceRedisService.getDeviceOnline(deviceSn); + Optional gatewayOpt = deviceRedisService.getDeviceOnline(request.getFrom()); + GatewayManager gatewayManager = SDKManager.registerDevice(request.getFrom(), deviceSn, + request.getData().getDomain(), request.getData().getType(), + request.getData().getSubType(), request.getData().getThingVersion(), updateTopoSubDevice.getThingVersion()); + + if (deviceOpt.isPresent() && gatewayOpt.isPresent()) { + deviceOnlineAgain(deviceOpt.get().getWorkspaceId(), request.getFrom(), deviceSn); + return new TopicStatusResponse().setData(MqttReply.success()); + } + + changeSubDeviceParent(deviceSn, request.getFrom()); + + DeviceDTO gateway = deviceGatewayConvertToDevice(request.getFrom(), request.getData()); + Optional gatewayEntityOpt = onlineSaveDevice(gateway, deviceSn, null); + if (gatewayEntityOpt.isEmpty()) { + log.error("Failed to go online, please check the status data or code logic."); + return null; + } + DeviceDTO subDevice = subDeviceConvertToDevice(updateTopoSubDevice); + Optional subDeviceEntityOpt = onlineSaveDevice(subDevice, null, gateway.getDeviceSn()); + if (subDeviceEntityOpt.isEmpty()) { + log.error("Failed to go online, please check the status data or code logic."); + return null; + } + subDevice = subDeviceEntityOpt.get(); + gateway = gatewayEntityOpt.get(); + dockGoOnline(gateway, subDevice); + deviceService.gatewayOnlineSubscribeTopic(gatewayManager); + + if (!StringUtils.hasText(subDevice.getWorkspaceId())) { + return new TopicStatusResponse().setData(MqttReply.success()); + } + + // Subscribe to topic related to drone devices. + deviceService.subDeviceOnlineSubscribeTopic(gatewayManager); + deviceService.pushDeviceOnlineTopo(gateway.getWorkspaceId(), gateway.getDeviceSn(), subDevice.getDeviceSn()); + deviceRedisService.setDeviceOnline(subDevice); + + log.debug("{} online.", subDevice.getDeviceSn()); + return new TopicStatusResponse().setData(MqttReply.success()); + } + + @Override + public TopicStatusResponse updateTopoOffline(TopicStatusRequest request, MessageHeaders headers) { + GatewayManager gatewayManager = SDKManager.registerDevice(request.getFrom(), null, + request.getData().getDomain(), request.getData().getType(), + request.getData().getSubType(), request.getData().getThingVersion(), null); + deviceService.gatewayOnlineSubscribeTopic(gatewayManager); + // Only the remote controller is logged in and the aircraft is not connected. + Optional deviceOpt = deviceRedisService.getDeviceOnline(request.getFrom()); + if (deviceOpt.isEmpty()) { + // When connecting for the first time + DeviceDTO gatewayDevice = deviceGatewayConvertToDevice(request.getFrom(), request.getData()); + Optional gatewayDeviceOpt = onlineSaveDevice(gatewayDevice, null, null); + if (gatewayDeviceOpt.isEmpty()) { + return null; + } + deviceService.pushDeviceOnlineTopo(gatewayDeviceOpt.get().getWorkspaceId(), request.getFrom(), null); + return new TopicStatusResponse().setData(MqttReply.success()); + } + + String deviceSn = deviceOpt.get().getChildDeviceSn(); + if (!StringUtils.hasText(deviceSn)) { + return new TopicStatusResponse().setData(MqttReply.success()); + } + + deviceService.subDeviceOffline(deviceSn); + return new TopicStatusResponse().setData(MqttReply.success()); + } + + @Override + public void osdDock(TopicOsdRequest request, MessageHeaders headers) { + String from = request.getFrom(); + Optional deviceOpt = deviceRedisService.getDeviceOnline(from); + if (deviceOpt.isEmpty()) { + deviceOpt = deviceService.getDeviceBySn(from); + if (deviceOpt.isEmpty()) { + log.error("Please restart the drone."); + return; + } + + if (!StringUtils.hasText(deviceOpt.get().getWorkspaceId())) { + log.error("Please bind the dock first."); + return; + } + } + + DeviceDTO device = deviceOpt.get(); + deviceRedisService.setDeviceOnline(device); + fillDockOsd(from, request.getData()); + + deviceService.pushOsdDataToWeb(device.getWorkspaceId(), BizCodeEnum.DOCK_OSD, from, request.getData()); + } + + @Override + public void osdDockDrone(TopicOsdRequest request, MessageHeaders headers) { + String from = request.getFrom(); + Optional deviceOpt = deviceRedisService.getDeviceOnline(from); + if (deviceOpt.isEmpty()) { + deviceOpt = deviceService.getDeviceBySn(from); + if (deviceOpt.isEmpty()) { + log.error("Please restart the drone."); + return; + } + } + + if (!StringUtils.hasText(deviceOpt.get().getWorkspaceId())) { + log.error("Please restart the drone."); + return; + } + + DeviceDTO device = deviceOpt.get(); + deviceRedisService.setDeviceOnline(device); + deviceRedisService.setDeviceOsd(from, request.getData()); + + deviceService.pushOsdDataToWeb(device.getWorkspaceId(), BizCodeEnum.DEVICE_OSD, from, request.getData()); + } + + @Override + public void osdRemoteControl(TopicOsdRequest request, MessageHeaders headers) { + String from = request.getFrom(); + Optional deviceOpt = deviceRedisService.getDeviceOnline(from); + if (deviceOpt.isEmpty()) { + deviceOpt = deviceService.getDeviceBySn(from); + if (deviceOpt.isEmpty()) { + log.error("Please restart the drone."); + return; + } + } + DeviceDTO device = deviceOpt.get(); + deviceRedisService.setDeviceOnline(device); + + OsdRemoteControl data = request.getData(); + deviceService.pushOsdDataToPilot(device.getWorkspaceId(), from, + new DeviceOsdHost() + .setLatitude(data.getLatitude()) + .setLongitude(data.getLongitude()) + .setHeight(data.getHeight())); + deviceService.pushOsdDataToWeb(device.getWorkspaceId(), BizCodeEnum.RC_OSD, from, data); + + } + + @Override + public void osdRcDrone(TopicOsdRequest request, MessageHeaders headers) { + String from = request.getFrom(); + Optional deviceOpt = deviceRedisService.getDeviceOnline(from); + if (deviceOpt.isEmpty()) { + deviceOpt = deviceService.getDeviceBySn(from); + if (deviceOpt.isEmpty()) { + log.error("Please restart the drone."); + return; + } + } + if (!StringUtils.hasText(deviceOpt.get().getWorkspaceId())) { + log.error("Please bind the drone first."); + return; + } + + DeviceDTO device = deviceOpt.get(); + deviceRedisService.setDeviceOnline(device); + + OsdRcDrone data = request.getData(); + deviceService.pushOsdDataToPilot(device.getWorkspaceId(), from, + new DeviceOsdHost() + .setLatitude(data.getLatitude()) + .setLongitude(data.getLongitude()) + .setElevation(data.getElevation()) + .setHeight(data.getHeight()) + .setAttitudeHead(data.getAttitudeHead()) + .setElevation(data.getElevation()) + .setHorizontalSpeed(data.getHorizontalSpeed()) + .setVerticalSpeed(data.getVerticalSpeed())); + deviceService.pushOsdDataToWeb(device.getWorkspaceId(), BizCodeEnum.DEVICE_OSD, from, data); + } + + @Override + public void dockFirmwareVersionUpdate(TopicStateRequest request, MessageHeaders headers) { + // If the reported version is empty, it will not be processed to prevent misleading page. + if (!StringUtils.hasText(request.getData().getFirmwareVersion())) { + return; + } + + DeviceDTO device = DeviceDTO.builder() + .deviceSn(request.getFrom()) + .firmwareVersion(request.getData().getFirmwareVersion()) + .firmwareStatus(request.getData().getNeedCompatibleStatus() ? + DeviceFirmwareStatusEnum.UNKNOWN : DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE) + .build(); + boolean isUpd = deviceService.updateDevice(device); + if (!isUpd) { + log.error("Data update of firmware version failed. SN: {}", request.getFrom()); + } + } + + @Override + public void rcFirmwareVersionUpdate(TopicStateRequest request, MessageHeaders headers) { + // If the reported version is empty, it will not be processed to prevent misleading page. + if (!StringUtils.hasText(request.getData().getFirmwareVersion())) { + return; + } + + DeviceDTO device = DeviceDTO.builder() + .deviceSn(request.getFrom()) + .firmwareVersion(request.getData().getFirmwareVersion()) + .build(); + boolean isUpd = deviceService.updateDevice(device); + if (!isUpd) { + log.error("Data update of firmware version failed. SN: {}", request.getFrom()); + } + } + + @Override + public void rcPayloadFirmwareVersionUpdate(TopicStateRequest request, MessageHeaders headers) { + // If the reported version is empty, it will not be processed to prevent misleading page. + if (!StringUtils.hasText(request.getData().getFirmwareVersion())) { + return; + } + + boolean isUpd = devicePayloadService.updateFirmwareVersion(request.getFrom(), request.getData()); + if (!isUpd) { + log.error("Data update of payload firmware version failed. SN: {}", request.getFrom()); + } + } + + @Override + public void dockControlSourceUpdate(TopicStateRequest request, MessageHeaders headers) { + // If the control source is empty, it will not be processed. + if (ControlSourceEnum.UNKNOWN == request.getData().getControlSource()) { + return; + } + Optional deviceOpt = deviceRedisService.getDeviceOnline(request.getFrom()); + if (deviceOpt.isEmpty()) { + return; + } + Optional dockOpt = deviceRedisService.getDeviceOnline(request.getGateway()); + if (dockOpt.isEmpty()) { + return; + } + + deviceService.updateFlightControl(dockOpt.get(), request.getData().getControlSource()); + devicePayloadService.updatePayloadControl(deviceOpt.get(), + request.getData().getPayloads().stream() + .map(p -> DevicePayloadReceiver.builder() + .controlSource(p.getControlSource()) + .payloadIndex(p.getPayloadIndex()) + .sn(p.getSn()) + .deviceSn(request.getFrom()) + .build()).collect(Collectors.toList())); + } + + @Override + public void rcControlSourceUpdate(TopicStateRequest request, MessageHeaders headers) { + // If the control source is empty, it will not be processed. + if (ControlSourceEnum.UNKNOWN == request.getData().getControlSource()) { + return; + } + Optional deviceOpt = deviceRedisService.getDeviceOnline(request.getFrom()); + if (deviceOpt.isEmpty()) { + return; + } + Optional dockOpt = deviceRedisService.getDeviceOnline(request.getGateway()); + if (dockOpt.isEmpty()) { + return; + } + + deviceService.updateFlightControl(dockOpt.get(), request.getData().getControlSource()); + devicePayloadService.updatePayloadControl(deviceOpt.get(), + request.getData().getPayloads().stream() + .map(p -> DevicePayloadReceiver.builder() + .controlSource(p.getControlSource()) + .payloadIndex(p.getPayloadIndex()) + .sn(p.getSn()) + .deviceSn(request.getFrom()) + .build()).collect(Collectors.toList())); + } + + private void dockGoOnline(DeviceDTO gateway, DeviceDTO subDevice) { + if (DeviceDomainEnum.DOCK != gateway.getDomain()) { + return; + } + if (!StringUtils.hasText(gateway.getWorkspaceId())) { + log.error("The dock is not bound, please bind it first and then go online."); + return; + } + if (!Objects.requireNonNullElse(subDevice.getBoundStatus(), false)) { + // Directly bind the drone of the dock to the same workspace as the dock. + deviceService.bindDevice(DeviceDTO.builder().deviceSn(subDevice.getChildDeviceSn()).workspaceId(gateway.getWorkspaceId()).build()); + subDevice.setWorkspaceId(gateway.getWorkspaceId()); + } + } + + private void changeSubDeviceParent(String deviceSn, String gatewaySn) { + List gatewaysList = deviceService.getDevicesByParams( + DeviceQueryParam.builder() + .childSn(deviceSn) + .build()); + gatewaysList.stream() + .filter(gateway -> !gateway.getDeviceSn().equals(gatewaySn)) + .forEach(gateway -> { + gateway.setChildDeviceSn(""); + deviceService.updateDevice(gateway); + deviceRedisService.getDeviceOnline(gateway.getDeviceSn()) + .ifPresent(device -> { + device.setChildDeviceSn(null); + deviceRedisService.setDeviceOnline(device); + }); + }); + } + + + public void deviceOnlineAgain(String workspaceId, String gatewaySn, String deviceSn) { + DeviceDTO device = DeviceDTO.builder().loginTime(LocalDateTime.now()).deviceSn(deviceSn).build(); + DeviceDTO gateway = DeviceDTO.builder() + .loginTime(LocalDateTime.now()) + .deviceSn(gatewaySn) + .childDeviceSn(deviceSn).build(); + deviceService.updateDevice(gateway); + deviceService.updateDevice(device); + gateway = deviceRedisService.getDeviceOnline(gatewaySn).map(g -> { + g.setChildDeviceSn(deviceSn); + return g; + }).get(); + device = deviceRedisService.getDeviceOnline(deviceSn).map(d -> { + d.setParentSn(gatewaySn); + return d; + }).get(); + deviceRedisService.setDeviceOnline(gateway); + deviceRedisService.setDeviceOnline(device); + if (StringUtils.hasText(workspaceId)) { + deviceService.subDeviceOnlineSubscribeTopic(SDKManager.getDeviceSDK(gatewaySn)); + } + + log.warn("{} is already online.", deviceSn); + } + + /** + * Convert the received gateway device object into a database entity object. + * @param gateway + * @return + */ + private DeviceDTO deviceGatewayConvertToDevice(String gatewaySn, UpdateTopo gateway) { + if (null == gateway) { + throw new IllegalArgumentException(); + } + return DeviceDTO.builder() + .deviceSn(gatewaySn) + .subType(gateway.getSubType()) + .type(gateway.getType()) + .thingVersion(gateway.getThingVersion()) + .domain(gateway.getDomain()) + .controlSource(gateway.getSubDevices().isEmpty() ? null : + ControlSourceEnum.find(gateway.getSubDevices().get(0).getIndex().getControlSource())) + .build(); + } + + /** + * Convert the received drone device object into a database entity object. + * @param device + * @return + */ + private DeviceDTO subDeviceConvertToDevice(UpdateTopoSubDevice device) { + if (null == device) { + throw new IllegalArgumentException(); + } + return DeviceDTO.builder() + .deviceSn(device.getSn()) + .type(device.getType()) + .subType(device.getSubType()) + .thingVersion(device.getThingVersion()) + .domain(device.getDomain()) + .build(); + } + + private Optional onlineSaveDevice(DeviceDTO device, String childSn, String parentSn) { + + device.setChildDeviceSn(childSn); + device.setLoginTime(LocalDateTime.now()); + + Optional deviceOpt = deviceService.getDeviceBySn(device.getDeviceSn()); + + if (deviceOpt.isEmpty()) { + device.setIconUrl(new DeviceIconUrl()); + // Set the icon of the gateway device displayed in the pilot's map, required in the TSA module. + device.getIconUrl().setNormalIconUrl(IconUrlEnum.NORMAL_PERSON.getUrl()); + // Set the icon of the gateway device displayed in the pilot's map when it is selected, required in the TSA module. + device.getIconUrl().setSelectIconUrl(IconUrlEnum.SELECT_PERSON.getUrl()); + device.setBoundStatus(false); + + // Query the model information of this gateway device. + dictionaryService.getOneDictionaryInfoByTypeSubType( + device.getDomain().getDomain(), device.getType().getType(), device.getSubType().getSubType()) + .ifPresent(entity -> { + device.setDeviceName(entity.getDeviceName()); + device.setNickname(entity.getDeviceName()); + device.setDeviceDesc(entity.getDeviceDesc()); + }); + } + boolean success = deviceService.saveOrUpdateDevice(device); + if (!success) { + return Optional.empty(); + } + + deviceOpt = deviceService.getDeviceBySn(device.getDeviceSn()); + DeviceDTO redisDevice = deviceOpt.get(); + redisDevice.setStatus(true); + redisDevice.setParentSn(parentSn); + + deviceRedisService.setDeviceOnline(redisDevice); + return deviceOpt; + } + + private void fillDockOsd(String dockSn, OsdDock dock) { + Optional oldDockOpt = deviceRedisService.getDeviceOsd(dockSn, OsdDock.class); + if (Objects.nonNull(dock.getJobNumber())) { + return; + } + if (oldDockOpt.isEmpty()) { + deviceRedisService.setDeviceOsd(dockSn, dock); + return; + } + OsdDock oldDock = oldDockOpt.get(); + if (Objects.nonNull(dock.getModeCode())) { + dock.setDrcState(oldDock.getDrcState()); + deviceRedisService.setDeviceOsd(dockSn, dock); + return; + } + if (Objects.nonNull(dock.getDrcState()) ) { + oldDock.setDrcState(dock.getDrcState()); + deviceRedisService.setDeviceOsd(dockSn, oldDock); + } + } +} diff --git a/src/main/java/com/dji/sample/manage/service/impl/SDKLivestreamService.java b/src/main/java/com/dji/sample/manage/service/impl/SDKLivestreamService.java new file mode 100644 index 0000000..d19e61d --- /dev/null +++ b/src/main/java/com/dji/sample/manage/service/impl/SDKLivestreamService.java @@ -0,0 +1,49 @@ +package com.dji.sample.manage.service.impl; + +import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; +import com.dji.sample.manage.service.ICapacityCameraService; +import com.dji.sdk.cloudapi.livestream.DockLivestreamAbilityUpdate; +import com.dji.sdk.cloudapi.livestream.RcLivestreamAbilityUpdate; +import com.dji.sdk.cloudapi.livestream.api.AbstractLivestreamService; +import com.dji.sdk.mqtt.state.TopicStateRequest; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/6 + */ +@Service +public class SDKLivestreamService extends AbstractLivestreamService { + + @Autowired + private ICapacityCameraService capacityCameraService; + + @Autowired + private ObjectMapper objectMapper; + + @Override + public void dockLivestreamAbilityUpdate(TopicStateRequest request, MessageHeaders headers) { + saveLiveCapacity(request.getData().getLiveCapacity().getDeviceList()); + } + + @Override + public void rcLivestreamAbilityUpdate(TopicStateRequest request, MessageHeaders headers) { + saveLiveCapacity(request.getData().getLiveCapacity().getDeviceList()); + } + + private void saveLiveCapacity(Object data) { + List devices = objectMapper.convertValue( + data, new TypeReference>() {}); + for (CapacityDeviceReceiver capacityDeviceReceiver : devices) { + capacityCameraService.saveCapacityCameraReceiverList( + capacityDeviceReceiver.getCameraList(), capacityDeviceReceiver.getSn()); + } + } +} diff --git a/src/main/java/com/dji/sample/manage/service/impl/SDKLogService.java b/src/main/java/com/dji/sample/manage/service/impl/SDKLogService.java new file mode 100644 index 0000000..7edd96e --- /dev/null +++ b/src/main/java/com/dji/sample/manage/service/impl/SDKLogService.java @@ -0,0 +1,14 @@ +package com.dji.sample.manage.service.impl; + +import com.dji.sdk.cloudapi.log.api.AbstractLogService; +import org.springframework.stereotype.Service; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/6 + */ +@Service +public class SDKLogService extends AbstractLogService { + +} diff --git a/src/main/java/com/dji/sample/manage/service/impl/SDKOrganizationService.java b/src/main/java/com/dji/sample/manage/service/impl/SDKOrganizationService.java new file mode 100644 index 0000000..5d019f4 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/service/impl/SDKOrganizationService.java @@ -0,0 +1,177 @@ +package com.dji.sample.manage.service.impl; + +import com.dji.sample.common.error.CommonErrorEnum; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; +import com.dji.sample.manage.model.dto.WorkspaceDTO; +import com.dji.sample.manage.service.IDeviceDictionaryService; +import com.dji.sample.manage.service.IDeviceService; +import com.dji.sample.manage.service.IWorkspaceService; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.dji.sdk.cloudapi.organization.*; +import com.dji.sdk.cloudapi.organization.api.AbstractOrganizationService; +import com.dji.sdk.cloudapi.tsa.DeviceIconUrl; +import com.dji.sdk.cloudapi.tsa.IconUrlEnum; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/7 + */ +@Service +public class SDKOrganizationService extends AbstractOrganizationService { + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceDictionaryService dictionaryService; + + @Autowired + private IWorkspaceService workspaceService; + + @Override + public TopicRequestsResponse> airportBindStatus(TopicRequestsRequest request, MessageHeaders headers) { + List data = request.getData().getDevices(); + + List bindStatusResult = new ArrayList<>(); + for (BindStatusResponseDevice bindStatus : data) { + Optional deviceOpt = deviceService.getDeviceBySn(bindStatus.getSn()); + bindStatusResult.add(deviceOpt.isPresent() ? dto2BindStatus(deviceOpt.get()) : + new BindStatusRequestDevice().setSn(bindStatus.getSn()).setDeviceBindOrganization(false)); + } + return new TopicRequestsResponse>() + .setData(MqttReply.success(new AirportBindStatusResponse().setBindStatus(bindStatusResult))); + } + + @Override + public TopicRequestsResponse> airportOrganizationGet(TopicRequestsRequest request, MessageHeaders headers) { + AirportOrganizationGetRequest organizationGet = request.getData(); + if (!StringUtils.hasText(organizationGet.getDeviceBindingCode())) { + return new TopicRequestsResponse().setData(MqttReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); + } + + Optional workspace = workspaceService.getWorkspaceNameByBindCode(organizationGet.getDeviceBindingCode()); + if (workspace.isEmpty()) { + return new TopicRequestsResponse().setData(MqttReply.error(CommonErrorEnum.GET_ORGANIZATION_FAILED)); + } + + return new TopicRequestsResponse>() + .setData(MqttReply.success(new AirportOrganizationGetResponse() + .setOrganizationName(workspace.get().getWorkspaceName()))); + } + + @Override + public TopicRequestsResponse> airportOrganizationBind(TopicRequestsRequest request, MessageHeaders headers) { + List devices = request.getData().getBindDevices(); + OrganizationBindDevice dock = null; + OrganizationBindDevice drone = null; + for (OrganizationBindDevice device : devices) { + DeviceDomainEnum val = device.getDeviceModelKey().getDomain(); + if (val == DeviceDomainEnum.DOCK) { + dock = device; + } + if (val == DeviceDomainEnum.DRONE) { + drone = device; + } + } + + Optional dockOpt = bindDevice2Dto(dock); + Optional droneOpt = bindDevice2Dto(drone); + List bindResult = new ArrayList<>(); + + droneOpt.ifPresent(droneDto -> { + dockOpt.get().setChildDeviceSn(droneDto.getDeviceSn()); + boolean success = deviceService.saveOrUpdateDevice(droneDto); + bindResult.add(success ? + OrganizationBindInfo.success(droneDto.getDeviceSn()) : + new OrganizationBindInfo(droneDto.getDeviceSn(), + CommonErrorEnum.DEVICE_BINDING_FAILED.getCode()) + ); + }); + boolean success = deviceService.saveOrUpdateDevice(dockOpt.get()); + + bindResult.add(success ? + OrganizationBindInfo.success(dock.getSn()) : + new OrganizationBindInfo(dock.getSn(), + CommonErrorEnum.DEVICE_BINDING_FAILED.getCode())); + + return new TopicRequestsResponse>() + .setData(MqttReply.success(new AirportOrganizationBindResponse().setErrInfos(bindResult))); + } + + /** + * Convert device binding data object into database entity object. + * + * @param receiver + * @return + */ + public Optional bindDevice2Dto(OrganizationBindDevice receiver) { + if (receiver == null) { + return Optional.empty(); + } + + DeviceEnum deviceModelKey = receiver.getDeviceModelKey(); + Optional dictionaryOpt = dictionaryService.getOneDictionaryInfoByTypeSubType( + deviceModelKey.getDomain().getDomain(), deviceModelKey.getType().getType(), + deviceModelKey.getSubType().getSubType()); + DeviceDTO.DeviceDTOBuilder builder = DeviceDTO.builder(); + + dictionaryOpt.ifPresent(entity -> + builder.deviceName(entity.getDeviceName()) + .nickname(entity.getDeviceName()) + .deviceDesc(entity.getDeviceDesc())); + Optional workspace = workspaceService.getWorkspaceNameByBindCode(receiver.getDeviceBindingCode()); + + DeviceDTO dto = builder + .workspaceId(workspace.map(WorkspaceDTO::getWorkspaceId).orElse(receiver.getOrganizationId())) + .domain(deviceModelKey.getDomain()) + .type(deviceModelKey.getType()) + .subType(deviceModelKey.getSubType()) + .deviceSn(receiver.getSn()) + .boundStatus(true) + .loginTime(LocalDateTime.now()) + .boundTime(LocalDateTime.now()) + .iconUrl(new DeviceIconUrl() + .setSelectIconUrl(IconUrlEnum.SELECT_EQUIPMENT.getUrl()) + .setNormalIconUrl(IconUrlEnum.NORMAL_EQUIPMENT.getUrl())) + .build(); + if (StringUtils.hasText(receiver.getDeviceCallsign())) { + dto.setNickname(receiver.getDeviceCallsign()); + } else { + Optional deviceOpt = deviceService.getDeviceBySn(receiver.getSn()); + dto.setNickname(deviceOpt.map(DeviceDTO::getNickname).orElse(dto.getNickname())); + } + return Optional.of(dto); + } + + /** + * Convert device data transfer object into device binding status data object. + * @param device + * @return + */ + private BindStatusRequestDevice dto2BindStatus(DeviceDTO device) { + if (device == null) { + return null; + } + return new BindStatusRequestDevice() + .setSn(device.getDeviceSn()) + .setDeviceCallsign(device.getNickname()) + .setDeviceBindOrganization(device.getBoundStatus()) + .setOrganizationId(device.getWorkspaceId()) + .setOrganizationName(device.getWorkspaceName()); + } +} diff --git a/src/main/java/com/dji/sample/manage/service/impl/SDKPropertySetService.java b/src/main/java/com/dji/sample/manage/service/impl/SDKPropertySetService.java new file mode 100644 index 0000000..73ce404 --- /dev/null +++ b/src/main/java/com/dji/sample/manage/service/impl/SDKPropertySetService.java @@ -0,0 +1,14 @@ +package com.dji.sample.manage.service.impl; + +import com.dji.sdk.cloudapi.property.api.AbstractPropertyService; +import org.springframework.stereotype.Service; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/6 + */ +@Service +public class SDKPropertySetService extends AbstractPropertyService { + +} 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 a255068..2a37485 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 @@ -1,12 +1,13 @@ package com.dji.sample.manage.service.impl; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.dto.TopologyDTO; import com.dji.sample.manage.model.dto.TopologyDeviceDTO; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.manage.model.param.DeviceQueryParam; import com.dji.sample.manage.service.IDeviceService; import com.dji.sample.manage.service.ITopologyService; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.tsa.DeviceTopology; +import com.dji.sdk.cloudapi.tsa.TopologyList; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -26,15 +27,15 @@ public class TopologyServiceImpl implements ITopologyService { private IDeviceService deviceService; @Override - public List getDeviceTopology(String workspaceId) { + public List getDeviceTopology(String workspaceId) { // Query the information of all gateway devices in the workspace. List gatewayList = deviceService.getDevicesByParams( DeviceQueryParam.builder() .workspaceId(workspaceId) - .domains(List.of(DeviceDomainEnum.GATEWAY.getVal())) + .domains(List.of(DeviceDomainEnum.REMOTER_CONTROL.getDomain())) .build()); - List topologyList = new ArrayList<>(); + List topologyList = new ArrayList<>(); gatewayList.forEach(device -> this.getDeviceTopologyByGatewaySn(device.getDeviceSn()) .ifPresent(topologyList::add)); @@ -42,21 +43,22 @@ public class TopologyServiceImpl implements ITopologyService { return topologyList; } - public Optional getDeviceTopologyByGatewaySn(String gatewaySn) { + public Optional getDeviceTopologyByGatewaySn(String gatewaySn) { Optional dtoOptional = deviceService.getDeviceBySn(gatewaySn); if (dtoOptional.isEmpty()) { return Optional.empty(); } - List parents = new ArrayList<>(); + List parents = new ArrayList<>(); DeviceDTO device = dtoOptional.get(); - TopologyDeviceDTO gateway = deviceService.deviceConvertToTopologyDTO(device); + DeviceTopology gateway = deviceService.deviceConvertToTopologyDTO(device); parents.add(gateway); // Query the topology data of the drone based on the drone sn. Optional deviceTopo = deviceService.getDeviceTopoForPilot(device.getChildDeviceSn()); - List deviceTopoList = new ArrayList<>(); + List deviceTopoList = new ArrayList<>(); deviceTopo.ifPresent(deviceTopoList::add); - return Optional.ofNullable(TopologyDTO.builder().parents(parents).hosts(deviceTopoList).build()); + return Optional.ofNullable(new TopologyList().setParents(parents).setHosts(deviceTopoList)); } + } 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 d754831..d466897 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 @@ -8,11 +8,8 @@ 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.component.mqtt.config.MqttPropertyConfiguration; import com.dji.sample.manage.dao.IUserMapper; import com.dji.sample.manage.model.dto.UserDTO; import com.dji.sample.manage.model.dto.UserListDTO; @@ -21,6 +18,9 @@ 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 com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.Pagination; +import com.dji.sdk.common.PaginationData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @@ -43,54 +43,50 @@ public class UserServiceImpl implements IUserService { private IUserMapper mapper; @Autowired - private MqttConfiguration mqttConfiguration; + private MqttPropertyConfiguration mqttPropertyConfiguration; @Autowired private IWorkspaceService workspaceService; @Override - public ResponseResult getUserByUsername(String username, String workspaceId) { + public HttpResultResponse getUserByUsername(String username, String workspaceId) { UserEntity userEntity = this.getUserByUsername(username); if (userEntity == null) { - return ResponseResult.builder() - .code(HttpStatus.UNAUTHORIZED.value()) - .message("invalid username") - .build(); + return new HttpResultResponse() + .setCode(HttpStatus.UNAUTHORIZED.value()) + .setMessage("invalid username"); } UserDTO user = this.entityConvertToDTO(userEntity); user.setWorkspaceId(workspaceId); - return ResponseResult.success(user); + return HttpResultResponse.success(user); } @Override - public ResponseResult userLogin(String username, String password, Integer flag) { + public HttpResultResponse userLogin(String username, String password, Integer flag) { // check user UserEntity userEntity = this.getUserByUsername(username); if (userEntity == null) { - return ResponseResult.builder() - .code(HttpStatus.UNAUTHORIZED.value()) - .message("invalid username") - .build(); + return new HttpResultResponse() + .setCode(HttpStatus.UNAUTHORIZED.value()) + .setMessage("invalid username"); } if (flag.intValue() != userEntity.getUserType().intValue()) { - return ResponseResult.error("The account type does not match."); + return HttpResultResponse.error("The account type does not match."); } if (!password.equals(userEntity.getPassword())) { - return ResponseResult.builder() - .code(HttpStatus.UNAUTHORIZED.value()) - .message("invalid password") - .build(); + return new HttpResultResponse() + .setCode(HttpStatus.UNAUTHORIZED.value()) + .setMessage("invalid password"); } Optional workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(userEntity.getWorkspaceId()); if (workspaceOpt.isEmpty()) { - return ResponseResult.builder() - .code(HttpStatus.UNAUTHORIZED.value()) - .message("invalid workspace id") - .build(); + return new HttpResultResponse() + .setCode(HttpStatus.UNAUTHORIZED.value()) + .setMessage("invalid workspace id"); } CustomClaim customClaim = new CustomClaim(userEntity.getUserId(), @@ -101,10 +97,10 @@ public class UserServiceImpl implements IUserService { String token = JwtUtil.createToken(customClaim.convertToMap()); UserDTO userDTO = entityConvertToDTO(userEntity); - userDTO.setMqttAddr(MqttConfiguration.getBasicMqttAddress()); + userDTO.setMqttAddr(MqttPropertyConfiguration.getBasicMqttAddress()); userDTO.setAccessToken(token); userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId()); - return ResponseResult.success(userDTO); + return HttpResultResponse.success(userDTO); } @Override @@ -212,7 +208,7 @@ public class UserServiceImpl implements IUserService { .userType(entity.getUserType()) .mqttUsername(entity.getMqttUsername()) .mqttPassword(entity.getMqttPassword()) - .mqttAddr(MqttConfiguration.getBasicMqttAddress()) + .mqttAddr(MqttPropertyConfiguration.getBasicMqttAddress()) .build(); } } 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 b90d262..ff492b2 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,24 +1,15 @@ 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 @@ -31,9 +22,6 @@ public class WorkspaceServiceImpl implements IWorkspaceService { @Autowired private ObjectMapper objectMapper; - @Autowired - private IMessageSenderService messageSenderService; - @Override public Optional getWorkspaceByWorkspaceId(String workspaceId) { return Optional.ofNullable(entityConvertToDto( @@ -48,35 +36,6 @@ public class WorkspaceServiceImpl implements IWorkspaceService { 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 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 1970479..ce65c19 100644 --- a/src/main/java/com/dji/sample/map/controller/WorkspaceElementController.java +++ b/src/main/java/com/dji/sample/map/controller/WorkspaceElementController.java @@ -1,19 +1,23 @@ package com.dji.sample.map.controller; import com.dji.sample.common.model.CustomClaim; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.websocket.model.BizCodeEnum; -import com.dji.sample.component.websocket.service.ISendMessageService; -import com.dji.sample.map.model.dto.*; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.map.service.IWorkspaceElementService; +import com.dji.sdk.cloudapi.map.CreateMapElementRequest; +import com.dji.sdk.cloudapi.map.CreateMapElementResponse; +import com.dji.sdk.cloudapi.map.GetMapElementsResponse; +import com.dji.sdk.cloudapi.map.UpdateMapElementRequest; +import com.dji.sdk.cloudapi.map.api.IHttpMapService; +import com.dji.sdk.common.HttpResultResponse; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; @@ -23,14 +27,26 @@ import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; * @date 2021/11/29 */ @RestController -@RequestMapping("${url.map.prefix}${url.map.version}/workspaces") -public class WorkspaceElementController { +public class WorkspaceElementController implements IHttpMapService { @Autowired private IWorkspaceElementService elementService; @Autowired - private ISendMessageService sendMessageService; + private IWebSocketMessageService sendMessageService; + + /** + * Delete all the element information in this group based on the group id. + * @param workspaceId + * @param groupId + * @return + */ + @DeleteMapping("${url.map.prefix}${url.map.version}/workspaces/{workspace_id}/element-groups/{group_id}/elements") + public HttpResultResponse deleteAllElementByGroupId(@PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "group_id") String groupId) { + + return elementService.deleteAllElementByGroupId(workspaceId, groupId); + } /** * In the first connection, pilot will send out this http request to obtain the group element list. @@ -41,71 +57,52 @@ public class WorkspaceElementController { * @param isDistributed * @return */ - @GetMapping("/{workspace_id}/element-groups") - public ResponseResult> getAllElements(@PathVariable(name = "workspace_id") String workspaceId, - @RequestParam(name = "group_id", required = false) String groupId, - @RequestParam(name = "is_distributed", required = false) Boolean isDistributed) { - List groupsList = elementService.getAllGroupsByWorkspaceId(workspaceId, groupId, isDistributed); - return ResponseResult.success(groupsList); + @Override + public HttpResultResponse> getMapElements(String workspaceId, String groupId, Boolean isDistributed, HttpServletRequest req, HttpServletResponse rsp) { + List groupsList = elementService.getAllGroupsByWorkspaceId(workspaceId, groupId, isDistributed); + return HttpResultResponse.>success(groupsList); } /** * When user draws a point, line or polygon on the PILOT/Web side. * Save the element information to the database. - * @param request * @param workspaceId * @param groupId * @param elementCreate * @return */ - @PostMapping("/{workspace_id}/element-groups/{group_id}/elements") - public ResponseResult saveElement(HttpServletRequest request, - @PathVariable(name = "workspace_id") String workspaceId, - @PathVariable(name = "group_id") String groupId, - @RequestBody ElementCreateDTO elementCreate) { - CustomClaim claims = (CustomClaim) request.getAttribute(TOKEN_CLAIM); + @Override + public HttpResultResponse createMapElement(String workspaceId, String groupId, + @Valid CreateMapElementRequest elementCreate, HttpServletRequest req, HttpServletResponse rsp) { + CustomClaim claims = (CustomClaim) req.getAttribute(TOKEN_CLAIM); // Set the creator of the element elementCreate.getResource().setUsername(claims.getUsername()); - ResponseResult response = elementService.saveElement(groupId, elementCreate); - if (response.getCode() != ResponseResult.CODE_SUCCESS) { + HttpResultResponse response = elementService.saveElement(workspaceId, groupId, elementCreate); + if (response.getCode() != HttpResultResponse.CODE_SUCCESS) { return response; } - // Notify all WebSocket connections in this workspace to be updated when an element is created. - elementService.getElementByElementId(elementCreate.getId()) - .ifPresent(groupElement -> sendMessageService.sendBatch( - workspaceId, BizCodeEnum.MAP_ELEMENT_CREATE.getCode(), groupElement)); - - return ResponseResult.success(new ConcurrentHashMap<>(Map.of("id", elementCreate.getId()))); + return HttpResultResponse.success(new CreateMapElementResponse().setId(elementCreate.getId())); } /** * When user edits a point, line or polygon on the PILOT/Web side. * Update the element information to the database. - * @param request * @param workspaceId * @param elementId * @param elementUpdate * @return */ - @PutMapping("/{workspace_id}/elements/{element_id}") - public ResponseResult updateElement(HttpServletRequest request, - @PathVariable(name = "workspace_id") String workspaceId, - @PathVariable(name = "element_id") String elementId, - @RequestBody ElementUpdateDTO elementUpdate) { + @Override + public HttpResultResponse updateMapElement(String workspaceId, String elementId, @Valid UpdateMapElementRequest elementUpdate, HttpServletRequest req, HttpServletResponse rsp) { + CustomClaim claims = (CustomClaim) req.getAttribute(TOKEN_CLAIM); - CustomClaim claims = (CustomClaim) request.getAttribute(TOKEN_CLAIM); - - ResponseResult response = elementService.updateElement(elementId, elementUpdate, claims.getUsername()); - if (response.getCode() != ResponseResult.CODE_SUCCESS) { + HttpResultResponse response = elementService.updateElement(workspaceId, elementId, elementUpdate, claims.getUsername()); + if (response.getCode() != HttpResultResponse.CODE_SUCCESS) { return response; } - // Notify all WebSocket connections in this workspace to update when there is an element update. - elementService.getElementByElementId(elementId) - .ifPresent(groupElement -> sendMessageService.sendBatch( - workspaceId, BizCodeEnum.MAP_ELEMENT_UPDATE.getCode(), groupElement)); return response; } @@ -116,45 +113,9 @@ public class WorkspaceElementController { * @param elementId * @return */ - @DeleteMapping("/{workspace_id}/elements/{element_id}") - public ResponseResult deleteElement(@PathVariable(name = "workspace_id") String workspaceId, - @PathVariable(name = "element_id") String elementId) { - - Optional elementOpt = elementService.getElementByElementId(elementId); - - ResponseResult response = elementService.deleteElement(elementId); - - // Notify all WebSocket connections in this workspace to update when an element is deleted. - if (ResponseResult.CODE_SUCCESS == response.getCode()) { - elementOpt.ifPresent(element -> - sendMessageService.sendBatch(workspaceId, BizCodeEnum.MAP_ELEMENT_DELETE.getCode(), - WebSocketElementDelDTO.builder() - .elementId(elementId) - .groupId(element.getGroupId()) - .build())); - } - return response; - } - - /** - * Delete all the element information in this group based on the group id. - * @param workspaceId - * @param groupId - * @return - */ - @DeleteMapping("/{workspace_id}/element-groups/{group_id}/elements") - public ResponseResult deleteAllElementByGroupId(@PathVariable(name = "workspace_id") String workspaceId, - @PathVariable(name = "group_id") String groupId) { - - ResponseResult response = elementService.deleteAllElementByGroupId(groupId); + @Override + public HttpResultResponse deleteMapElement(String workspaceId, String elementId, HttpServletRequest req, HttpServletResponse rsp) { - // Notify all WebSocket connections in this workspace to update when elements are deleted. - if (ResponseResult.CODE_SUCCESS == response.getCode()) { - - sendMessageService.sendBatch(workspaceId, BizCodeEnum.MAP_GROUP_REFRESH.getCode(), - // Group ids that need to re-request data - new ConcurrentHashMap(Map.of("ids", new String[]{groupId}))); - } - return response; + return elementService.deleteElement(workspaceId, elementId); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/map/model/dto/ContentPropertyDTO.java b/src/main/java/com/dji/sample/map/model/dto/ContentPropertyDTO.java deleted file mode 100644 index e8fe1d2..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ContentPropertyDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dji.sample.map.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/30 - */ -@Builder -@AllArgsConstructor -@NoArgsConstructor -@Data -public class ContentPropertyDTO { - - private String color; - - @JsonProperty("clampToGround") - private Boolean clampToGround; - -} diff --git a/src/main/java/com/dji/sample/map/model/dto/ElementCoordinateDTO.java b/src/main/java/com/dji/sample/map/model/dto/ElementCoordinateDTO.java deleted file mode 100644 index e0799c4..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ElementCoordinateDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dji.sample.map.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/30 - */ -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class ElementCoordinateDTO { - - private Double longitude; - - private Double latitude; - - private Double altitude; - -} diff --git a/src/main/java/com/dji/sample/map/model/dto/ElementCreateDTO.java b/src/main/java/com/dji/sample/map/model/dto/ElementCreateDTO.java deleted file mode 100644 index a594bba..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ElementCreateDTO.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.dji.sample.map.model.dto; - -import lombok.Data; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/30 - */ -@Data -public class ElementCreateDTO { - - private String id; - - private String name; - - private ElementResourceDTO resource; -} diff --git a/src/main/java/com/dji/sample/map/model/dto/ElementLineStringDTO.java b/src/main/java/com/dji/sample/map/model/dto/ElementLineStringDTO.java deleted file mode 100644 index 117790b..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ElementLineStringDTO.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.dji.sample.map.model.dto; - -import com.dji.sample.map.model.enums.ElementTypeEnum; -import lombok.Data; -import org.springframework.util.CollectionUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/30 - */ -@Data -public class ElementLineStringDTO extends ElementType { - - private Double[][] coordinates; - - public ElementLineStringDTO() { - super(ElementTypeEnum.LINE_STRING.getDesc()); - } - - @Override - public List convertToList() { - List coordinateList = new ArrayList<>(); - for (Double[] coordinate : this.coordinates) { - coordinateList.add(ElementCoordinateDTO.builder() - .longitude(coordinate[0]) - .latitude(coordinate[1]) - .build()); - } - return coordinateList; - } - - @Override - public void adapterCoordinateType(List coordinateList) { - if (CollectionUtils.isEmpty(coordinateList)) { - return; - } - this.coordinates = new Double[coordinateList.size()][2]; - for (int i = 0; i < this.coordinates.length; i++) { - this.coordinates[i][0] = coordinateList.get(i).getLongitude(); - this.coordinates[i][1] = coordinateList.get(i).getLatitude(); - } - } -} diff --git a/src/main/java/com/dji/sample/map/model/dto/ElementPointDTO.java b/src/main/java/com/dji/sample/map/model/dto/ElementPointDTO.java deleted file mode 100644 index 3eeee7d..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ElementPointDTO.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.dji.sample.map.model.dto; - -import com.dji.sample.map.model.enums.ElementTypeEnum; -import lombok.Data; -import org.springframework.util.CollectionUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/30 - */ -@Data -public class ElementPointDTO extends ElementType { - - private Double[] coordinates; - - public ElementPointDTO() { - super(ElementTypeEnum.POINT.getDesc()); - } - - @Override - public List convertToList() { - List coordinateList = new ArrayList<>(); - coordinateList.add(ElementCoordinateDTO.builder() - .longitude(this.coordinates[0]) - .latitude(this.coordinates[1]) - .altitude(this.coordinates.length == 3 ? this.coordinates[2] : null) - .build()); - return coordinateList; - } - - @Override - public void adapterCoordinateType(List coordinateList) { - if (CollectionUtils.isEmpty(coordinateList)) { - return; - } - this.coordinates = new Double[]{ - coordinateList.get(0).getLongitude(), - coordinateList.get(0).getLatitude(), - coordinateList.get(0).getAltitude() - }; - } -} diff --git a/src/main/java/com/dji/sample/map/model/dto/ElementPolygonDTO.java b/src/main/java/com/dji/sample/map/model/dto/ElementPolygonDTO.java deleted file mode 100644 index 35a8d35..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ElementPolygonDTO.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.dji.sample.map.model.dto; - -import com.dji.sample.map.model.enums.ElementTypeEnum; -import lombok.Data; -import org.springframework.util.CollectionUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/30 - */ -@Data -public class ElementPolygonDTO extends ElementType { - - private Double[][][] coordinates; - - public ElementPolygonDTO() { - super(ElementTypeEnum.POLYGON.getDesc()); - } - - @Override - public List convertToList() { - List coordinateList = new ArrayList<>(); - - for (Double[] coordinate : this.coordinates[0]) { - coordinateList.add(ElementCoordinateDTO.builder() - .longitude(coordinate[0]) - .latitude(coordinate[1]) - .build()); - } - return coordinateList; - } - - @Override - public void adapterCoordinateType(List coordinateList) { - if (CollectionUtils.isEmpty(coordinateList)) { - return; - } - this.coordinates = new Double[1][coordinateList.size()][2]; - for (int i = 0; i < this.coordinates[0].length; i++) { - this.coordinates[0][i][0] = coordinateList.get(i).getLongitude(); - this.coordinates[0][i][1] = coordinateList.get(i).getLatitude(); - } - } -} diff --git a/src/main/java/com/dji/sample/map/model/dto/ElementResourceDTO.java b/src/main/java/com/dji/sample/map/model/dto/ElementResourceDTO.java deleted file mode 100644 index b89df70..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ElementResourceDTO.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.map.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/29 - */ -@Builder -@AllArgsConstructor -@NoArgsConstructor -@Data -public class ElementResourceDTO { - - private Integer type; - - @JsonProperty(value = "user_name") - private String username; - - private ResourceContentDTO content; -} diff --git a/src/main/java/com/dji/sample/map/model/dto/ElementType.java b/src/main/java/com/dji/sample/map/model/dto/ElementType.java deleted file mode 100644 index 1fdf3f6..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ElementType.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.dji.sample.map.model.dto; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/30 - */ -@Data -@NoArgsConstructor -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", - include = JsonTypeInfo.As.EXISTING_PROPERTY, defaultImpl = ElementType.class) -@JsonSubTypes({ - @JsonSubTypes.Type(value = ElementPointDTO.class, name = "Point"), - @JsonSubTypes.Type(value = ElementLineStringDTO.class, name = "LineString"), - @JsonSubTypes.Type(value = ElementPolygonDTO.class, name = "Polygon") -}) -public abstract class ElementType { - - private String type; - - ElementType(String type) { - this.type = type; - } - - /** - * Convert coordinate data into objects and then add them to the collection. - * @return - */ - public abstract List convertToList(); - - /** - * Converts coordinates in a collection of objects to a specific type. - * @param coordinateList - */ - public abstract void adapterCoordinateType(List coordinateList); -} diff --git a/src/main/java/com/dji/sample/map/model/dto/ElementUpdateDTO.java b/src/main/java/com/dji/sample/map/model/dto/ElementUpdateDTO.java deleted file mode 100644 index 58897e2..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ElementUpdateDTO.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.dji.sample.map.model.dto; - -import lombok.Data; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/1 - */ -@Data -public class ElementUpdateDTO { - - private String name; - - private ResourceContentDTO content; - -} diff --git a/src/main/java/com/dji/sample/map/model/dto/GroupDTO.java b/src/main/java/com/dji/sample/map/model/dto/GroupDTO.java deleted file mode 100644 index 1c75e67..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/GroupDTO.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.dji.sample.map.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/29 - */ -@AllArgsConstructor -@NoArgsConstructor -@Builder -@Data -public class GroupDTO { - - private String id; - - private String name; - - private Integer type; - - private List elements; - - @JsonProperty(value = "is_distributed") - private Boolean isDistributed; - - @JsonProperty(value = "is_lock") - private Boolean isLock; - - @JsonProperty(value = "create_time") - private Long createTime; -} diff --git a/src/main/java/com/dji/sample/map/model/dto/GroupElementDTO.java b/src/main/java/com/dji/sample/map/model/dto/GroupElementDTO.java index b48d1b6..70ff1e4 100644 --- a/src/main/java/com/dji/sample/map/model/dto/GroupElementDTO.java +++ b/src/main/java/com/dji/sample/map/model/dto/GroupElementDTO.java @@ -1,5 +1,6 @@ package com.dji.sample.map.model.dto; +import com.dji.sdk.cloudapi.map.ElementResource; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; @@ -18,7 +19,6 @@ import lombok.NoArgsConstructor; @Builder @JsonInclude(JsonInclude.Include.NON_NULL) public class GroupElementDTO { - private Integer display; @JsonProperty("id") private String elementId; @@ -31,7 +31,7 @@ public class GroupElementDTO { @JsonProperty(value = "update_time") private Long updateTime; - private ElementResourceDTO resource; + private ElementResource resource; @JsonProperty("group_id") private String groupId; diff --git a/src/main/java/com/dji/sample/map/model/dto/ResourceContentDTO.java b/src/main/java/com/dji/sample/map/model/dto/ResourceContentDTO.java deleted file mode 100644 index 6ae3c9a..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/ResourceContentDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dji.sample.map.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.2 - * @date 2021/11/30 - */ -@Builder -@AllArgsConstructor -@NoArgsConstructor -@Data -public class ResourceContentDTO { - - @Builder.Default - private String type = "Feature"; - - private ContentPropertyDTO properties; - - private ElementType geometry; -} diff --git a/src/main/java/com/dji/sample/map/model/dto/WebSocketElementDelDTO.java b/src/main/java/com/dji/sample/map/model/dto/WebSocketElementDelDTO.java deleted file mode 100644 index 5d99272..0000000 --- a/src/main/java/com/dji/sample/map/model/dto/WebSocketElementDelDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dji.sample.map.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/2 - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class WebSocketElementDelDTO { - - @JsonProperty("id") - private String elementId; - - @JsonProperty("group_id") - private String groupId; -} diff --git a/src/main/java/com/dji/sample/map/model/enums/ElementTypeEnum.java b/src/main/java/com/dji/sample/map/model/enums/ElementTypeEnum.java index 886ebe2..0b91b61 100644 --- a/src/main/java/com/dji/sample/map/model/enums/ElementTypeEnum.java +++ b/src/main/java/com/dji/sample/map/model/enums/ElementTypeEnum.java @@ -1,9 +1,6 @@ package com.dji.sample.map.model.enums; -import com.dji.sample.map.model.dto.ElementLineStringDTO; -import com.dji.sample.map.model.dto.ElementPointDTO; -import com.dji.sample.map.model.dto.ElementPolygonDTO; -import com.dji.sample.map.model.dto.ElementType; +import com.dji.sdk.cloudapi.map.*; import java.util.Optional; @@ -14,56 +11,51 @@ import java.util.Optional; */ public enum ElementTypeEnum { - POINT(0, "Point"), + POINT(ElementResourceTypeEnum.POINT), - LINE_STRING(1, "LineString"), + LINE_STRING(ElementResourceTypeEnum.LINE_STRING), - POLYGON(2, "Polygon"), + POLYGON(ElementResourceTypeEnum.POLYGON); - UNKNOWN(-1, "Unknown"); + private ElementResourceTypeEnum typeEnum; - private int val; - - private String desc; - - ElementTypeEnum(int val, String desc) { - this.val = val; - this.desc = desc; + ElementTypeEnum(ElementResourceTypeEnum typeEnum) { + this.typeEnum = typeEnum; } - public static Optional findType(int val) { - if (POINT.val == val) { - return Optional.of(new ElementPointDTO()); + public static Optional findType(int val) { + if (POINT.typeEnum.getType() == val) { + return Optional.of(new ElementPointGeometry()); } - if (LINE_STRING.val == val) { - return Optional.of(new ElementLineStringDTO()); + if (LINE_STRING.typeEnum.getType() == val) { + return Optional.of(new ElementLineStringGeometry()); } - if (POLYGON.val == val) { - return Optional.of(new ElementPolygonDTO()); + if (POLYGON.typeEnum.getType() == val) { + return Optional.of(new ElementPolygonGeometry()); } return Optional.empty(); } public String getDesc() { - return desc; + return typeEnum.getTypeName(); } public static int findVal(String desc) { - if (POINT.desc.equals(desc)) { - return POINT.val; + if (POINT.typeEnum.getTypeName().equals(desc)) { + return POINT.typeEnum.getType(); } - if (LINE_STRING.desc.equals(desc)) { - return LINE_STRING.val; + if (LINE_STRING.typeEnum.getTypeName().equals(desc)) { + return LINE_STRING.typeEnum.getType(); } - if (POLYGON.desc.equals(desc)) { - return POLYGON.val; + if (POLYGON.typeEnum.getTypeName().equals(desc)) { + return POLYGON.typeEnum.getType(); } - return UNKNOWN.val; + throw new RuntimeException("unknown type:" + desc); } } diff --git a/src/main/java/com/dji/sample/map/service/IElementCoordinateService.java b/src/main/java/com/dji/sample/map/service/IElementCoordinateService.java index 7668205..5764729 100644 --- a/src/main/java/com/dji/sample/map/service/IElementCoordinateService.java +++ b/src/main/java/com/dji/sample/map/service/IElementCoordinateService.java @@ -1,6 +1,6 @@ package com.dji.sample.map.service; -import com.dji.sample.map.model.dto.ElementCoordinateDTO; +import com.dji.sdk.cloudapi.map.ElementCoordinate; import java.util.List; @@ -16,7 +16,7 @@ public interface IElementCoordinateService { * @param elementId * @return */ - List getCoordinateByElementId(String elementId); + List getCoordinateByElementId(String elementId); /** * Save all the coordinate data of this element. @@ -24,7 +24,7 @@ public interface IElementCoordinateService { * @param elementId * @return */ - Boolean saveCoordinate(List coordinate, String elementId); + Boolean saveCoordinate(List coordinate, String elementId); /** * Delete all the coordinates of the element according to its id. diff --git a/src/main/java/com/dji/sample/map/service/IGroupElementService.java b/src/main/java/com/dji/sample/map/service/IGroupElementService.java index d6513fa..47097aa 100644 --- a/src/main/java/com/dji/sample/map/service/IGroupElementService.java +++ b/src/main/java/com/dji/sample/map/service/IGroupElementService.java @@ -1,8 +1,9 @@ package com.dji.sample.map.service; -import com.dji.sample.map.model.dto.ElementCreateDTO; -import com.dji.sample.map.model.dto.ElementUpdateDTO; import com.dji.sample.map.model.dto.GroupElementDTO; +import com.dji.sdk.cloudapi.map.CreateMapElementRequest; +import com.dji.sdk.cloudapi.map.MapGroupElement; +import com.dji.sdk.cloudapi.map.UpdateMapElementRequest; import java.util.List; import java.util.Optional; @@ -19,7 +20,7 @@ public interface IGroupElementService { * @param groupId * @return */ - List getElementsByGroupId(String groupId); + List getElementsByGroupId(String groupId); /** * Save all the elements. @@ -27,7 +28,7 @@ public interface IGroupElementService { * @param elementCreate * @return */ - Boolean saveElement(String groupId, ElementCreateDTO elementCreate); + Boolean saveElement(String groupId, CreateMapElementRequest elementCreate); /** * Query the element information based on the element id and update element. @@ -36,7 +37,7 @@ public interface IGroupElementService { * @param username * @return */ - Boolean updateElement(String elementId, ElementUpdateDTO elementUpdate, String username); + Boolean updateElement(String elementId, UpdateMapElementRequest elementUpdate, String username); /** * Delete the element based on the element id. diff --git a/src/main/java/com/dji/sample/map/service/IGroupService.java b/src/main/java/com/dji/sample/map/service/IGroupService.java index bead14b..14cefd8 100644 --- a/src/main/java/com/dji/sample/map/service/IGroupService.java +++ b/src/main/java/com/dji/sample/map/service/IGroupService.java @@ -1,6 +1,6 @@ package com.dji.sample.map.service; -import com.dji.sample.map.model.dto.GroupDTO; +import com.dji.sdk.cloudapi.map.GetMapElementsResponse; import java.util.List; @@ -19,6 +19,6 @@ public interface IGroupService { * @param isDistributed Used to define if the group needs to be distributed. Default is true. * @return */ - List getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed); + List getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed); } diff --git a/src/main/java/com/dji/sample/map/service/IWorkspaceElementService.java b/src/main/java/com/dji/sample/map/service/IWorkspaceElementService.java index 4d24769..2d4699f 100644 --- a/src/main/java/com/dji/sample/map/service/IWorkspaceElementService.java +++ b/src/main/java/com/dji/sample/map/service/IWorkspaceElementService.java @@ -1,10 +1,8 @@ package com.dji.sample.map.service; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.map.model.dto.ElementCreateDTO; -import com.dji.sample.map.model.dto.ElementUpdateDTO; -import com.dji.sample.map.model.dto.GroupDTO; import com.dji.sample.map.model.dto.GroupElementDTO; +import com.dji.sdk.cloudapi.map.*; +import com.dji.sdk.common.HttpResultResponse; import java.util.List; import java.util.Optional; @@ -24,34 +22,40 @@ public interface IWorkspaceElementService { * @param isDistributed * @return */ - List getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed); + List getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed); /** * Save all the elements, including the information of the elements in the group, * and the coordinate information in the elements. + * + * @param workspaceId * @param groupId * @param elementCreate * @return */ - ResponseResult saveElement(String groupId, ElementCreateDTO elementCreate); + HttpResultResponse saveElement(String workspaceId, String groupId, CreateMapElementRequest elementCreate); /** * Update the element information based on the element id, * including the information of the elements in the group, and the coordinate information in the elements. + * + * @param workspaceId * @param elementId * @param elementUpdate * @param username * @return */ - ResponseResult updateElement(String elementId, ElementUpdateDTO elementUpdate, String username); + HttpResultResponse updateElement(String workspaceId, String elementId, UpdateMapElementRequest elementUpdate, String username); /** * Delete the element information based on the element id, * including the information of the elements in the group, and the coordinate information in the elements. + * + * @param workspaceId * @param elementId * @return */ - ResponseResult deleteElement(String elementId); + HttpResultResponse deleteElement(String workspaceId, String elementId); /** * Query an element based on the element id, @@ -64,8 +68,14 @@ public interface IWorkspaceElementService { /** * Delete all the elements information based on the group id, * including the information of the elements in the group, and the coordinate information in the elements. + * + * @param workspaceId * @param groupId * @return */ - ResponseResult deleteAllElementByGroupId(String groupId); + HttpResultResponse deleteAllElementByGroupId(String workspaceId, String groupId); + + MapElementCreateWsResponse element2CreateWsElement(GroupElementDTO element); + + MapElementUpdateWsResponse element2UpdateWsElement(GroupElementDTO element); } \ No newline at end of file diff --git a/src/main/java/com/dji/sample/map/service/impl/ElementCoordinateServiceImpl.java b/src/main/java/com/dji/sample/map/service/impl/ElementCoordinateServiceImpl.java index bab9775..beff134 100644 --- a/src/main/java/com/dji/sample/map/service/impl/ElementCoordinateServiceImpl.java +++ b/src/main/java/com/dji/sample/map/service/impl/ElementCoordinateServiceImpl.java @@ -3,9 +3,9 @@ package com.dji.sample.map.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.dji.sample.map.dao.IElementCoordinateMapper; -import com.dji.sample.map.model.dto.ElementCoordinateDTO; import com.dji.sample.map.model.entity.ElementCoordinateEntity; import com.dji.sample.map.service.IElementCoordinateService; +import com.dji.sdk.cloudapi.map.ElementCoordinate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,7 +26,7 @@ public class ElementCoordinateServiceImpl implements IElementCoordinateService { private IElementCoordinateMapper mapper; @Override - public List getCoordinateByElementId(String elementId) { + public List getCoordinateByElementId(String elementId) { return mapper.selectList( new LambdaQueryWrapper() .eq(ElementCoordinateEntity::getElementId, elementId)) @@ -36,8 +36,8 @@ public class ElementCoordinateServiceImpl implements IElementCoordinateService { } @Override - public Boolean saveCoordinate(List coordinateList, String elementId) { - for (ElementCoordinateDTO coordinate : coordinateList) { + public Boolean saveCoordinate(List coordinateList, String elementId) { + for (ElementCoordinate coordinate : coordinateList) { ElementCoordinateEntity entity = this.dtoConvertToEntity(coordinate); entity.setElementId(elementId); @@ -60,17 +60,15 @@ public class ElementCoordinateServiceImpl implements IElementCoordinateService { * @param entity * @return */ - private ElementCoordinateDTO entityConvertToDto(ElementCoordinateEntity entity) { - ElementCoordinateDTO.ElementCoordinateDTOBuilder builder = ElementCoordinateDTO.builder(); + private ElementCoordinate entityConvertToDto(ElementCoordinateEntity entity) { if (entity == null) { - return builder.build(); + return null; } - return builder - .longitude(entity.getLongitude()) - .latitude(entity.getLatitude()) - .altitude(entity.getAltitude()) - .build(); + return new ElementCoordinate() + .setLongitude(entity.getLongitude()) + .setLatitude(entity.getLatitude()) + .setAltitude(entity.getAltitude()); } /** @@ -78,9 +76,8 @@ public class ElementCoordinateServiceImpl implements IElementCoordinateService { * @param coordinate * @return */ - private ElementCoordinateEntity dtoConvertToEntity(ElementCoordinateDTO coordinate) { + private ElementCoordinateEntity dtoConvertToEntity(ElementCoordinate coordinate) { ElementCoordinateEntity.ElementCoordinateEntityBuilder builder = ElementCoordinateEntity.builder(); - if (coordinate == null) { return builder.build(); } diff --git a/src/main/java/com/dji/sample/map/service/impl/GroupElementServiceImpl.java b/src/main/java/com/dji/sample/map/service/impl/GroupElementServiceImpl.java index f516f04..675a0f7 100644 --- a/src/main/java/com/dji/sample/map/service/impl/GroupElementServiceImpl.java +++ b/src/main/java/com/dji/sample/map/service/impl/GroupElementServiceImpl.java @@ -2,11 +2,12 @@ package com.dji.sample.map.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.dji.sample.map.dao.IGroupElementMapper; -import com.dji.sample.map.model.dto.*; +import com.dji.sample.map.model.dto.GroupElementDTO; import com.dji.sample.map.model.entity.GroupElementEntity; import com.dji.sample.map.model.enums.ElementTypeEnum; import com.dji.sample.map.service.IElementCoordinateService; import com.dji.sample.map.service.IGroupElementService; +import com.dji.sdk.cloudapi.map.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -31,15 +32,14 @@ public class GroupElementServiceImpl implements IGroupElementService { private IElementCoordinateService elementCoordinateService; @Override - public List getElementsByGroupId(String groupId) { + public List getElementsByGroupId(String groupId) { List elementList = mapper.selectList( new LambdaQueryWrapper() .eq(GroupElementEntity::getGroupId, groupId)); - List groupElementList = new ArrayList<>(); + List groupElementList = new ArrayList<>(); for (GroupElementEntity elementEntity : elementList) { - - GroupElementDTO groupElement = this.entityConvertToDto(elementEntity); + MapGroupElement groupElement = this.entityConvertToDto(elementEntity); groupElementList.add(groupElement); this.addCoordinateToElement(groupElement, elementEntity); @@ -48,7 +48,7 @@ public class GroupElementServiceImpl implements IGroupElementService { } @Override - public Boolean saveElement(String groupId, ElementCreateDTO elementCreate) { + public Boolean saveElement(String groupId, CreateMapElementRequest elementCreate) { Optional groupElementOpt = this.getEntityByElementId(elementCreate.getId()); if (groupElementOpt.isPresent()) { @@ -57,11 +57,17 @@ public class GroupElementServiceImpl implements IGroupElementService { GroupElementEntity groupElement = this.createDtoConvertToEntity(elementCreate); groupElement.setGroupId(groupId); - return mapper.insert(groupElement) > 0; + boolean saveElement = mapper.insert(groupElement) > 0; + if (!saveElement) { + return false; + } + // save coordinate + return elementCoordinateService.saveCoordinate( + elementCreate.getResource().getContent().getGeometry().convertToList(), elementCreate.getId()); } @Override - public Boolean updateElement(String elementId, ElementUpdateDTO elementUpdate, String username) { + public Boolean updateElement(String elementId, UpdateMapElementRequest elementUpdate, String username) { Optional groupElementOpt = this.getEntityByElementId(elementId); if (groupElementOpt.isEmpty()) { return false; @@ -70,8 +76,16 @@ public class GroupElementServiceImpl implements IGroupElementService { GroupElementEntity groupElement = groupElementOpt.get(); groupElement.setUsername(username); this.updateEntityWithDto(elementUpdate, groupElement); - - return mapper.updateById(groupElement) > 0; + boolean update = mapper.updateById(groupElement) > 0; + if (!update) { + return false; + } + // delete all coordinates according to element id. + boolean delCoordinate = elementCoordinateService.deleteCoordinateByElementId(elementId); + // save coordinate + boolean saveCoordinate = elementCoordinateService.saveCoordinate( + elementUpdate.getContent().getGeometry().convertToList(), elementId); + return delCoordinate & saveCoordinate; } @Override @@ -93,11 +107,24 @@ public class GroupElementServiceImpl implements IGroupElementService { return Optional.empty(); } GroupElementEntity elementEntity = elementEntityOpt.get(); - GroupElementDTO groupElement = this.entityConvertToDto(elementEntity); + MapGroupElement groupElement = this.entityConvertToDto(elementEntity); this.addCoordinateToElement(groupElement, elementEntity); + return Optional.ofNullable(groupElement2Dto(groupElement, elementEntity.getGroupId())); + } - return Optional.ofNullable(groupElement); + private GroupElementDTO groupElement2Dto(MapGroupElement element, String groupId) { + if (null == element) { + return null; + } + return GroupElementDTO.builder() + .elementId(element.getId()) + .groupId(groupId) + .updateTime(element.getUpdateTime()) + .createTime(element.getCreateTime()) + .name(element.getName()) + .resource(element.getResource()) + .build(); } /** @@ -105,24 +132,19 @@ public class GroupElementServiceImpl implements IGroupElementService { * @param element * @param elementEntity */ - private void addCoordinateToElement(GroupElementDTO element, GroupElementEntity elementEntity) { - Optional coordinateOpt = ElementTypeEnum.findType(elementEntity.getElementType()); + private void addCoordinateToElement(MapGroupElement element, GroupElementEntity elementEntity) { + Optional coordinateOpt = ElementTypeEnum.findType(elementEntity.getElementType()); if (coordinateOpt.isEmpty()) { return; } - - ElementType elementType = coordinateOpt.get(); - - element.getResource().setContent( - ResourceContentDTO.builder() - .properties(ContentPropertyDTO.builder() - .clampToGround(elementEntity.getClampToGround()) - .color(elementEntity.getColor()) - .build()) - .geometry(elementType) - .build()); - - elementType.adapterCoordinateType( + element.getResource() + .setContent(new ElementContent() + .setProperties(new ElementProperty() + .setClampToGround(elementEntity.getClampToGround()) + .setColor(elementEntity.getColor())) + .setGeometry(coordinateOpt.get())); + + coordinateOpt.get().adapterCoordinateType( elementCoordinateService.getCoordinateByElementId(elementEntity.getElementId())); } @@ -142,24 +164,19 @@ public class GroupElementServiceImpl implements IGroupElementService { * @param entity * @return */ - private GroupElementDTO entityConvertToDto(GroupElementEntity entity) { - GroupElementDTO.GroupElementDTOBuilder builder = GroupElementDTO.builder(); + private MapGroupElement entityConvertToDto(GroupElementEntity entity) { if (entity == null) { - return builder.build(); + return null; } - return builder - .display(entity.getDisplay()) - .groupId(entity.getGroupId()) - .elementId(entity.getElementId()) - .name(entity.getElementName()) - .createTime(entity.getCreateTime()) - .updateTime(entity.getUpdateTime()) - .resource(ElementResourceDTO.builder() - .type(entity.getElementType()) - .username(entity.getUsername()) - .build()) - .build(); + return new MapGroupElement() + .setId(entity.getElementId()) + .setName(entity.getElementName()) + .setCreateTime(entity.getCreateTime()) + .setUpdateTime(entity.getUpdateTime()) + .setResource(new ElementResource() + .setType(ElementResourceTypeEnum.find(entity.getElementType())) + .setUsername(entity.getUsername())); } /** @@ -167,8 +184,8 @@ public class GroupElementServiceImpl implements IGroupElementService { * @param elementCreate * @return */ - private GroupElementEntity createDtoConvertToEntity(ElementCreateDTO elementCreate) { - ContentPropertyDTO properties = elementCreate.getResource().getContent().getProperties(); + private GroupElementEntity createDtoConvertToEntity(CreateMapElementRequest elementCreate) { + ElementProperty properties = elementCreate.getResource().getContent().getProperties(); return GroupElementEntity.builder() .elementId(elementCreate.getId()) .elementName(elementCreate.getName()) @@ -184,7 +201,7 @@ public class GroupElementServiceImpl implements IGroupElementService { * @param elementUpdate * @param groupElement */ - private void updateEntityWithDto(ElementUpdateDTO elementUpdate, GroupElementEntity groupElement) { + private void updateEntityWithDto(UpdateMapElementRequest elementUpdate, GroupElementEntity groupElement) { if (elementUpdate == null || groupElement == null) { return; } diff --git a/src/main/java/com/dji/sample/map/service/impl/GroupServiceImpl.java b/src/main/java/com/dji/sample/map/service/impl/GroupServiceImpl.java index 07ac7f1..5c3e913 100644 --- a/src/main/java/com/dji/sample/map/service/impl/GroupServiceImpl.java +++ b/src/main/java/com/dji/sample/map/service/impl/GroupServiceImpl.java @@ -2,10 +2,11 @@ package com.dji.sample.map.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.dji.sample.map.dao.IGroupMapper; -import com.dji.sample.map.model.dto.GroupDTO; import com.dji.sample.map.model.entity.GroupEntity; import com.dji.sample.map.service.IGroupElementService; import com.dji.sample.map.service.IGroupService; +import com.dji.sdk.cloudapi.map.GetMapElementsResponse; +import com.dji.sdk.cloudapi.map.GroupTypeEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,7 +31,7 @@ public class GroupServiceImpl implements IGroupService { private IGroupElementService groupElementService; @Override - public List getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed) { + public List getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed) { return mapper.selectList( new LambdaQueryWrapper() @@ -47,20 +48,15 @@ public class GroupServiceImpl implements IGroupService { * @param entity * @return */ - private GroupDTO entityConvertToDto(GroupEntity entity) { - GroupDTO.GroupDTOBuilder builder = GroupDTO.builder(); - + private GetMapElementsResponse entityConvertToDto(GroupEntity entity) { if (entity == null) { - return builder.build(); + return null; } - return builder - .id(entity.getGroupId()) - .name(entity.getGroupName()) - .type(entity.getGroupType()) - .isLock(entity.getIsLock()) - .isDistributed(entity.getIsDistributed()) - .createTime(entity.getCreateTime()) - .build(); + return new GetMapElementsResponse() + .setId(entity.getGroupId()) + .setName(entity.getGroupName()) + .setType(GroupTypeEnum.find(entity.getGroupType())) + .setLock(entity.getIsLock()); } } diff --git a/src/main/java/com/dji/sample/map/service/impl/WorkspaceElementServiceImpl.java b/src/main/java/com/dji/sample/map/service/impl/WorkspaceElementServiceImpl.java index 49b5805..7b6c134 100644 --- a/src/main/java/com/dji/sample/map/service/impl/WorkspaceElementServiceImpl.java +++ b/src/main/java/com/dji/sample/map/service/impl/WorkspaceElementServiceImpl.java @@ -1,14 +1,14 @@ package com.dji.sample.map.service.impl; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.map.model.dto.ElementCreateDTO; -import com.dji.sample.map.model.dto.ElementUpdateDTO; -import com.dji.sample.map.model.dto.GroupDTO; +import com.dji.sample.component.websocket.model.BizCodeEnum; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.map.model.dto.GroupElementDTO; import com.dji.sample.map.service.IElementCoordinateService; import com.dji.sample.map.service.IGroupElementService; import com.dji.sample.map.service.IGroupService; import com.dji.sample.map.service.IWorkspaceElementService; +import com.dji.sdk.cloudapi.map.*; +import com.dji.sdk.common.HttpResultResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -34,60 +34,67 @@ public class WorkspaceElementServiceImpl implements IWorkspaceElementService { @Autowired private IElementCoordinateService elementCoordinateService; + @Autowired + private IWebSocketMessageService webSocketMessageService; + @Override - public List getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed) { - List groupList = groupService.getAllGroupsByWorkspaceId(workspaceId, groupId, isDistributed); - groupList.forEach(group -> group.setElements( - groupElementService.getElementsByGroupId(group.getId()) - )); + public List getAllGroupsByWorkspaceId(String workspaceId, String groupId, Boolean isDistributed) { + List groupList = groupService.getAllGroupsByWorkspaceId(workspaceId, groupId, isDistributed); + groupList.forEach(group -> group.setElements(groupElementService.getElementsByGroupId(group.getId()))); return groupList; } @Override - public ResponseResult saveElement(String groupId, ElementCreateDTO elementCreate) { + public HttpResultResponse saveElement(String workspaceId, String groupId, CreateMapElementRequest elementCreate) { boolean saveElement = groupElementService.saveElement(groupId, elementCreate); if (!saveElement) { - return ResponseResult.error("Failed to save the element."); + return HttpResultResponse.error("Failed to save the element."); } - - // save coordinate - boolean saveCoordinate = elementCoordinateService.saveCoordinate( - elementCreate.getResource().getContent().getGeometry().convertToList(), elementCreate.getId()); - - return saveCoordinate ? - ResponseResult.success() : ResponseResult.error("Failed to save the coordinate."); + // Notify all WebSocket connections in this workspace to be updated when an element is created. + getElementByElementId(elementCreate.getId()) + .ifPresent(groupElement -> webSocketMessageService.sendBatch( + workspaceId, BizCodeEnum.MAP_ELEMENT_CREATE.getCode(), + element2CreateWsElement(groupElement))); + return HttpResultResponse.success(); } - @Override - public ResponseResult updateElement(String elementId, ElementUpdateDTO elementUpdate, String username) { + public HttpResultResponse updateElement(String workspaceId, String elementId, UpdateMapElementRequest elementUpdate, String username) { boolean updElement = groupElementService.updateElement(elementId, elementUpdate, username); if (!updElement) { - return ResponseResult.error("Failed to update the element."); + return HttpResultResponse.error("Failed to update the element."); } - // delete all coordinates according to element id. - boolean delCoordinate = elementCoordinateService.deleteCoordinateByElementId(elementId); - // save coordinate - boolean saveCoordinate = elementCoordinateService.saveCoordinate( - elementUpdate.getContent().getGeometry().convertToList(), elementId); - - return delCoordinate && saveCoordinate ? - ResponseResult.success() : ResponseResult.error("Failed to update the coordinate."); + // Notify all WebSocket connections in this workspace to update when there is an element update. + getElementByElementId(elementId) + .ifPresent(groupElement -> webSocketMessageService.sendBatch( + workspaceId, BizCodeEnum.MAP_ELEMENT_UPDATE.getCode(), + element2UpdateWsElement(groupElement))); + return HttpResultResponse.success(); } @Override - public ResponseResult deleteElement(String elementId) { + public HttpResultResponse deleteElement(String workspaceId, String elementId) { + Optional elementOpt = getElementByElementId(elementId); boolean delElement = groupElementService.deleteElement(elementId); if (!delElement) { - return ResponseResult.error("Failed to delete the element."); + return HttpResultResponse.error("Failed to delete the element."); } // delete all coordinates according to element id. boolean delCoordinate = elementCoordinateService.deleteCoordinateByElementId(elementId); + if (!delCoordinate) { + return HttpResultResponse.error("Failed to delete the coordinate."); + } - return delCoordinate ? - ResponseResult.success() : ResponseResult.error("Failed to delete the coordinate."); + // Notify all WebSocket connections in this workspace to update when an element is deleted. + elementOpt.ifPresent(element -> + webSocketMessageService.sendBatch(workspaceId, BizCodeEnum.MAP_ELEMENT_DELETE.getCode(), + new MapElementDeleteWsResponse() + .setGroupId(element.getGroupId()) + .setId(elementId))); + + return HttpResultResponse.success(); } @Override @@ -96,14 +103,41 @@ public class WorkspaceElementServiceImpl implements IWorkspaceElementService { } @Override - public ResponseResult deleteAllElementByGroupId(String groupId) { - List groupElementList = groupElementService.getElementsByGroupId(groupId); - for (GroupElementDTO groupElement : groupElementList) { - ResponseResult response = this.deleteElement(groupElement.getElementId()); - if (ResponseResult.CODE_SUCCESS != response.getCode()) { + public HttpResultResponse deleteAllElementByGroupId(String workspaceId, String groupId) { + List groupElementList = groupElementService.getElementsByGroupId(groupId); + for (MapGroupElement groupElement : groupElementList) { + HttpResultResponse response = this.deleteElement(workspaceId, groupElement.getId()); + if (HttpResultResponse.CODE_SUCCESS != response.getCode()) { return response; } } - return ResponseResult.success(); + + return HttpResultResponse.success(); + } + + public MapElementCreateWsResponse element2CreateWsElement(GroupElementDTO element) { + if (element == null) { + return null; + } + return new MapElementCreateWsResponse() + .setId(element.getElementId()) + .setGroupId(element.getGroupId()) + .setName(element.getName()) + .setResource(element.getResource()) + .setUpdateTime(element.getUpdateTime()) + .setCreateTime(element.getCreateTime()); + } + + public MapElementUpdateWsResponse element2UpdateWsElement(GroupElementDTO element) { + if (element == null) { + return null; + } + return new MapElementUpdateWsResponse() + .setId(element.getElementId()) + .setGroupId(element.getGroupId()) + .setName(element.getName()) + .setResource(element.getResource()) + .setUpdateTime(element.getUpdateTime()) + .setCreateTime(element.getCreateTime()); } } 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 8a378f5..3934014 100644 --- a/src/main/java/com/dji/sample/media/controller/FileController.java +++ b/src/main/java/com/dji/sample/media/controller/FileController.java @@ -1,9 +1,9 @@ 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 com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -29,11 +29,11 @@ public class FileController { * @return */ @GetMapping("/{workspace_id}/files") - public ResponseResult> getFilesList(@RequestParam(defaultValue = "1") Long page, - @RequestParam(name = "page_size", defaultValue = "10") Long pageSize, - @PathVariable(name = "workspace_id") String workspaceId) { + public HttpResultResponse> getFilesList(@RequestParam(defaultValue = "1") Long page, + @RequestParam(name = "page_size", defaultValue = "10") Long pageSize, + @PathVariable(name = "workspace_id") String workspaceId) { PaginationData filesList = fileService.getMediaFilesPaginationByWorkspaceId(workspaceId, page, pageSize); - return ResponseResult.success(filesList); + return HttpResultResponse.success(filesList); } /** 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 b02f8ad..ec21a54 100644 --- a/src/main/java/com/dji/sample/media/controller/MediaController.java +++ b/src/main/java/com/dji/sample/media/controller/MediaController.java @@ -1,17 +1,17 @@ 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 com.dji.sdk.cloudapi.media.*; +import com.dji.sdk.cloudapi.media.api.IHttpMediaService; +import com.dji.sdk.common.HttpResultResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * @author sean @@ -20,8 +20,7 @@ import java.util.concurrent.ConcurrentHashMap; */ @Slf4j @RestController -@RequestMapping("${url.media.prefix}${url.media.version}/workspaces") -public class MediaController { +public class MediaController implements IHttpMediaService { @Autowired private IMediaService mediaService; @@ -29,45 +28,44 @@ public class MediaController { /** * Check if the file has been uploaded by the fingerprint. * @param workspaceId - * @param file + * @param request * @return */ - @PostMapping("/{workspace_id}/fast-upload") - public ResponseResult fastUpload(@PathVariable(name = "workspace_id") String workspaceId, @RequestBody FileUploadDTO file) { + @Override + public HttpResultResponse mediaFastUpload(String workspaceId, @Valid MediaFastUploadRequest request, HttpServletRequest req, HttpServletResponse rsp) { + boolean isExist = mediaService.fastUpload(workspaceId, request.getFingerprint()); - boolean isExist = mediaService.fastUpload(workspaceId, file.getFingerprint()); - - return isExist ? ResponseResult.success() : ResponseResult.error(file.getFingerprint() + "don't exist."); + return isExist ? HttpResultResponse.success() : HttpResultResponse.error(request.getFingerprint() + "don't exist."); } /** * When the file is uploaded to the storage server by pilot, * the basic information of the file is reported through this interface. * @param workspaceId - * @param file + * @param request * @return */ - @PostMapping("/{workspace_id}/upload-callback") - public ResponseResult uploadCallback(@PathVariable(name = "workspace_id") String workspaceId, @RequestBody FileUploadDTO file) { - mediaService.saveMediaFile(workspaceId, file); - return ResponseResult.success(file.getObjectKey()); - + @Override + public HttpResultResponse mediaUploadCallback(String workspaceId, @Valid MediaUploadCallbackRequest request, HttpServletRequest req, HttpServletResponse rsp) { + mediaService.saveMediaFile(workspaceId, request); + return HttpResultResponse.success(request.getObjectKey()); } /** * Query the files that already exist in this workspace based on the workspace id and the collection of tiny fingerprints. * @param workspaceId - * @param tinyFingerprints There is only one tiny_fingerprint parameter in the body. + * @param request There is only one tiny_fingerprint parameter in the body. * But it is not recommended to use Map to receive the parameter. * @return */ - @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))); + @Override + public HttpResultResponse getExistFileTinyFingerprint(String workspaceId, @Valid GetFileFingerprintRequest request, HttpServletRequest req, HttpServletResponse rsp) { + List existingList = mediaService.getExistTinyFingerprints(workspaceId, request.getTinyFingerprints()); + return HttpResultResponse.success(new GetFileFingerprintResponse().setTinyFingerprints(existingList)); } + @Override + public HttpResultResponse folderUploadCallback(String workspaceId, @Valid FolderUploadCallbackRequest request, HttpServletRequest req, HttpServletResponse rsp) { + return null; + } } \ 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 deleted file mode 100644 index d00e953..0000000 --- a/src/main/java/com/dji/sample/media/model/CredentialsDTO.java +++ /dev/null @@ -1,50 +0,0 @@ -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; - - -/** - * @author sean - * @version 0.2 - * @date 2021/12/7 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class CredentialsDTO { - - private static final int DELAY = 300; - - private String accessKeyId; - - private String accessKeySecret; - - private Long expire; - - private String securityToken; - - public CredentialsDTO(Credentials credentials, long expire) { - this.accessKeyId = credentials.accessKey(); - this.accessKeySecret = credentials.secretKey(); - this.securityToken = credentials.sessionToken(); - this.expire = expire - DELAY; - } - - public CredentialsDTO(AssumeRoleResponse.Credentials credentials, long expire) { - this.accessKeyId = credentials.getAccessKeyId(); - this.accessKeySecret = credentials.getAccessKeySecret(); - this.securityToken = credentials.getSecurityToken(); - this.expire = expire - DELAY; - } - - public CredentialsDTO(com.amazonaws.services.securitytoken.model.Credentials credentials) { - this.accessKeyId = credentials.getAccessKeyId(); - this.accessKeySecret = credentials.getSecretAccessKey(); - this.securityToken = credentials.getSessionToken(); - this.expire = (credentials.getExpiration().getTime() - System.currentTimeMillis()) / 1000 - DELAY; - } -} diff --git a/src/main/java/com/dji/sample/media/model/FileExtensionDTO.java b/src/main/java/com/dji/sample/media/model/FileExtensionDTO.java deleted file mode 100644 index c1f96b7..0000000 --- a/src/main/java/com/dji/sample/media/model/FileExtensionDTO.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.dji.sample.media.model; - -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/7 - */ -@Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class FileExtensionDTO { - - private String droneModelKey; - - private Boolean isOriginal; - - private String payloadModelKey; - - private String tinnyFingerprint; - - private String sn; - - private String flightId; - -} diff --git a/src/main/java/com/dji/sample/media/model/FileMetadataDTO.java b/src/main/java/com/dji/sample/media/model/FileMetadataDTO.java deleted file mode 100644 index bab4b5a..0000000 --- a/src/main/java/com/dji/sample/media/model/FileMetadataDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.dji.sample.media.model; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.Data; - -import java.util.Date; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/7 - */ -@Data -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class FileMetadataDTO { - - private Double absoluteAltitude; - - @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssX") - private Date createdTime; - - private Double gimbalYawDegree; - - private PositionDTO photoedPosition; - - private PositionDTO shootPosition; - - private Double relativeAltitude; -} diff --git a/src/main/java/com/dji/sample/media/model/FileUploadCallback.java b/src/main/java/com/dji/sample/media/model/FileUploadCallback.java deleted file mode 100644 index 62bbcc7..0000000 --- a/src/main/java/com/dji/sample/media/model/FileUploadCallback.java +++ /dev/null @@ -1,18 +0,0 @@ -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/FileUploadDTO.java b/src/main/java/com/dji/sample/media/model/FileUploadDTO.java deleted file mode 100644 index acfa910..0000000 --- a/src/main/java/com/dji/sample/media/model/FileUploadDTO.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.dji.sample.media.model; - -import lombok.Data; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/7 - */ -@Data -public class FileUploadDTO { - - private FileExtensionDTO ext; - - private String fingerprint; - - private String name; - - private String path; - - private FileMetadataDTO metadata; - - private String objectKey; - - private Integer subFileType; -} diff --git a/src/main/java/com/dji/sample/media/model/MediaFileCountDTO.java b/src/main/java/com/dji/sample/media/model/MediaFileCountDTO.java index ac804c6..2aa952a 100644 --- a/src/main/java/com/dji/sample/media/model/MediaFileCountDTO.java +++ b/src/main/java/com/dji/sample/media/model/MediaFileCountDTO.java @@ -27,4 +27,6 @@ public class MediaFileCountDTO { private Integer mediaCount; private Integer uploadedCount; + + private String deviceSn; } diff --git a/src/main/java/com/dji/sample/media/model/PositionDTO.java b/src/main/java/com/dji/sample/media/model/PositionDTO.java deleted file mode 100644 index 1b5cda4..0000000 --- a/src/main/java/com/dji/sample/media/model/PositionDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.dji.sample.media.model; - -import lombok.Data; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/7 - */ -@Data -public class PositionDTO { - - private Double lat; - - private Double lng; -} diff --git a/src/main/java/com/dji/sample/media/model/StsCredentialsDTO.java b/src/main/java/com/dji/sample/media/model/StsCredentialsDTO.java deleted file mode 100644 index a4da2ab..0000000 --- a/src/main/java/com/dji/sample/media/model/StsCredentialsDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.dji.sample.media.model; - -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author sean - * @version 0.2 - * @date 2021/12/7 - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) -public class StsCredentialsDTO { - - private String bucket; - - private CredentialsDTO credentials; - - private String endpoint; - - private String objectKeyPrefix; - - private String provider; - - private String region; -} 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 d3fd9c8..72f41a1 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,8 @@ 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 com.dji.sdk.cloudapi.media.MediaUploadCallbackRequest; +import com.dji.sdk.common.PaginationData; import java.net.URL; import java.util.List; @@ -28,7 +28,7 @@ public interface IFileService { * @param file * @return */ - Integer saveFile(String workspaceId, FileUploadDTO file); + Integer saveFile(String workspaceId, MediaUploadCallbackRequest file); /** * Query information about all files in this workspace based on the workspace id. diff --git a/src/main/java/com/dji/sample/media/service/IMediaRedisService.java b/src/main/java/com/dji/sample/media/service/IMediaRedisService.java new file mode 100644 index 0000000..03fc446 --- /dev/null +++ b/src/main/java/com/dji/sample/media/service/IMediaRedisService.java @@ -0,0 +1,26 @@ +package com.dji.sample.media.service; + +import com.dji.sample.media.model.MediaFileCountDTO; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/9 + */ +public interface IMediaRedisService { + + void setMediaCount(String gatewaySn, String jobId, MediaFileCountDTO mediaFile); + + MediaFileCountDTO getMediaCount(String gatewaySn, String jobId); + + boolean delMediaCount(String gatewaySn, String jobId); + + boolean detMediaCountByDeviceSn(String gatewaySn); + + void setMediaHighestPriority(String gatewaySn, MediaFileCountDTO mediaFile); + + MediaFileCountDTO getMediaHighestPriority(String gatewaySn); + + boolean delMediaHighestPriority(String gatewaySn); + +} 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 15a80cc..4b55d2f 100644 --- a/src/main/java/com/dji/sample/media/service/IMediaService.java +++ b/src/main/java/com/dji/sample/media/service/IMediaService.java @@ -1,6 +1,6 @@ package com.dji.sample.media.service; -import com.dji.sample.media.model.FileUploadDTO; +import com.dji.sdk.cloudapi.media.MediaUploadCallbackRequest; import java.util.List; @@ -25,7 +25,7 @@ public interface IMediaService { * @param file * @return */ - Integer saveMediaFile(String workspaceId, FileUploadDTO file); + Integer saveMediaFile(String workspaceId, MediaUploadCallbackRequest file); /** * Query tiny fingerprints about all files in this workspace based on the workspace id. 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 6d73a24..de82e5e 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 @@ -2,18 +2,19 @@ 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.model.enums.DeviceDomainEnum; import com.dji.sample.manage.service.IDeviceDictionaryService; import com.dji.sample.media.dao.IFileMapper; -import com.dji.sample.media.model.FileUploadDTO; import com.dji.sample.media.model.MediaFileDTO; import com.dji.sample.media.model.MediaFileEntity; import com.dji.sample.media.service.IFileService; +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.dji.sdk.cloudapi.media.MediaSubFileTypeEnum; +import com.dji.sdk.cloudapi.media.MediaUploadCallbackRequest; +import com.dji.sdk.common.Pagination; +import com.dji.sdk.common.PaginationData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,7 +23,6 @@ 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; import java.util.UUID; @@ -66,7 +66,7 @@ public class FileServiceImpl implements IFileService { } @Override - public Integer saveFile(String workspaceId, FileUploadDTO file) { + public Integer saveFile(String workspaceId, MediaUploadCallbackRequest file) { MediaFileEntity fileEntity = this.fileUploadConvertToEntity(file); fileEntity.setWorkspaceId(workspaceId); fileEntity.setFileId(UUID.randomUUID().toString()); @@ -121,7 +121,7 @@ public class FileServiceImpl implements IFileService { * @param file * @return */ - private MediaFileEntity fileUploadConvertToEntity(FileUploadDTO file) { + private MediaFileEntity fileUploadConvertToEntity(MediaUploadCallbackRequest file) { MediaFileEntity.MediaFileEntityBuilder builder = MediaFileEntity.builder(); if (file != null) { @@ -129,19 +129,18 @@ public class FileServiceImpl implements IFileService { .filePath(file.getPath()) .fingerprint(file.getFingerprint()) .objectKey(file.getObjectKey()) - .subFileType(file.getSubFileType()) - .isOriginal(file.getExt().getIsOriginal()) - .jobId(file.getExt().getFlightId()) + .subFileType(Optional.ofNullable(file.getSubFileType()).map(MediaSubFileTypeEnum::getType).orElse(null)) + .isOriginal(file.getExt().getOriginal()) + .jobId(file.getExt().getFileGroupId()) .drone(file.getExt().getSn()) - .tinnyFingerprint(file.getExt().getTinnyFingerprint()); + .tinnyFingerprint(file.getExt().getTinnyFingerprint()) + .payload(file.getExt().getPayloadModelKey().getDevice()); // domain-type-subType - int[] payloadModel = Arrays.stream(file.getExt().getPayloadModelKey().split("-")) - .map(Integer::valueOf) - .mapToInt(Integer::intValue) - .toArray(); + DeviceEnum payloadModelKey = file.getExt().getPayloadModelKey(); Optional payloadDict = deviceDictionaryService - .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), payloadModel[1], payloadModel[2]); + .getOneDictionaryInfoByTypeSubType(payloadModelKey.getDomain().getDomain(), + payloadModelKey.getType().getType(), payloadModelKey.getSubType().getSubType()); payloadDict.ifPresent(payload -> builder.payload(payload.getDeviceName())); } return builder.build(); diff --git a/src/main/java/com/dji/sample/media/service/impl/MediaRedisServiceImpl.java b/src/main/java/com/dji/sample/media/service/impl/MediaRedisServiceImpl.java new file mode 100644 index 0000000..9090d75 --- /dev/null +++ b/src/main/java/com/dji/sample/media/service/impl/MediaRedisServiceImpl.java @@ -0,0 +1,54 @@ +package com.dji.sample.media.service.impl; + +import com.dji.sample.component.redis.RedisConst; +import com.dji.sample.component.redis.RedisOpsUtils; +import com.dji.sample.media.model.MediaFileCountDTO; +import com.dji.sample.media.service.IMediaRedisService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/9 + */ +@Service +@Slf4j +public class MediaRedisServiceImpl implements IMediaRedisService { + + + @Override + public void setMediaCount(String gatewaySn, String jobId, MediaFileCountDTO mediaFile) { + RedisOpsUtils.hashSet(RedisConst.MEDIA_FILE_PREFIX + gatewaySn, jobId, mediaFile); + } + + @Override + public MediaFileCountDTO getMediaCount(String gatewaySn, String jobId) { + return (MediaFileCountDTO) RedisOpsUtils.hashGet(RedisConst.MEDIA_FILE_PREFIX + gatewaySn, jobId); + } + + @Override + public boolean delMediaCount(String gatewaySn, String jobId) { + return RedisOpsUtils.hashDel(RedisConst.MEDIA_FILE_PREFIX + gatewaySn, new String[]{jobId}); + } + + @Override + public boolean detMediaCountByDeviceSn(String gatewaySn) { + return RedisOpsUtils.del(RedisConst.MEDIA_FILE_PREFIX + gatewaySn); + } + + @Override + public void setMediaHighestPriority(String gatewaySn, MediaFileCountDTO mediaFile) { + RedisOpsUtils.setWithExpire(RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + gatewaySn, mediaFile, RedisConst.DEVICE_ALIVE_SECOND * 5); + } + + @Override + public MediaFileCountDTO getMediaHighestPriority(String gatewaySn) { + return (MediaFileCountDTO) RedisOpsUtils.get(RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + gatewaySn); + } + + @Override + public boolean delMediaHighestPriority(String gatewaySn) { + return RedisOpsUtils.del(RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + gatewaySn); + } +} 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 5a75fa0..2437008 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,35 +1,31 @@ package com.dji.sample.media.service.impl; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.ChannelName; -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; -import com.dji.sample.component.mqtt.model.MapKeyConst; -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.oss.model.OssConfiguration; import com.dji.sample.component.websocket.model.BizCodeEnum; -import com.dji.sample.component.websocket.service.ISendMessageService; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.service.IDeviceRedisService; import com.dji.sample.manage.service.IDeviceService; -import com.dji.sample.media.model.FileUploadCallback; -import com.dji.sample.media.model.FileUploadDTO; import com.dji.sample.media.model.MediaFileCountDTO; import com.dji.sample.media.model.MediaFileDTO; import com.dji.sample.media.service.IFileService; +import com.dji.sample.media.service.IMediaRedisService; import com.dji.sample.media.service.IMediaService; -import com.dji.sample.wayline.model.dto.WaylineJobDTO; import com.dji.sample.wayline.service.IWaylineJobService; +import com.dji.sdk.cloudapi.media.*; +import com.dji.sdk.cloudapi.media.api.AbstractMediaService; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -41,7 +37,7 @@ import java.util.stream.Collectors; */ @Service @Slf4j -public class MediaServiceImpl implements IMediaService { +public class MediaServiceImpl extends AbstractMediaService implements IMediaService { @Autowired private IFileService fileService; @@ -52,25 +48,25 @@ public class MediaServiceImpl implements IMediaService { @Autowired private ObjectMapper objectMapper; - @Autowired - private IMessageSenderService messageSenderService; - @Autowired private IDeviceService deviceService; @Autowired - private ISendMessageService sendMessageService; + private IWebSocketMessageService webSocketMessageService; @Autowired private IDeviceRedisService deviceRedisService; + @Autowired + private IMediaRedisService mediaRedisService; + @Override public Boolean fastUpload(String workspaceId, String fingerprint) { return fileService.checkExist(workspaceId, fingerprint); } @Override - public Integer saveMediaFile(String workspaceId, FileUploadDTO file) { + public Integer saveMediaFile(String workspaceId, MediaUploadCallbackRequest file) { return fileService.saveFile(workspaceId, file); } @@ -92,133 +88,129 @@ public class MediaServiceImpl implements IMediaService { } - /** - * Handle media files messages reported by dock. - * @param receiver - * @return - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleFileUploadCallBack(CommonTopicReceiver receiver) { - FileUploadCallback callback = objectMapper.convertValue(receiver.getData(), FileUploadCallback.class); + @Override + public TopicEventsResponse fileUploadCallback(TopicEventsRequest request, MessageHeaders headers) { + FileUploadCallback callback = request.getData(); - if (callback.getResult() != ResponseResult.CODE_SUCCESS) { + if (MqttReply.CODE_SUCCESS != callback.getResult()) { log.error("Media file upload failed!"); return null; } String jobId = callback.getFile().getExt().getFlightId(); - Optional deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway()); - MediaFileCountDTO mediaFileCount = (MediaFileCountDTO) RedisOpsUtils.hashGet(RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway(), jobId); + Optional deviceOpt = deviceRedisService.getDeviceOnline(request.getGateway()); + MediaFileCountDTO mediaFileCount = mediaRedisService.getMediaCount(request.getGateway(), jobId); // duplicate data if (deviceOpt.isEmpty() - || (Objects.nonNull(mediaFileCount) && receiver.getBid().equals(mediaFileCount.getBid()) - && receiver.getTid().equals(mediaFileCount.getTid()))) { - return receiver; + || (Objects.nonNull(mediaFileCount) && request.getBid().equals(mediaFileCount.getBid()) + && request.getTid().equals(mediaFileCount.getTid()))) { + return new TopicEventsResponse().setData(MqttReply.success()); } - DeviceDTO device = deviceOpt.get(); - Optional jobOpt = waylineJobService.getJobByJobId(device.getWorkspaceId(), jobId); - if (jobOpt.isPresent()) { - boolean isSave = parseMediaFile(callback, jobOpt.get()); - if (!isSave) { - log.error("Failed to save the file to the database, please check the data manually."); - return null; - } + boolean isSave = parseMediaFile(callback, device); + if (!isSave) { + log.error("Failed to save the file to the database, please check the data manually."); + return null; } - notifyUploadedCount(mediaFileCount, receiver, jobId, device); - return receiver; + notifyUploadedCount(mediaFileCount, request, jobId, device); + return new TopicEventsResponse().setData(MqttReply.success()); } - /** - * update the uploaded count and notify web side - * @param mediaFileCount - * @param receiver - * @param jobId - */ - private void notifyUploadedCount(MediaFileCountDTO mediaFileCount, CommonTopicReceiver receiver, String jobId, DeviceDTO dock) { - // Do not notify when files that do not belong to the route are uploaded. - if (Objects.isNull(mediaFileCount)) { - return; + @Override + public TopicEventsResponse highestPriorityUploadFlightTaskMedia( + TopicEventsRequest request, MessageHeaders headers) { + String jobId = request.getData().getFlightId(); + if (!StringUtils.hasText(jobId)) { + return null; } - mediaFileCount.setBid(receiver.getBid()); - mediaFileCount.setTid(receiver.getTid()); - mediaFileCount.setUploadedCount(mediaFileCount.getUploadedCount() + 1); - - String key = RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway(); - // After all the files of the job are uploaded, delete the media file key. - if (mediaFileCount.getUploadedCount() >= mediaFileCount.getMediaCount()) { - RedisOpsUtils.hashDel(key, new String[]{jobId}); - // After uploading, delete the key with the highest priority. - String highestKey = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + receiver.getGateway(); - if (RedisOpsUtils.checkExist(highestKey) && - jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(highestKey)).getJobId())) { - RedisOpsUtils.del(highestKey); + MediaFileCountDTO countDTO = mediaRedisService.getMediaHighestPriority(request.getGateway()); + if (Objects.nonNull(countDTO)) { + if (jobId.equals(countDTO.getJobId())) { + mediaRedisService.setMediaHighestPriority(request.getGateway(), countDTO); + return new TopicEventsResponse().setData(MqttReply.success()); } + countDTO.setPreJobId(countDTO.getJobId()); + } + countDTO.setJobId(jobId); + mediaRedisService.setMediaHighestPriority(request.getGateway(), countDTO); - if (RedisOpsUtils.hashLen(key) == 0) { - RedisOpsUtils.del(key); - } - } else { - RedisOpsUtils.hashSet(key, jobId, mediaFileCount); + Optional deviceOpt = deviceRedisService.getDeviceOnline(request.getGateway()); + if (deviceOpt.isEmpty()) { + return null; } - sendMessageService.sendBatch(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal(), - BizCodeEnum.FILE_UPLOAD_CALLBACK.getCode(), mediaFileCount); + webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), + BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode(), countDTO); + + return new TopicEventsResponse().setData(MqttReply.success()); } - private Boolean parseMediaFile(FileUploadCallback callback, WaylineJobDTO job) { + private Boolean parseMediaFile(FileUploadCallback callback, DeviceDTO device) { + MediaUploadCallbackRequest file = convert2callbackRequest(callback.getFile()); // Set the drone sn that shoots the media - Optional dockDTO = deviceService.getDeviceBySn(job.getDockSn()); - dockDTO.ifPresent(dock -> callback.getFile().getExt().setSn(dock.getChildDeviceSn())); + file.getExt().setSn(device.getChildDeviceSn()); // set path - String objectKey = callback.getFile().getObjectKey(); - callback.getFile().setPath(objectKey.substring(objectKey.indexOf("/") + 1, objectKey.lastIndexOf("/"))); + String objectKey = file.getObjectKey(); + file.setPath(objectKey.substring(Optional.of(objectKey.indexOf(OssConfiguration.objectDirPrefix)) + .filter(index -> index > 0).map(index -> index++).orElse(0), + objectKey.lastIndexOf("/"))); - return fileService.saveFile(job.getWorkspaceId(), callback.getFile()) > 0; + return fileService.saveFile(device.getWorkspaceId(), file) > 0; } - /** - * Handles the highest priority message about media uploads. - * @param receiver - * @param headers - * @return - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleHighestPriorityUploadFlightTaskMedia(CommonTopicReceiver receiver, MessageHeaders headers) { - Map map = objectMapper.convertValue(receiver.getData(), Map.class); - if (map.isEmpty() || !map.containsKey(MapKeyConst.FLIGHT_ID)) { - return null; + private void notifyUploadedCount(MediaFileCountDTO mediaFileCount, TopicEventsRequest request, String jobId, DeviceDTO dock) { + // Do not notify when files that do not belong to the route are uploaded. + if (Objects.isNull(mediaFileCount)) { + return; } + mediaFileCount.setBid(request.getBid()); + mediaFileCount.setTid(request.getTid()); + mediaFileCount.setUploadedCount(mediaFileCount.getUploadedCount() + 1); - String dockSn = receiver.getGateway(); - String jobId = map.get(MapKeyConst.FLIGHT_ID).toString(); - String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + dockSn; - MediaFileCountDTO countDTO = new MediaFileCountDTO(); - if (RedisOpsUtils.checkExist(key)) { - countDTO = (MediaFileCountDTO) RedisOpsUtils.get(key); - if (jobId.equals(countDTO.getJobId())) { - RedisOpsUtils.setWithExpire(key, countDTO, RedisConst.DEVICE_ALIVE_SECOND * 5); - return null; - } + // After all the files of the job are uploaded, delete the media file key. + if (mediaFileCount.getUploadedCount() >= mediaFileCount.getMediaCount()) { + mediaRedisService.delMediaCount(request.getGateway(), jobId); - countDTO.setPreJobId(countDTO.getJobId()); + // After uploading, delete the key with the highest priority. + MediaFileCountDTO fileCount = mediaRedisService.getMediaHighestPriority(request.getGateway()); + if (Objects.nonNull(fileCount) && jobId.equals(fileCount.getJobId())) { + mediaRedisService.delMediaHighestPriority(request.getGateway()); + } + } else { + mediaRedisService.setMediaCount(request.getGateway(), jobId, mediaFileCount); } - countDTO.setJobId(jobId); - RedisOpsUtils.setWithExpire(key, countDTO, RedisConst.DEVICE_ALIVE_SECOND * 5); + webSocketMessageService.sendBatch(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal(), + BizCodeEnum.FILE_UPLOAD_CALLBACK.getCode(), mediaFileCount); + } - Optional deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway()); - if (deviceOpt.isEmpty()) { + private MediaUploadCallbackRequest convert2callbackRequest(FileUploadCallbackFile file) { + if (Objects.isNull(file)) { return null; } - sendMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), - BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode(), countDTO); - - return receiver; + return new MediaUploadCallbackRequest() + .setExt(Optional.ofNullable(file.getExt()) + .map(ext -> new MediaFileExtension() + .setDroneModelKey(ext.getDroneModelKey()) + .setFileGroupId(ext.getFlightId()) + .setOriginal(ext.getOriginal()) + .setPayloadModelKey(ext.getPayloadModelKey())) + .orElse(new MediaFileExtension())) + .setMetadata(Optional.ofNullable(file.getMetadata()) + .map(data -> new MediaFileMetadata() + .setAbsoluteAltitude(data.getAbsoluteAltitude()) + .setGimbalYawDegree(data.getGimbalYawDegree()) + .setRelativeAltitude(data.getRelativeAltitude()) + .setShootPosition(data.getShootPosition()) + .setCreatedTime(data.getCreatedTime())) + .get()) + .setName(file.getName()) + .setObjectKey(file.getObjectKey()) + .setPath(file.getPath()); } } 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 01147e7..1097cb1 100644 --- a/src/main/java/com/dji/sample/storage/controller/StorageController.java +++ b/src/main/java/com/dji/sample/storage/controller/StorageController.java @@ -1,22 +1,22 @@ package com.dji.sample.storage.controller; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.media.model.StsCredentialsDTO; import com.dji.sample.storage.service.IStorageService; +import com.dji.sdk.cloudapi.storage.StsCredentialsResponse; +import com.dji.sdk.cloudapi.storage.api.IHttpStorageService; +import com.dji.sdk.common.HttpResultResponse; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + /** * @author sean * @version 0.3 * @date 2021/12/29 */ @RestController -@RequestMapping("${url.storage.prefix}${url.storage.version}/workspaces/") -public class StorageController { +public class StorageController implements IHttpStorageService { @Autowired private IStorageService storageService; @@ -26,11 +26,9 @@ public class StorageController { * @param workspaceId * @return */ - @PostMapping("/{workspace_id}/sts") - public ResponseResult getSTSCredentials(@PathVariable(name = "workspace_id") String workspaceId) { - - StsCredentialsDTO stsCredentials = storageService.getSTSCredentials(); - return ResponseResult.success(stsCredentials); + @Override + public HttpResultResponse getTemporaryCredential(String workspaceId, HttpServletRequest req, HttpServletResponse rsp) { + StsCredentialsResponse stsCredentials = storageService.getSTSCredentials(); + return HttpResultResponse.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 9bca0ef..4d86788 100644 --- a/src/main/java/com/dji/sample/storage/service/IStorageService.java +++ b/src/main/java/com/dji/sample/storage/service/IStorageService.java @@ -1,8 +1,6 @@ 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; +import com.dji.sdk.cloudapi.storage.StsCredentialsResponse; /** * @author sean @@ -15,12 +13,6 @@ public interface IStorageService { * Get custom temporary credentials object for uploading the media and wayline. * @return temporary credentials object */ - StsCredentialsDTO getSTSCredentials(); + StsCredentialsResponse 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 index 8c02072..6295400 100644 --- a/src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java +++ b/src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java @@ -1,14 +1,15 @@ 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 com.dji.sdk.cloudapi.media.StorageConfigGet; +import com.dji.sdk.cloudapi.media.api.AbstractMediaService; +import com.dji.sdk.cloudapi.storage.StsCredentialsResponse; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; 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; @@ -18,36 +19,24 @@ import org.springframework.stereotype.Service; * @date 2022/3/9 */ @Service -public class StorageServiceImpl implements IStorageService { - - @Autowired - private IMessageSenderService messageSender; +public class StorageServiceImpl extends AbstractMediaService implements IStorageService { @Autowired private OssServiceContext ossService; @Override - public StsCredentialsDTO getSTSCredentials() { - return StsCredentialsDTO.builder() - .endpoint(OssConfiguration.endpoint) - .bucket(OssConfiguration.bucket) - .credentials(ossService.getCredentials()) - .provider(OssConfiguration.provider) - .objectKeyPrefix(OssConfiguration.objectDirPrefix) - .region(OssConfiguration.region) - .build(); + public StsCredentialsResponse getSTSCredentials() { + return new StsCredentialsResponse() + .setEndpoint(OssConfiguration.endpoint) + .setBucket(OssConfiguration.bucket) + .setCredentials(ossService.getCredentials()) + .setProvider(OssConfiguration.provider) + .setObjectKeyPrefix(OssConfiguration.objectDirPrefix) + .setRegion(OssConfiguration.region); } @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); + public TopicRequestsResponse> storageConfigGet(TopicRequestsRequest response, MessageHeaders headers) { + return new TopicRequestsResponse>().setData(MqttReply.success(getSTSCredentials())); } } 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 918835e..7b877f4 100644 --- a/src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java +++ b/src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java @@ -1,23 +1,31 @@ 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.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 com.dji.sdk.cloudapi.device.DeviceEnum; +import com.dji.sdk.cloudapi.wayline.*; +import com.dji.sdk.cloudapi.wayline.api.IHttpWaylineService; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.io.IOException; import java.net.URL; import java.sql.SQLException; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; @@ -27,38 +35,52 @@ import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; * @date 2021/12/22 */ @RestController -@RequestMapping("${url.wayline.prefix}${url.wayline.version}/workspaces") -public class WaylineFileController { +public class WaylineFileController implements IHttpWaylineService { @Autowired private IWaylineFileService waylineFileService; + /** + * Delete the wayline file in the workspace according to the wayline id. + * @param workspaceId + * @param waylineId + * @return + */ + @DeleteMapping("${url.wayline.prefix}${url.wayline.version}/workspaces/{workspace_id}/waylines/{wayline_id}") + public HttpResultResponse deleteWayline(@PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "wayline_id") String waylineId) { + boolean isDel = waylineFileService.deleteByWaylineId(workspaceId, waylineId); + return isDel ? HttpResultResponse.success() : HttpResultResponse.error("Failed to delete wayline."); + } + + /** + * Import kmz wayline files. + * @param file + * @return + */ + @PostMapping("${url.wayline.prefix}${url.wayline.version}/workspaces/{workspace_id}/waylines/file/upload") + public HttpResultResponse importKmzFile(HttpServletRequest request, MultipartFile file) { + if (Objects.isNull(file)) { + return HttpResultResponse.error("No file received."); + } + CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); + String workspaceId = customClaim.getWorkspaceId(); + String creator = customClaim.getUsername(); + waylineFileService.importKmzFile(file, workspaceId, creator); + return HttpResultResponse.success(); + } + /** * Query the basic data of the wayline file according to the query conditions. * The query condition field in pilot is fixed. - * @param orderBy Sorted fields. Spliced at the end of the sql statement. - * @param favorited Whether the wayline file is favorited or not. - * @param page - * @param pageSize - * @param templateType + * @param request * @param workspaceId * @return */ - @GetMapping("/{workspace_id}/waylines") - public ResponseResult> getWaylinesPagination(@RequestParam(name = "order_by") String orderBy, - @RequestParam(required = false) boolean favorited, @RequestParam Integer page, - @RequestParam(name = "page_size", defaultValue = "10") Integer pageSize, - @RequestParam(name = "template_type", required = false) Integer[] templateType, - @PathVariable(name = "workspace_id") String workspaceId) { - WaylineQueryParam param = WaylineQueryParam.builder() - .favorited(favorited) - .page(page) - .pageSize(pageSize) - .orderBy(orderBy) - .templateType(templateType) - .build(); - PaginationData data = waylineFileService.getWaylinesByParam(workspaceId, param); - return ResponseResult.success(data); + @Override + public HttpResultResponse> getWaylineList(@Valid GetWaylineListRequest request, String workspaceId, HttpServletRequest req, HttpServletResponse rsp) { + PaginationData data = waylineFileService.getWaylinesByParam(workspaceId, request); + return HttpResultResponse.success(data); } /** @@ -66,44 +88,59 @@ public class WaylineFileController { * and redirect to this address directly for download. * @param workspaceId * @param waylineId - * @param response + * @param req + * @param rsp */ - @GetMapping("/{workspace_id}/waylines/{wayline_id}/url") - public void getFileUrl(@PathVariable(name = "workspace_id") String workspaceId, - @PathVariable(name = "wayline_id") String waylineId, HttpServletResponse response) { - + @Override + public void getWaylineFileDownloadAddress(String workspaceId, String waylineId, HttpServletRequest req, HttpServletResponse rsp) { try { URL url = waylineFileService.getObjectUrl(workspaceId, waylineId); - response.sendRedirect(url.toString()); + rsp.sendRedirect(url.toString()); } catch (IOException | SQLException e) { e.printStackTrace(); } } + /** + * Checking whether the name already exists according to the wayline name must ensure the uniqueness of the wayline name. + * This interface will be called when uploading waylines and must be available. + * @param workspaceId + * @param names + * @return + */ + @Override + public HttpResultResponse> getDuplicatedWaylineName(String workspaceId, @NotNull @Size(min = 1) List names, HttpServletRequest req, HttpServletResponse rsp) { + List existNamesList = waylineFileService.getDuplicateNames(workspaceId, names); + + return HttpResultResponse.success(existNamesList); + } + /** * When the wayline file is uploaded to the storage server by pilot, * the basic information of the file is reported through this interface. * @param request * @param workspaceId - * @param uploadFile * @return */ - @PostMapping("/{workspace_id}/upload-callback") - public ResponseResult uploadCallBack(HttpServletRequest request, - @PathVariable(name = "workspace_id") String workspaceId, - @RequestBody WaylineFileUploadDTO uploadFile) { - - CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); - - WaylineFileDTO metadata = uploadFile.getMetadata(); - metadata.setUsername(customClaim.getUsername()); - metadata.setObjectKey(uploadFile.getObjectKey()); - metadata.setName(uploadFile.getName()); + @Override + public HttpResultResponse fileUploadResultReport(String workspaceId, @Valid WaylineUploadCallbackRequest request, HttpServletRequest req, HttpServletResponse rsp) { + CustomClaim customClaim = (CustomClaim)req.getAttribute(TOKEN_CLAIM); + + WaylineUploadCallbackMetadata metadata = request.getMetadata(); + + WaylineFileDTO file = WaylineFileDTO.builder() + .username(customClaim.getUsername()) + .objectKey(request.getObjectKey()) + .name(request.getName()) + .templateTypes(metadata.getTemplateTypes().stream().map(WaylineTypeEnum::getValue).collect(Collectors.toList())) + .payloadModelKeys(metadata.getPayloadModelKeys().stream().map(DeviceEnum::getDevice).collect(Collectors.toList())) + .droneModelKey(metadata.getDroneModelKey().getDevice()) + .build(); - int id = waylineFileService.saveWaylineFile(workspaceId, metadata); + int id = waylineFileService.saveWaylineFile(workspaceId, file); - return id <= 0 ? ResponseResult.error() : ResponseResult.success(); + return id <= 0 ? HttpResultResponse.error() : HttpResultResponse.success(); } /** @@ -112,12 +149,11 @@ public class WaylineFileController { * @param ids wayline file id * @return */ - @PostMapping("/{workspace_id}/favorites") - public ResponseResult markFavorite(@PathVariable(name = "workspace_id") String workspaceId, - @RequestParam(name = "id") List ids) { + @Override + public HttpResultResponse batchFavoritesWayline(String workspaceId, @NotNull @Size(min = 1) List ids, HttpServletRequest req, HttpServletResponse rsp) { boolean isMark = waylineFileService.markFavorite(workspaceId, ids, true); - return isMark ? ResponseResult.success() : ResponseResult.error(); + return isMark ? HttpResultResponse.success() : HttpResultResponse.error(); } /** @@ -126,56 +162,10 @@ public class WaylineFileController { * @param ids wayline file id * @return */ - @DeleteMapping("/{workspace_id}/favorites") - public ResponseResult unmarkFavorite(@PathVariable(name = "workspace_id") String workspaceId, - @RequestParam(name = "id") List ids) { + @Override + public HttpResultResponse batchUnfavoritesWayline(String workspaceId, @NotNull @Size(min = 1) List ids, HttpServletRequest req, HttpServletResponse rsp) { boolean isMark = waylineFileService.markFavorite(workspaceId, ids, false); - return isMark ? ResponseResult.success() : ResponseResult.error(); - } - - /** - * Checking whether the name already exists according to the wayline name must ensure the uniqueness of the wayline name. - * This interface will be called when uploading waylines and must be available. - * @param workspaceId - * @param names - * @return - */ - @GetMapping("/{workspace_id}/waylines/duplicate-names") - public ResponseResult checkDuplicateNames(@PathVariable(name = "workspace_id") String workspaceId, - @RequestParam(name = "name") List names) { - List existNamesList = waylineFileService.getDuplicateNames(workspaceId, names); - - 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."); - } - - /** - * Import kmz wayline files. - * @param file - * @return - */ - @PostMapping("/{workspace_id}/waylines/file/upload") - public ResponseResult importKmzFile(HttpServletRequest request, MultipartFile file) { - if (Objects.isNull(file)) { - return ResponseResult.error("No file received."); - } - CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); - String workspaceId = customClaim.getWorkspaceId(); - String creator = customClaim.getUsername(); - waylineFileService.importKmzFile(file, workspaceId, creator); - return ResponseResult.success(); + return isMark ? HttpResultResponse.success() : HttpResultResponse.error(); } } diff --git a/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java b/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java index 481b6a2..5d8c259 100644 --- a/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java +++ b/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java @@ -1,12 +1,13 @@ 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.model.param.UpdateJobParam; +import com.dji.sample.wayline.service.IFlightTaskService; import com.dji.sample.wayline.service.IWaylineJobService; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -29,6 +30,9 @@ public class WaylineJobController { @Autowired private IWaylineJobService waylineJobService; + @Autowired + private IFlightTaskService flighttaskService; + /** * Create a wayline task for the Dock. * @param request @@ -38,12 +42,12 @@ public class WaylineJobController { * @throws SQLException */ @PostMapping("/{workspace_id}/flight-tasks") - public ResponseResult createJob(HttpServletRequest request, @Valid @RequestBody CreateJobParam param, - @PathVariable(name = "workspace_id") String workspaceId) throws SQLException { + public HttpResultResponse createJob(HttpServletRequest request, @Valid @RequestBody CreateJobParam param, + @PathVariable(name = "workspace_id") String workspaceId) throws SQLException { CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM); customClaim.setWorkspaceId(workspaceId); - return waylineJobService.publishFlightTask(param, customClaim); + return flighttaskService.publishFlightTask(param, customClaim); } /** @@ -54,11 +58,11 @@ public class WaylineJobController { * @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) { + public HttpResultResponse> 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); + return HttpResultResponse.success(data); } /** @@ -69,10 +73,10 @@ public class WaylineJobController { * @throws SQLException */ @DeleteMapping("/{workspace_id}/jobs") - public ResponseResult publishCancelJob(@RequestParam(name = "job_id") Set jobIds, - @PathVariable(name = "workspace_id") String workspaceId) throws SQLException { - waylineJobService.cancelFlightTask(workspaceId, jobIds); - return ResponseResult.success(); + public HttpResultResponse publishCancelJob(@RequestParam(name = "job_id") Set jobIds, + @PathVariable(name = "workspace_id") String workspaceId) throws SQLException { + flighttaskService.cancelFlightTask(workspaceId, jobIds); + return HttpResultResponse.success(); } /** @@ -82,17 +86,17 @@ public class WaylineJobController { * @return */ @PostMapping("/{workspace_id}/jobs/{job_id}/media-highest") - public ResponseResult uploadMediaHighestPriority(@PathVariable(name = "workspace_id") String workspaceId, - @PathVariable(name = "job_id") String jobId) { - waylineJobService.uploadMediaHighestPriority(workspaceId, jobId); - return ResponseResult.success(); + public HttpResultResponse uploadMediaHighestPriority(@PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "job_id") String jobId) { + flighttaskService.uploadMediaHighestPriority(workspaceId, jobId); + return HttpResultResponse.success(); } @PutMapping("/{workspace_id}/jobs/{job_id}") - public ResponseResult updateJobStatus(@PathVariable(name = "workspace_id") String workspaceId, - @PathVariable(name = "job_id") String jobId, - @Valid @RequestBody UpdateJobParam param) { - waylineJobService.updateJobStatus(workspaceId, jobId, param); - return ResponseResult.success(); + public HttpResultResponse updateJobStatus(@PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "job_id") String jobId, + @Valid @RequestBody UpdateJobParam param) { + flighttaskService.updateJobStatus(workspaceId, jobId, param); + return HttpResultResponse.success(); } } diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobKey.java b/src/main/java/com/dji/sample/wayline/model/dto/ConditionalWaylineJobKey.java similarity index 55% rename from src/main/java/com/dji/sample/wayline/model/dto/WaylineJobKey.java rename to src/main/java/com/dji/sample/wayline/model/dto/ConditionalWaylineJobKey.java index f19ed93..2dd1ca2 100644 --- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobKey.java +++ b/src/main/java/com/dji/sample/wayline/model/dto/ConditionalWaylineJobKey.java @@ -3,13 +3,15 @@ package com.dji.sample.wayline.model.dto; import com.dji.sample.component.redis.RedisConst; import lombok.Data; +import java.util.Objects; + /** * @author sean * @version 1.4 * @date 2023/3/28 */ @Data -public class WaylineJobKey { +public class ConditionalWaylineJobKey { private String workspaceId; @@ -17,17 +19,18 @@ public class WaylineJobKey { private String jobId; - public WaylineJobKey(String workspaceId, String dockSn, String jobId) { + public ConditionalWaylineJobKey(String workspaceId, String dockSn, String jobId) { this.workspaceId = workspaceId; this.dockSn = dockSn; this.jobId = jobId; } - private WaylineJobKey(String[] keyArr) { - this(keyArr[0], keyArr[1], keyArr[2]); - } - public WaylineJobKey(String key) { - this(key.split(RedisConst.DELIMITER)); + public ConditionalWaylineJobKey(String key) { + if (Objects.isNull(key)) { + return; + } + String[] keyArr = key.split(RedisConst.DELIMITER); + new ConditionalWaylineJobKey(keyArr[0], keyArr[1], keyArr[2]); } public String getKey() { diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgressExt.java b/src/main/java/com/dji/sample/wayline/model/dto/FlighttaskProgressExt.java similarity index 87% rename from src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgressExt.java rename to src/main/java/com/dji/sample/wayline/model/dto/FlighttaskProgressExt.java index bec5eaa..a04ea45 100644 --- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgressExt.java +++ b/src/main/java/com/dji/sample/wayline/model/dto/FlighttaskProgressExt.java @@ -8,7 +8,7 @@ import lombok.Data; * @date 2022/6/9 */ @Data -public class WaylineTaskProgressExt { +public class FlighttaskProgressExt { private Integer currentWaypointIndex; diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgress.java b/src/main/java/com/dji/sample/wayline/model/dto/FlighttaskProgressProgress.java similarity index 82% rename from src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgress.java rename to src/main/java/com/dji/sample/wayline/model/dto/FlighttaskProgressProgress.java index fa814e7..46e525d 100644 --- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgress.java +++ b/src/main/java/com/dji/sample/wayline/model/dto/FlighttaskProgressProgress.java @@ -8,7 +8,7 @@ import lombok.Data; * @date 2022/6/9 */ @Data -public class WaylineTaskProgress { +public class FlighttaskProgressProgress { private Integer currentStep; 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 deleted file mode 100644 index 61f7d01..0000000 --- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineFileUploadDTO.java +++ /dev/null @@ -1,18 +0,0 @@ -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 index 4f36608..83b6b0a 100644 --- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java +++ b/src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java @@ -1,7 +1,8 @@ package com.dji.sample.wayline.model.dto; -import com.dji.sample.wayline.model.enums.WaylineTaskTypeEnum; -import com.dji.sample.wayline.model.enums.WaylineTemplateTypeEnum; +import com.dji.sdk.cloudapi.wayline.OutOfControlActionEnum; +import com.dji.sdk.cloudapi.wayline.TaskTypeEnum; +import com.dji.sdk.cloudapi.wayline.WaylineTypeEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -34,9 +35,9 @@ public class WaylineJobDTO { private String workspaceId; - private WaylineTemplateTypeEnum waylineType; + private WaylineTypeEnum waylineType; - private WaylineTaskTypeEnum taskType; + private TaskTypeEnum taskType; private LocalDateTime executeTime; @@ -56,7 +57,7 @@ public class WaylineJobDTO { private Integer rthAltitude; - private Integer outOfControlAction; + private OutOfControlActionEnum outOfControlAction; private Integer mediaCount; diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskConditionDTO.java b/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskConditionDTO.java index 6a1dc18..c924b07 100644 --- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskConditionDTO.java +++ b/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskConditionDTO.java @@ -1,5 +1,7 @@ package com.dji.sample.wayline.model.dto; +import com.dji.sdk.cloudapi.wayline.ExecutableConditions; +import com.dji.sdk.cloudapi.wayline.ReadyConditions; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -16,7 +18,7 @@ import lombok.NoArgsConstructor; @AllArgsConstructor public class WaylineTaskConditionDTO { - private WaylineTaskReadyConditionDTO readyConditions; + private ReadyConditions readyConditions; - private WaylineTaskExecutableConditionDTO executableConditions; + private ExecutableConditions executableConditions; } diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskCreateDTO.java b/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskCreateDTO.java index 04cde89..3297a08 100644 --- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskCreateDTO.java +++ b/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskCreateDTO.java @@ -1,7 +1,5 @@ package com.dji.sample.wayline.model.dto; -import com.dji.sample.wayline.model.enums.WaylineTaskTypeEnum; -import com.dji.sample.wayline.model.enums.WaylineTemplateTypeEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -20,9 +18,9 @@ public class WaylineTaskCreateDTO { private String flightId; - private WaylineTaskTypeEnum taskType; + private Integer taskType; - private WaylineTemplateTypeEnum waylineType; + private Integer waylineType; private Long executeTime; diff --git a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgressReceiver.java b/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgressReceiver.java index b86bc9b..75671c4 100644 --- a/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgressReceiver.java +++ b/src/main/java/com/dji/sample/wayline/model/dto/WaylineTaskProgressReceiver.java @@ -10,9 +10,9 @@ import lombok.Data; @Data public class WaylineTaskProgressReceiver { - private WaylineTaskProgressExt ext; + private FlighttaskProgressExt ext; - private WaylineTaskProgress progress; + private FlighttaskProgressProgress progress; private String status; diff --git a/src/main/java/com/dji/sample/wayline/model/enums/WaylineErrorCodeEnum.java b/src/main/java/com/dji/sample/wayline/model/enums/WaylineErrorCodeEnum.java index 8643380..162c939 100644 --- a/src/main/java/com/dji/sample/wayline/model/enums/WaylineErrorCodeEnum.java +++ b/src/main/java/com/dji/sample/wayline/model/enums/WaylineErrorCodeEnum.java @@ -1,6 +1,6 @@ package com.dji.sample.wayline.model.enums; -import com.dji.sample.common.error.IErrorInfo; +import com.dji.sdk.common.IErrorInfo; import com.fasterxml.jackson.annotation.JsonCreator; import java.util.Arrays; @@ -63,12 +63,12 @@ public enum WaylineErrorCodeEnum implements IErrorInfo { } @Override - public String getErrorMsg() { + public String getMessage() { return msg; } @Override - public Integer getErrorCode() { + public Integer getCode() { return code; } diff --git a/src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java b/src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java deleted file mode 100644 index db81356..0000000 --- a/src/main/java/com/dji/sample/wayline/model/enums/WaylineTaskTypeEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.dji.sample.wayline.model.enums; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.Arrays; - -/** - * @author sean - * @version 1.3 - * @date 2022/9/26 - */ -public enum WaylineTaskTypeEnum { - - IMMEDIATE(0), - - TIMED(1), - - CONDITION(2); - - int val; - - WaylineTaskTypeEnum(int val) { - this.val = val; - } - - @JsonValue - public int getVal() { - return val; - } - - @JsonCreator - public static WaylineTaskTypeEnum find(Integer val) { - return Arrays.stream(values()).filter(taskTypeEnum -> taskTypeEnum.val == val).findAny().get(); - } -} diff --git a/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java b/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java index 558a9d2..f1cdedd 100644 --- a/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java +++ b/src/main/java/com/dji/sample/wayline/model/param/CreateJobParam.java @@ -1,7 +1,8 @@ package com.dji.sample.wayline.model.param; -import com.dji.sample.wayline.model.enums.WaylineTaskTypeEnum; -import com.dji.sample.wayline.model.enums.WaylineTemplateTypeEnum; +import com.dji.sdk.cloudapi.wayline.OutOfControlActionEnum; +import com.dji.sdk.cloudapi.wayline.TaskTypeEnum; +import com.dji.sdk.cloudapi.wayline.WaylineTypeEnum; import lombok.Data; import org.hibernate.validator.constraints.Range; @@ -27,18 +28,17 @@ public class CreateJobParam { private String dockSn; @NotNull - private WaylineTemplateTypeEnum waylineType; + private WaylineTypeEnum waylineType; @NotNull - private WaylineTaskTypeEnum taskType; + private TaskTypeEnum taskType; @Range(min = 20, max = 500) @NotNull private Integer rthAltitude; @NotNull - @Range(max = 2) - private Integer outOfControlAction; + private OutOfControlActionEnum outOfControlAction; @Range(min = 50, max = 90) private Integer minBatteryCapacity; diff --git a/src/main/java/com/dji/sample/wayline/service/IFlightTaskService.java b/src/main/java/com/dji/sample/wayline/service/IFlightTaskService.java index 1b0f4e2..79c4857 100644 --- a/src/main/java/com/dji/sample/wayline/service/IFlightTaskService.java +++ b/src/main/java/com/dji/sample/wayline/service/IFlightTaskService.java @@ -1,5 +1,16 @@ package com.dji.sample.wayline.service; +import com.dji.sample.common.model.CustomClaim; +import com.dji.sample.wayline.model.dto.ConditionalWaylineJobKey; +import com.dji.sample.wayline.model.dto.WaylineJobDTO; +import com.dji.sample.wayline.model.param.CreateJobParam; +import com.dji.sample.wayline.model.param.UpdateJobParam; +import com.dji.sdk.common.HttpResultResponse; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; + /** * @author sean * @version 1.1 @@ -8,4 +19,60 @@ package com.dji.sample.wayline.service; public interface IFlightTaskService { + /** + * Issue wayline mission to the dock. + * @param param + * @param customClaim user info + * @return + */ + HttpResultResponse publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException; + + /** + * Issue wayline mission to the dock. + * @param waylineJob + * @return + * @throws SQLException + */ + HttpResultResponse publishOneFlightTask(WaylineJobDTO waylineJob) throws SQLException; + + /** + * Execute the task immediately. + * @param jobId + * @throws SQLException + * @return + */ + Boolean executeFlightTask(String workspaceId, String jobId); + + /** + * Cancel the task Base on job Ids. + * @param workspaceId + * @param jobIds + * @throws SQLException + */ + void cancelFlightTask(String workspaceId, Collection jobIds); + + /** + * Cancel the dock tasks that have been issued but have not yet been executed. + * @param workspaceId + * @param dockSn + * @param jobIds + */ + void publishCancelTask(String workspaceId, String dockSn, List jobIds); + + /** + * Set the media files for this job to upload immediately. + * @param workspaceId + * @param jobId + */ + void uploadMediaHighestPriority(String workspaceId, String jobId); + + /** + * Manually control the execution status of wayline job. + * @param workspaceId + * @param jobId + * @param param + */ + void updateJobStatus(String workspaceId, String jobId, UpdateJobParam param); + + void retryPrepareJob(ConditionalWaylineJobKey jobKey, WaylineJobDTO waylineJob); } 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 4a722c8..03c74ef 100644 --- a/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java +++ b/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java @@ -1,8 +1,9 @@ package com.dji.sample.wayline.service; -import com.dji.sample.common.model.PaginationData; import com.dji.sample.wayline.model.dto.WaylineFileDTO; -import com.dji.sample.wayline.model.param.WaylineQueryParam; +import com.dji.sdk.cloudapi.wayline.GetWaylineListRequest; +import com.dji.sdk.cloudapi.wayline.GetWaylineListResponse; +import com.dji.sdk.common.PaginationData; import org.springframework.web.multipart.MultipartFile; import java.net.URL; @@ -23,7 +24,7 @@ public interface IWaylineFileService { * @param param * @return */ - PaginationData getWaylinesByParam(String workspaceId, WaylineQueryParam param); + PaginationData getWaylinesByParam(String workspaceId, GetWaylineListRequest param); /** * Query the information of this wayline file according to the wayline file id. @@ -31,7 +32,7 @@ public interface IWaylineFileService { * @param waylineId * @return */ - Optional getWaylineByWaylineId(String workspaceId, String waylineId); + Optional getWaylineByWaylineId(String workspaceId, String waylineId); /** * Get the download address of the file object. diff --git a/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java b/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java index 608c93f..1b5c84f 100644 --- a/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java +++ b/src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java @@ -1,16 +1,10 @@ package com.dji.sample.wayline.service; -import com.dji.sample.common.model.CustomClaim; -import com.dji.sample.common.model.PaginationData; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.CommonTopicReceiver; import com.dji.sample.wayline.model.dto.WaylineJobDTO; import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; import com.dji.sample.wayline.model.param.CreateJobParam; -import com.dji.sample.wayline.model.param.UpdateJobParam; -import org.springframework.messaging.MessageHeaders; +import com.dji.sdk.common.PaginationData; -import java.sql.SQLException; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -41,46 +35,6 @@ public interface IWaylineJobService { */ Optional createWaylineJobByParent(String workspaceId, String parentId); - /** - * Issue wayline mission to the dock. - * @param param - * @param customClaim user info - * @return - */ - ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException; - - /** - * Issue wayline mission to the dock. - * @param waylineJob - * @return - * @throws SQLException - */ - ResponseResult publishOneFlightTask(WaylineJobDTO waylineJob) throws SQLException; - - /** - * Execute the task immediately. - * @param jobId - * @throws SQLException - * @return - */ - Boolean executeFlightTask(String workspaceId, String jobId); - - /** - * Cancel the task Base on job Ids. - * @param workspaceId - * @param jobIds - * @throws SQLException - */ - void cancelFlightTask(String workspaceId, Collection jobIds); - - /** - * Cancel the dock tasks that have been issued but have not yet been executed. - * @param workspaceId - * @param dockSn - * @param jobIds - */ - void publishCancelTask(String workspaceId, String dockSn, List jobIds); - /** * Query wayline jobs based on conditions. * @param workspaceId @@ -114,28 +68,6 @@ public interface IWaylineJobService { */ PaginationData getJobsByWorkspaceId(String workspaceId, long page, long pageSize); - /** - * Process to get interface data of flight mission resources. - * @param receiver - * @param headers - */ - void flightTaskResourceGet(CommonTopicReceiver receiver, MessageHeaders headers); - - /** - * Set the media files for this job to upload immediately. - * @param workspaceId - * @param jobId - */ - void uploadMediaHighestPriority(String workspaceId, String jobId); - - /** - * Manually control the execution status of wayline job. - * @param workspaceId - * @param jobId - * @param param - */ - void updateJobStatus(String workspaceId, String jobId, UpdateJobParam param); - /** * Query the wayline execution status of the dock. * @param dockSn diff --git a/src/main/java/com/dji/sample/wayline/service/IWaylineRedisService.java b/src/main/java/com/dji/sample/wayline/service/IWaylineRedisService.java index 0f8408c..7230c50 100644 --- a/src/main/java/com/dji/sample/wayline/service/IWaylineRedisService.java +++ b/src/main/java/com/dji/sample/wayline/service/IWaylineRedisService.java @@ -1,9 +1,9 @@ package com.dji.sample.wayline.service; import com.dji.sample.component.mqtt.model.EventsReceiver; +import com.dji.sample.wayline.model.dto.ConditionalWaylineJobKey; import com.dji.sample.wayline.model.dto.WaylineJobDTO; -import com.dji.sample.wayline.model.dto.WaylineJobKey; -import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; +import com.dji.sdk.cloudapi.wayline.FlighttaskProgress; import java.util.Optional; @@ -19,14 +19,14 @@ public interface IWaylineRedisService { * @param dockSn * @param data */ - void setRunningWaylineJob(String dockSn, EventsReceiver data); + void setRunningWaylineJob(String dockSn, EventsReceiver data); /** * Query the status of wayline job performed by the dock in redis. * @param dockSn * @return */ - Optional> getRunningWaylineJob(String dockSn); + Optional> getRunningWaylineJob(String dockSn); /** * Delete the wayline job status of the dock operation in redis. @@ -70,13 +70,6 @@ public interface IWaylineRedisService { */ String getBlockedWaylineJobId(String dockSn); - /** - * Delete the wayline job id blocked by the dock in redis. - * @param dockSn - * @return - */ - Boolean delBlockedWaylineJobId(String dockSn); - /** * Save the conditional wayline job by the dock to redis. * @param waylineJob @@ -97,30 +90,11 @@ public interface IWaylineRedisService { */ Boolean delConditionalWaylineJob(String jobId); - /** - * Add the wayline job that needs to be issued. - * @param waylineJob - * @return - */ - Boolean addPreparedWaylineJob(WaylineJobDTO waylineJob); + Boolean addPrepareConditionalWaylineJob(WaylineJobDTO waylineJob); - /** - * Get the latest wayline job that needs to be issued. - * @return - */ - Optional getNearestPreparedWaylineJob(); + Optional getNearestConditionalWaylineJob(); - /** - * Get the time when the wayline job is issued. - * @param jobKey - * @return - */ - Double getPreparedWaylineJobTime(WaylineJobKey jobKey); + Double getConditionalWaylineJobTime(ConditionalWaylineJobKey jobKey); - /** - * Delete the wayline job that needs to be issued in redis. - * @param jobKey - * @return - */ - Boolean removePreparedWaylineJob(WaylineJobKey jobKey); + Boolean removePrepareConditionalWaylineJob(ConditionalWaylineJobKey jobKey); } diff --git a/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java b/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java index 893b0bc..938cd80 100644 --- a/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java +++ b/src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java @@ -1,40 +1,54 @@ package com.dji.sample.wayline.service.impl; import com.dji.sample.common.error.CommonErrorEnum; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.IMessageSenderService; +import com.dji.sample.common.model.CustomClaim; +import com.dji.sample.component.mqtt.model.EventsReceiver; 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.service.ISendMessageService; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.service.IDeviceRedisService; import com.dji.sample.media.model.MediaFileCountDTO; +import com.dji.sample.media.service.IMediaRedisService; +import com.dji.sample.wayline.model.dto.ConditionalWaylineJobKey; import com.dji.sample.wayline.model.dto.WaylineJobDTO; -import com.dji.sample.wayline.model.dto.WaylineJobKey; -import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; +import com.dji.sample.wayline.model.dto.WaylineTaskConditionDTO; +import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; -import com.dji.sample.wayline.model.enums.WaylineTaskTypeEnum; +import com.dji.sample.wayline.model.param.CreateJobParam; +import com.dji.sample.wayline.model.param.UpdateJobParam; import com.dji.sample.wayline.service.IFlightTaskService; +import com.dji.sample.wayline.service.IWaylineFileService; import com.dji.sample.wayline.service.IWaylineJobService; import com.dji.sample.wayline.service.IWaylineRedisService; -import com.fasterxml.jackson.core.type.TypeReference; +import com.dji.sdk.cloudapi.device.ExitWaylineWhenRcLostEnum; +import com.dji.sdk.cloudapi.media.UploadFlighttaskMediaPrioritize; +import com.dji.sdk.cloudapi.media.api.AbstractMediaService; +import com.dji.sdk.cloudapi.wayline.*; +import com.dji.sdk.cloudapi.wayline.api.AbstractWaylineService; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpStatus; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.messaging.MessageHeaders; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import java.time.LocalDateTime; -import java.time.ZoneId; +import java.net.URL; +import java.sql.SQLException; +import java.time.*; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * @author sean @@ -43,16 +57,13 @@ import java.util.concurrent.TimeUnit; */ @Service @Slf4j -public class FlightTaskServiceImpl implements IFlightTaskService { - - @Autowired - private IMessageSenderService messageSender; +public class FlightTaskServiceImpl extends AbstractWaylineService implements IFlightTaskService { @Autowired private ObjectMapper mapper; @Autowired - private ISendMessageService websocketMessageService; + private IWebSocketMessageService websocketMessageService; @Autowired private IWaylineJobService waylineJobService; @@ -63,205 +74,427 @@ public class FlightTaskServiceImpl implements IFlightTaskService { @Autowired private IWaylineRedisService waylineRedisService; - /** - * Handle the progress messages of the flight tasks reported by the dock. - * @param receiver - * @param headers - */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleProgress(CommonTopicReceiver receiver, MessageHeaders headers) { - EventsReceiver eventsReceiver = mapper.convertValue(receiver.getData(), - new TypeReference>(){}); - eventsReceiver.setBid(receiver.getBid()); - eventsReceiver.setSn(receiver.getGateway()); + @Autowired + private IMediaRedisService mediaRedisService; - WaylineTaskProgressReceiver output = eventsReceiver.getOutput(); + @Autowired + private IWaylineFileService waylineFileService; - log.info("Task progress: {}", output.getProgress().toString()); + @Autowired + private SDKWaylineService abstractWaylineService; + + @Autowired + @Qualifier("mediaServiceImpl") + private AbstractMediaService abstractMediaService; - if (eventsReceiver.getResult() != ResponseResult.CODE_SUCCESS) { - log.error("Task progress ===> Error code: " + eventsReceiver.getResult()); + @Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS) + public void checkScheduledJob() { + Object jobIdValue = RedisOpsUtils.zGetMin(RedisConst.WAYLINE_JOB_TIMED_EXECUTE); + if (Objects.isNull(jobIdValue)) { + return; + } + log.info("Check the timed tasks of the wayline. {}", jobIdValue); + // format: {workspace_id}:{dock_sn}:{job_id} + String[] jobArr = String.valueOf(jobIdValue).split(RedisConst.DELIMITER); + double time = RedisOpsUtils.zScore(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, jobIdValue); + long now = System.currentTimeMillis(); + int offset = 30_000; + + // Expired tasks are deleted directly. + if (time < now - offset) { + RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, jobIdValue); + waylineJobService.updateJob(WaylineJobDTO.builder() + .jobId(jobArr[2]) + .status(WaylineJobStatusEnum.FAILED.getVal()) + .executeTime(LocalDateTime.now()) + .completedTime(LocalDateTime.now()) + .code(HttpStatus.SC_REQUEST_TIMEOUT).build()); + return; } - EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus()); - waylineRedisService.setRunningWaylineJob(receiver.getGateway(), eventsReceiver); + if (now <= time && time <= now + offset) { + try { + this.executeFlightTask(jobArr[0], jobArr[2]); + } catch (Exception e) { + log.info("The scheduled task delivery failed."); + waylineJobService.updateJob(WaylineJobDTO.builder() + .jobId(jobArr[2]) + .status(WaylineJobStatusEnum.FAILED.getVal()) + .executeTime(LocalDateTime.now()) + .completedTime(LocalDateTime.now()) + .code(HttpStatus.SC_INTERNAL_SERVER_ERROR).build()); + } finally { + RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, jobIdValue); + } + } + } - Optional deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway()); - if (deviceOpt.isEmpty()) { - return null; + @Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS) + public void prepareConditionJob() { + Optional jobKeyOpt = waylineRedisService.getNearestConditionalWaylineJob(); + if (jobKeyOpt.isEmpty()) { + return; } + ConditionalWaylineJobKey jobKey = jobKeyOpt.get(); + log.info("Check the conditional tasks of the wayline. {}", jobKey.toString()); + // format: {workspace_id}:{dock_sn}:{job_id} + double time = waylineRedisService.getConditionalWaylineJobTime(jobKey); + long now = System.currentTimeMillis(); + // prepare the task one day in advance. + int offset = 86_400_000; - if (statusEnum.getEnd()) { - handleEndStatus(receiver, statusEnum, output.getExt().getMediaCount(), eventsReceiver.getResult(), deviceOpt.get()); + if (now + offset < time) { + return; } - websocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), - BizCodeEnum.FLIGHT_TASK_PROGRESS.getCode(), eventsReceiver); + WaylineJobDTO job = WaylineJobDTO.builder() + .jobId(jobKey.getJobId()) + .status(WaylineJobStatusEnum.FAILED.getVal()) + .executeTime(LocalDateTime.now()) + .completedTime(LocalDateTime.now()) + .code(HttpStatus.SC_INTERNAL_SERVER_ERROR).build(); + try { + Optional waylineJobOpt = waylineRedisService.getConditionalWaylineJob(jobKey.getJobId()); + if (waylineJobOpt.isEmpty()) { + job.setCode(CommonErrorEnum.REDIS_DATA_NOT_FOUND.getCode()); + waylineJobService.updateJob(job); + waylineRedisService.removePrepareConditionalWaylineJob(jobKey); + return; + } + WaylineJobDTO waylineJob = waylineJobOpt.get(); - return receiver; - } + HttpResultResponse result = this.publishOneFlightTask(waylineJob); + waylineRedisService.removePrepareConditionalWaylineJob(jobKey); - private void handleEndStatus(CommonTopicReceiver receiver, EventsResultStatusEnum statusEnum, int mediaCount, int code, DeviceDTO dock) { + if (HttpResultResponse.CODE_SUCCESS == result.getCode()) { + return; + } - WaylineJobDTO job = WaylineJobDTO.builder() - .jobId(receiver.getBid()) - .status(WaylineJobStatusEnum.SUCCESS.getVal()) - .completedTime(LocalDateTime.now()) - .mediaCount(mediaCount) - .build(); + // If the end time is exceeded, no more retries will be made. + waylineRedisService.delConditionalWaylineJob(jobKey.getJobId()); + if (waylineJob.getEndTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - RedisConst.WAYLINE_JOB_BLOCK_TIME * 1000 < now) { + return; + } - // record the update of the media count. - if (Objects.nonNull(job.getMediaCount()) && job.getMediaCount() != 0) { - RedisOpsUtils.hashSet(RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway(), job.getJobId(), - MediaFileCountDTO.builder().jobId(receiver.getBid()).mediaCount(job.getMediaCount()).uploadedCount(0).build()); - } + // Retry if the end time has not been exceeded. + this.retryPrepareJob(jobKey, waylineJob); - if (EventsResultStatusEnum.OK != statusEnum) { - job.setCode(code); - job.setStatus(WaylineJobStatusEnum.FAILED.getVal()); + } catch (Exception e) { + log.info("Failed to prepare the conditional task."); + waylineJobService.updateJob(job); } - waylineRedisService.getConditionalWaylineJob(receiver.getBid()).ifPresent(waylineJob -> - retryPrepareConditionJob(new WaylineJobKey(dock.getWorkspaceId(), dock.getDeviceSn(), receiver.getBid()), waylineJob)); - waylineJobService.updateJob(job); - waylineRedisService.delRunningWaylineJob(receiver.getGateway()); - waylineRedisService.delPausedWaylineJob(receiver.getBid()); - waylineRedisService.delBlockedWaylineJobId(receiver.getGateway()); - } /** - * Notifications will be received through this interface when tasks are ready on the device. - * @param receiver - * @param headers + * For immediate tasks, the server time shall prevail. + * @param param */ - @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_READY, outputChannel = ChannelName.OUTBOUND_EVENTS) - public CommonTopicReceiver handleTaskNotifications(CommonTopicReceiver receiver, MessageHeaders headers) { - String dockSn = receiver.getGateway(); - Set flightIds = mapper.convertValue(receiver.getData(), - new TypeReference>>(){}).get(MapKeyConst.FLIGHT_IDS); - - log.info("ready task list:{}", Arrays.toString(flightIds.toArray())); - // Check conditional task blocking status. - String blockedId = waylineRedisService.getBlockedWaylineJobId(dockSn); - if (StringUtils.hasText(blockedId)) { - log.info("The dock is in a state of wayline congestion, and the task will not be executed."); - return null; + private void fillImmediateTime(CreateJobParam param) { + if (TaskTypeEnum.IMMEDIATE != param.getTaskType()) { + return; } + long now = System.currentTimeMillis() / 1000; + param.setTaskDays(List.of(now)); + param.setTaskPeriods(List.of(List.of(now))); + } - Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); - if (deviceOpt.isEmpty()) { - log.info("The dock is offline."); - return null; + + private void addConditions(WaylineJobDTO waylineJob, CreateJobParam param, Long beginTime, Long endTime) { + if (TaskTypeEnum.CONDITIONAL != param.getTaskType()) { + return; } - DeviceDTO device = deviceOpt.get(); - Optional jobOpt = waylineJobService.getJobsByConditions(device.getWorkspaceId(), flightIds, WaylineJobStatusEnum.PENDING) - .stream().filter(job -> flightIds.contains(job.getJobId())) - .sorted(Comparator.comparingInt(a -> a.getTaskType().getVal())) - .min(Comparator.comparing(WaylineJobDTO::getBeginTime)); - if (jobOpt.isEmpty()) { - return receiver; + + waylineJob.setConditions( + WaylineTaskConditionDTO.builder() + .executableConditions(Objects.nonNull(param.getMinStorageCapacity()) ? + new ExecutableConditions().setStorageCapacity(param.getMinStorageCapacity()) : null) + .readyConditions(new ReadyConditions() + .setBatteryCapacity(param.getMinBatteryCapacity()) + .setBeginTime(beginTime) + .setEndTime(endTime)) + .build()); + + waylineRedisService.setConditionalWaylineJob(waylineJob); + // key: wayline_job_condition, value: {workspace_id}:{dock_sn}:{job_id} + boolean isAdd = waylineRedisService.addPrepareConditionalWaylineJob(waylineJob); + if (!isAdd) { + throw new RuntimeException("Failed to create conditional job."); } - executeReadyTask(jobOpt.get()); + } - return receiver; + @Override + public HttpResultResponse publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException { + fillImmediateTime(param); + + for (Long taskDay : param.getTaskDays()) { + LocalDate date = LocalDate.ofInstant(Instant.ofEpochSecond(taskDay), ZoneId.systemDefault()); + for (List taskPeriod : param.getTaskPeriods()) { + long beginTime = LocalDateTime.of(date, LocalTime.ofInstant(Instant.ofEpochSecond(taskPeriod.get(0)), ZoneId.systemDefault())) + .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + long endTime = taskPeriod.size() > 1 ? + LocalDateTime.of(date, LocalTime.ofInstant(Instant.ofEpochSecond(taskPeriod.get(1)), ZoneId.systemDefault())) + .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : beginTime; + if (TaskTypeEnum.IMMEDIATE != param.getTaskType() && endTime < System.currentTimeMillis()) { + continue; + } + + Optional waylineJobOpt = waylineJobService.createWaylineJob(param, customClaim.getWorkspaceId(), customClaim.getUsername(), beginTime, endTime); + if (waylineJobOpt.isEmpty()) { + throw new SQLException("Failed to create wayline job."); + } + WaylineJobDTO waylineJob = waylineJobOpt.get(); + // If it is a conditional task type, add conditions to the job parameters. + addConditions(waylineJob, param, beginTime, endTime); + + HttpResultResponse response = this.publishOneFlightTask(waylineJob); + if (HttpResultResponse.CODE_SUCCESS != response.getCode()) { + return response; + } + } + } + return HttpResultResponse.success(); } - private void executeReadyTask(WaylineJobDTO waylineJob) { - try { - boolean isExecute = waylineJobService.executeFlightTask(waylineJob.getWorkspaceId(), waylineJob.getJobId()); - if (isExecute || WaylineTaskTypeEnum.CONDITION != waylineJob.getTaskType()) { - return; + public HttpResultResponse publishOneFlightTask(WaylineJobDTO waylineJob) throws SQLException { + + boolean isOnline = deviceRedisService.checkDeviceOnline(waylineJob.getDockSn()); + if (!isOnline) { + throw new RuntimeException("Dock is offline."); + } + + boolean isSuccess = this.prepareFlightTask(waylineJob); + if (!isSuccess) { + return HttpResultResponse.error("Failed to prepare job."); + } + + // Issue an immediate task execution command. + if (TaskTypeEnum.IMMEDIATE == waylineJob.getTaskType()) { + if (!executeFlightTask(waylineJob.getWorkspaceId(), waylineJob.getJobId())) { + return HttpResultResponse.error("Failed to execute job."); } - Optional waylineJobOpt = waylineRedisService.getConditionalWaylineJob(waylineJob.getJobId()); - if (waylineJobOpt.isEmpty()) { - log.info("The conditional job has expired and will no longer be executed."); - return; + } + + if (TaskTypeEnum.TIMED == waylineJob.getTaskType()) { + // key: wayline_job_timed, value: {workspace_id}:{dock_sn}:{job_id} + boolean isAdd = RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, + waylineJob.getWorkspaceId() + RedisConst.DELIMITER + waylineJob.getDockSn() + RedisConst.DELIMITER + waylineJob.getJobId(), + waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + if (!isAdd) { + return HttpResultResponse.error("Failed to create scheduled job."); } - waylineJob = waylineJobOpt.get(); - this.retryPrepareConditionJob(new WaylineJobKey(waylineJob.getWorkspaceId(), waylineJob.getDockSn(), waylineJob.getJobId()), waylineJob); - } catch (Exception e) { - log.error("Failed to execute task. ID: {}, Name:{}", waylineJob.getJobId(), waylineJob.getJobName()); - this.retryPrepareConditionJob(new WaylineJobKey(waylineJob.getWorkspaceId(), waylineJob.getDockSn(), waylineJob.getJobId()), waylineJob); - e.printStackTrace(); } + + return HttpResultResponse.success(); } - @Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS) - private void prepareWaylineJob() { - Optional jobKeyOpt = waylineRedisService.getNearestPreparedWaylineJob(); - if (jobKeyOpt.isEmpty()) { - return; + private Boolean prepareFlightTask(WaylineJobDTO waylineJob) throws SQLException { + // get wayline file + Optional waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId()); + if (waylineFile.isEmpty()) { + throw new SQLException("Wayline file doesn't exist."); } - // format: {workspace_id}:{dock_sn}:{job_id} - WaylineJobKey jobKey = jobKeyOpt.get(); - log.info("Check the prepared tasks of the wayline. {}", jobKey.toString()); - WaylineJobDTO job = WaylineJobDTO.builder() - .jobId(jobKey.getJobId()) - .status(WaylineJobStatusEnum.FAILED.getVal()) - .executeTime(LocalDateTime.now()) - .completedTime(LocalDateTime.now()) - .code(HttpStatus.SC_INTERNAL_SERVER_ERROR).build(); - Optional waylineJobOpt = getPreparedJob(jobKey, job); - if (waylineJobOpt.isEmpty()) { - return; + // get file url + URL url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getId()); + + FlighttaskPrepareRequest flightTask = new FlighttaskPrepareRequest() + .setFlightId(waylineJob.getJobId()) + .setExecuteTime(waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) + .setTaskType(waylineJob.getTaskType()) + .setWaylineType(waylineJob.getWaylineType()) + .setRthAltitude(waylineJob.getRthAltitude()) + .setOutOfControlAction(waylineJob.getOutOfControlAction()) + .setExitWaylineWhenRcLost(ExitWaylineWhenRcLostEnum.EXECUTE_RC_LOST_ACTION) + .setFile(new FlighttaskFile() + .setUrl(url.toString()) + .setFingerprint(waylineFile.get().getSign())); + + if (TaskTypeEnum.CONDITIONAL == waylineJob.getTaskType()) { + if (Objects.isNull(waylineJob.getConditions())) { + throw new IllegalArgumentException(); + } + flightTask.setReadyConditions(waylineJob.getConditions().getReadyConditions()); + flightTask.setExecutableConditions(waylineJob.getConditions().getExecutableConditions()); } - WaylineJobDTO waylineJob = waylineJobOpt.get(); - try { - ResponseResult result = waylineJobService.publishOneFlightTask(waylineJob); - if (ResponseResult.CODE_SUCCESS == result.getCode()) { - return; + TopicServicesResponse serviceReply = abstractWaylineService.flighttaskPrepare( + SDKManager.getDeviceSDK(waylineJob.getDockSn()), flightTask); + if (!serviceReply.getData().getResult().isSuccess()) { + log.info("Prepare task ====> Error code: {}", serviceReply.getData().getResult()); + waylineJobService.updateJob(WaylineJobDTO.builder() + .workspaceId(waylineJob.getWorkspaceId()) + .jobId(waylineJob.getJobId()) + .executeTime(LocalDateTime.now()) + .status(WaylineJobStatusEnum.FAILED.getVal()) + .completedTime(LocalDateTime.now()) + .code(serviceReply.getData().getResult().getCode()).build()); + return false; + } + return true; + } + + + @Override + public Boolean executeFlightTask(String workspaceId, String jobId) { + // get job + Optional waylineJob = waylineJobService.getJobByJobId(workspaceId, jobId); + if (waylineJob.isEmpty()) { + throw new IllegalArgumentException("Job doesn't exist."); + } + + boolean isOnline = deviceRedisService.checkDeviceOnline(waylineJob.get().getDockSn()); + if (!isOnline) { + throw new RuntimeException("Dock is offline."); + } + + WaylineJobDTO job = waylineJob.get(); + + TopicServicesResponse serviceReply = abstractWaylineService.flighttaskExecute( + SDKManager.getDeviceSDK(job.getDockSn()), new FlighttaskExecuteRequest().setFlightId(jobId)); + if (!serviceReply.getData().getResult().isSuccess()) { + log.info("Execute job ====> Error: {}", serviceReply.getData().getResult()); + waylineJobService.updateJob(WaylineJobDTO.builder() + .jobId(jobId) + .executeTime(LocalDateTime.now()) + .status(WaylineJobStatusEnum.FAILED.getVal()) + .completedTime(LocalDateTime.now()) + .code(serviceReply.getData().getResult().getCode()).build()); + // The conditional task fails and enters the blocking status. + if (TaskTypeEnum.CONDITIONAL == job.getTaskType() + && WaylineErrorCodeEnum.find(serviceReply.getData().getResult().getCode()).isBlock()) { + waylineRedisService.setBlockedWaylineJob(job.getDockSn(), jobId); } - log.info("Failed to prepare the task. {}", result.getMessage()); - job.setCode(result.getCode()); - waylineJobService.updateJob(job); - // Retry if the end time has not been exceeded. - this.retryPrepareConditionJob(jobKey, waylineJob); - } catch (Exception e) { - log.info("Failed to prepare the task. {}", e.getLocalizedMessage()); - waylineJobService.updateJob(job); - this.retryPrepareConditionJob(jobKey, waylineJob); + return false; } + + waylineJobService.updateJob(WaylineJobDTO.builder() + .jobId(jobId) + .executeTime(LocalDateTime.now()) + .status(WaylineJobStatusEnum.IN_PROGRESS.getVal()) + .build()); + waylineRedisService.setRunningWaylineJob(job.getDockSn(), EventsReceiver.builder().bid(jobId).sn(job.getDockSn()).build()); + return true; } - private boolean checkTime(long time) { - // prepare the task one day in advance. - int offset = 86_400_000; - return System.currentTimeMillis() + offset >= time; + @Override + public void cancelFlightTask(String workspaceId, Collection jobIds) { + List waylineJobs = waylineJobService.getJobsByConditions(workspaceId, jobIds, WaylineJobStatusEnum.PENDING); + + Set waylineJobIds = waylineJobs.stream().map(WaylineJobDTO::getJobId).collect(Collectors.toSet()); + // Check if the task status is correct. + boolean isErr = !jobIds.removeAll(waylineJobIds) || !jobIds.isEmpty() ; + if (isErr) { + throw new IllegalArgumentException("These tasks have an incorrect status and cannot be canceled. " + Arrays.toString(jobIds.toArray())); + } + + // Group job id by dock sn. + Map> dockJobs = waylineJobs.stream() + .collect(Collectors.groupingBy(WaylineJobDTO::getDockSn, + Collectors.mapping(WaylineJobDTO::getJobId, Collectors.toList()))); + dockJobs.forEach((dockSn, idList) -> this.publishCancelTask(workspaceId, dockSn, idList)); + + } + + public void publishCancelTask(String workspaceId, String dockSn, List jobIds) { + boolean isOnline = deviceRedisService.checkDeviceOnline(dockSn); + if (!isOnline) { + throw new RuntimeException("Dock is offline."); + } + + TopicServicesResponse serviceReply = abstractWaylineService.flighttaskUndo(SDKManager.getDeviceSDK(dockSn), + new FlighttaskUndoRequest().setFlightIds(jobIds)); + if (!serviceReply.getData().getResult().isSuccess()) { + log.info("Cancel job ====> Error: {}", serviceReply.getData().getResult()); + throw new RuntimeException("Failed to cancel the wayline job of " + dockSn); + } + + for (String jobId : jobIds) { + waylineJobService.updateJob(WaylineJobDTO.builder() + .workspaceId(workspaceId) + .jobId(jobId) + .status(WaylineJobStatusEnum.CANCEL.getVal()) + .completedTime(LocalDateTime.now()) + .build()); + RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, workspaceId + RedisConst.DELIMITER + dockSn + RedisConst.DELIMITER + jobId); + } + } - private Optional getPreparedJob(WaylineJobKey jobKey, WaylineJobDTO job) { - long time = waylineRedisService.getPreparedWaylineJobTime(jobKey).longValue(); - if (!checkTime(time)) { - return Optional.empty(); + @Override + public void uploadMediaHighestPriority(String workspaceId, String jobId) { + Optional jobOpt = waylineJobService.getJobByJobId(workspaceId, jobId); + if (jobOpt.isEmpty()) { + throw new RuntimeException(CommonErrorEnum.ILLEGAL_ARGUMENT.getMessage()); + } + + String dockSn = jobOpt.get().getDockSn(); + String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + dockSn; + if (RedisOpsUtils.checkExist(key) && jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(key)).getJobId())) { + return; } - Optional waylineJobOpt = waylineRedisService.getConditionalWaylineJob(jobKey.getJobId()); - // Determine whether the conditional task or the scheduled task has expired. + TopicServicesResponse reply = abstractMediaService.uploadFlighttaskMediaPrioritize( + SDKManager.getDeviceSDK(dockSn), new UploadFlighttaskMediaPrioritize().setFlightId(jobId)); + if (!reply.getData().getResult().isSuccess()) { + throw new RuntimeException("Failed to set media job upload priority. Error: " + reply.getData().getResult()); + } + } + + @Override + public void updateJobStatus(String workspaceId, String jobId, UpdateJobParam param) { + Optional waylineJobOpt = waylineJobService.getJobByJobId(workspaceId, jobId); if (waylineJobOpt.isEmpty()) { - waylineJobOpt = waylineJobService.getJobByJobId(jobKey.getWorkspaceId(), jobKey.getJobId()); - if (waylineJobOpt.isEmpty() || waylineJobOpt.get().getEndTime().isBefore(LocalDateTime.now())) { - job.setCode(CommonErrorEnum.REDIS_DATA_NOT_FOUND.getErrorCode()); - waylineJobService.updateJob(job); - return Optional.empty(); - } + throw new RuntimeException("The job does not exist."); } - waylineRedisService.removePreparedWaylineJob(jobKey); - return waylineJobOpt; + WaylineJobDTO waylineJob = waylineJobOpt.get(); + WaylineJobStatusEnum statusEnum = waylineJobService.getWaylineState(waylineJob.getDockSn()); + if (statusEnum.getEnd() || WaylineJobStatusEnum.PENDING == statusEnum) { + throw new RuntimeException("The wayline job status does not match, and the operation cannot be performed."); + } + + switch (param.getStatus()) { + case PAUSE: + pauseJob(workspaceId, waylineJob.getDockSn(), jobId, statusEnum); + break; + case RESUME: + resumeJob(workspaceId, waylineJob.getDockSn(), jobId, statusEnum); + break; + } + } - private void retryPrepareConditionJob(WaylineJobKey jobKey, WaylineJobDTO waylineJob) { - if (WaylineTaskTypeEnum.CONDITION != waylineJob.getTaskType()) { + private void pauseJob(String workspaceId, String dockSn, String jobId, WaylineJobStatusEnum statusEnum) { + if (WaylineJobStatusEnum.PAUSED == statusEnum && jobId.equals(waylineRedisService.getPausedWaylineJobId(dockSn))) { + waylineRedisService.setPausedWaylineJob(dockSn, jobId); return; } - // If the end time is exceeded, no more retries will be made. - waylineRedisService.delConditionalWaylineJob(jobKey.getJobId()); - if (waylineJob.getEndTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() < System.currentTimeMillis()) { + + TopicServicesResponse reply = abstractWaylineService.flighttaskPause(SDKManager.getDeviceSDK(dockSn)); + if (!reply.getData().getResult().isSuccess()) { + throw new RuntimeException("Failed to pause wayline job. Error: " + reply.getData().getResult()); + } + waylineRedisService.delRunningWaylineJob(dockSn); + waylineRedisService.setPausedWaylineJob(dockSn, jobId); + } + + private void resumeJob(String workspaceId, String dockSn, String jobId, WaylineJobStatusEnum statusEnum) { + Optional> runningDataOpt = waylineRedisService.getRunningWaylineJob(dockSn); + if (WaylineJobStatusEnum.IN_PROGRESS == statusEnum && jobId.equals(runningDataOpt.map(EventsReceiver::getSn).get())) { + waylineRedisService.setRunningWaylineJob(dockSn, runningDataOpt.get()); return; } + TopicServicesResponse reply = abstractWaylineService.flighttaskRecovery(SDKManager.getDeviceSDK(dockSn)); + if (!reply.getData().getResult().isSuccess()) { + throw new RuntimeException("Failed to resume wayline job. Error: " + reply.getData().getResult()); + } + runningDataOpt.ifPresent(runningData -> waylineRedisService.setRunningWaylineJob(dockSn, runningData)); + waylineRedisService.delPausedWaylineJob(dockSn); + } + + @Override + public void retryPrepareJob(ConditionalWaylineJobKey jobKey, WaylineJobDTO waylineJob) { Optional childJobOpt = waylineJobService.createWaylineJobByParent(jobKey.getWorkspaceId(), jobKey.getJobId()); if (childJobOpt.isEmpty()) { log.error("Failed to create wayline job."); @@ -270,7 +503,7 @@ public class FlightTaskServiceImpl implements IFlightTaskService { WaylineJobDTO newJob = childJobOpt.get(); newJob.setBeginTime(LocalDateTime.now().plusSeconds(RedisConst.WAYLINE_JOB_BLOCK_TIME)); - boolean isAdd = waylineRedisService.addPreparedWaylineJob(newJob); + boolean isAdd = waylineRedisService.addPrepareConditionalWaylineJob(newJob); if (!isAdd) { log.error("Failed to create wayline job. {}", newJob.getJobId()); return; @@ -279,4 +512,45 @@ public class FlightTaskServiceImpl implements IFlightTaskService { waylineJob.setJobId(newJob.getJobId()); waylineRedisService.setConditionalWaylineJob(waylineJob); } + + + @Override + public TopicEventsResponse flighttaskReady(TopicEventsRequest response, MessageHeaders headers) { + List flightIds = response.getData().getFlightIds(); + + log.info("ready task list:{}", Arrays.toString(flightIds.toArray()) ); + // Check conditional task blocking status. + String blockedId = waylineRedisService.getBlockedWaylineJobId(response.getGateway()); + if (!StringUtils.hasText(blockedId)) { + return null; + } + + Optional deviceOpt = deviceRedisService.getDeviceOnline(response.getGateway()); + if (deviceOpt.isEmpty()) { + return null; + } + DeviceDTO device = deviceOpt.get(); + + try { + for (String jobId : flightIds) { + boolean isExecute = this.executeFlightTask(device.getWorkspaceId(), jobId); + if (!isExecute) { + return null; + } + Optional waylineJobOpt = waylineRedisService.getConditionalWaylineJob(jobId); + if (waylineJobOpt.isEmpty()) { + log.info("The conditional job has expired and will no longer be executed."); + return new TopicEventsResponse<>(); + } + WaylineJobDTO waylineJob = waylineJobOpt.get(); + this.retryPrepareJob(new ConditionalWaylineJobKey(device.getWorkspaceId(), response.getGateway(), jobId), waylineJob); + return new TopicEventsResponse<>(); + } + } catch (Exception e) { + log.error("Failed to execute conditional task."); + e.printStackTrace(); + } + return new TopicEventsResponse<>(); + } + } diff --git a/src/main/java/com/dji/sample/wayline/service/impl/SDKWaylineService.java b/src/main/java/com/dji/sample/wayline/service/impl/SDKWaylineService.java new file mode 100644 index 0000000..4b82fc7 --- /dev/null +++ b/src/main/java/com/dji/sample/wayline/service/impl/SDKWaylineService.java @@ -0,0 +1,159 @@ +package com.dji.sample.wayline.service.impl; + +import com.dji.sample.common.error.CommonErrorEnum; +import com.dji.sample.component.mqtt.model.EventsReceiver; +import com.dji.sample.component.websocket.model.BizCodeEnum; +import com.dji.sample.component.websocket.service.IWebSocketMessageService; +import com.dji.sample.manage.model.dto.DeviceDTO; +import com.dji.sample.manage.model.enums.UserTypeEnum; +import com.dji.sample.manage.service.IDeviceRedisService; +import com.dji.sample.media.model.MediaFileCountDTO; +import com.dji.sample.media.service.IMediaRedisService; +import com.dji.sample.wayline.model.dto.WaylineJobDTO; +import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; +import com.dji.sample.wayline.service.IWaylineFileService; +import com.dji.sample.wayline.service.IWaylineJobService; +import com.dji.sample.wayline.service.IWaylineRedisService; +import com.dji.sdk.cloudapi.wayline.*; +import com.dji.sdk.cloudapi.wayline.api.AbstractWaylineService; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; + +import java.net.URL; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.Optional; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/7 + */ +@Service +@Slf4j +public class SDKWaylineService extends AbstractWaylineService { + + @Autowired + private IDeviceRedisService deviceRedisService; + + @Autowired + private IWaylineRedisService waylineRedisService; + + @Autowired + private IMediaRedisService mediaRedisService; + + @Autowired + private IWebSocketMessageService webSocketMessageService; + + @Autowired + private IWaylineJobService waylineJobService; + + @Autowired + private IWaylineFileService waylineFileService; + + @Override + public TopicEventsResponse deviceExitHomingNotify(TopicEventsRequest request, MessageHeaders headers) { + return super.deviceExitHomingNotify(request, headers); + } + + @Override + public TopicEventsResponse flighttaskProgress(TopicEventsRequest> response, MessageHeaders headers) { + EventsReceiver eventsReceiver = new EventsReceiver<>(); + eventsReceiver.setResult(response.getData().getResult()); + eventsReceiver.setOutput(response.getData().getOutput()); + eventsReceiver.setBid(response.getBid()); + eventsReceiver.setSn(response.getGateway()); + + FlighttaskProgress output = eventsReceiver.getOutput(); + log.info("Task progress: {}", output.getProgress().toString()); + if (!eventsReceiver.getResult().isSuccess()) { + log.error("Task progress ===> Error: " + eventsReceiver.getResult()); + } + + Optional deviceOpt = deviceRedisService.getDeviceOnline(response.getGateway()); + if (deviceOpt.isEmpty()) { + return new TopicEventsResponse<>(); + } + + FlighttaskStatusEnum statusEnum = output.getStatus(); + waylineRedisService.setRunningWaylineJob(response.getGateway(), eventsReceiver); + + if (statusEnum.isEnd()) { + WaylineJobDTO job = WaylineJobDTO.builder() + .jobId(response.getBid()) + .status(WaylineJobStatusEnum.SUCCESS.getVal()) + .completedTime(LocalDateTime.now()) + .mediaCount(output.getExt().getMediaCount()) + .build(); + + // record the update of the media count. + if (Objects.nonNull(job.getMediaCount()) && job.getMediaCount() != 0) { + mediaRedisService.setMediaCount(response.getGateway(), job.getJobId(), + MediaFileCountDTO.builder().deviceSn(deviceOpt.get().getChildDeviceSn()) + .jobId(response.getBid()).mediaCount(job.getMediaCount()).uploadedCount(0).build()); + } + + if (FlighttaskStatusEnum.OK != statusEnum) { + job.setCode(eventsReceiver.getResult().getCode()); + job.setStatus(WaylineJobStatusEnum.FAILED.getVal()); + } + waylineJobService.updateJob(job); + waylineRedisService.delRunningWaylineJob(response.getGateway()); + waylineRedisService.delPausedWaylineJob(response.getBid()); + } + + webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), + BizCodeEnum.FLIGHT_TASK_PROGRESS.getCode(), eventsReceiver); + + return new TopicEventsResponse<>(); + } + + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Override + public TopicRequestsResponse> flighttaskResourceGet(TopicRequestsRequest response, MessageHeaders headers) { + String jobId = response.getData().getFlightId(); + + Optional deviceOpt = deviceRedisService.getDeviceOnline(response.getGateway()); + if (deviceOpt.isEmpty()) { + log.error("The device is offline, please try again later."); + return new TopicRequestsResponse().setData(MqttReply.error(CommonErrorEnum.DEVICE_OFFLINE)); + } + Optional waylineJobOpt = waylineJobService.getJobByJobId(deviceOpt.get().getWorkspaceId(), jobId); + if (waylineJobOpt.isEmpty()) { + log.error("The wayline job does not exist."); + return new TopicRequestsResponse().setData(MqttReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); + } + + WaylineJobDTO waylineJob = waylineJobOpt.get(); + + // get wayline file + Optional waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId()); + if (waylineFile.isEmpty()) { + log.error("The wayline file does not exist."); + return new TopicRequestsResponse().setData(MqttReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); + } + // get file url + try { + URL url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getId()); + return new TopicRequestsResponse>().setData( + MqttReply.success(new FlighttaskResourceGetResponse() + .setFile(new FlighttaskFile() + .setUrl(url.toString()) + .setFingerprint(waylineFile.get().getSign())))); + } catch (SQLException | NullPointerException e) { + e.printStackTrace(); + return new TopicRequestsResponse().setData(MqttReply.error(CommonErrorEnum.SYSTEM_ERROR)); + } + } +} 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 bcea104..ae79c37 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 @@ -3,18 +3,20 @@ package com.dji.sample.wayline.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.dji.sample.common.model.Pagination; -import com.dji.sample.common.model.PaginationData; import com.dji.sample.component.oss.model.OssConfiguration; import com.dji.sample.component.oss.service.impl.OssServiceContext; -import com.dji.sample.manage.model.enums.DeviceDomainEnum; import com.dji.sample.wayline.dao.IWaylineFileMapper; import com.dji.sample.wayline.model.dto.KmzFileProperties; import com.dji.sample.wayline.model.dto.WaylineFileDTO; import com.dji.sample.wayline.model.entity.WaylineFileEntity; -import com.dji.sample.wayline.model.enums.WaylineTemplateTypeEnum; -import com.dji.sample.wayline.model.param.WaylineQueryParam; import com.dji.sample.wayline.service.IWaylineFileService; +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.dji.sdk.cloudapi.wayline.GetWaylineListRequest; +import com.dji.sdk.cloudapi.wayline.GetWaylineListResponse; +import com.dji.sdk.cloudapi.wayline.WaylineTypeEnum; +import com.dji.sdk.common.Pagination; +import com.dji.sdk.common.PaginationData; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Node; @@ -55,23 +57,23 @@ public class WaylineFileServiceImpl implements IWaylineFileService { private OssServiceContext ossService; @Override - public PaginationData getWaylinesByParam(String workspaceId, WaylineQueryParam param) { + public PaginationData getWaylinesByParam(String workspaceId, GetWaylineListRequest param) { // Paging Query Page page = mapper.selectPage( new Page(param.getPage(), param.getPageSize()), new LambdaQueryWrapper() .eq(WaylineFileEntity::getWorkspaceId, workspaceId) - .eq(param.isFavorited(), WaylineFileEntity::getFavorited, param.isFavorited()) + .eq(Objects.nonNull(param.getFavorited()), WaylineFileEntity::getFavorited, param.getFavorited()) .and(param.getTemplateType() != null, wrapper -> { - for (Integer type : param.getTemplateType()) { - wrapper.like(WaylineFileEntity::getTemplateTypes, type).or(); + for (WaylineTypeEnum type : param.getTemplateType()) { + wrapper.like(WaylineFileEntity::getTemplateTypes, type.getValue()).or(); } }) // There is a risk of SQL injection .last(StringUtils.hasText(param.getOrderBy()), " order by " + param.getOrderBy())); // Wrap the results of a paging query into a custom paging object. - List records = page.getRecords() + List records = page.getRecords() .stream() .map(this::entityConvertToDTO) .collect(Collectors.toList()); @@ -80,7 +82,7 @@ public class WaylineFileServiceImpl implements IWaylineFileService { } @Override - public Optional getWaylineByWaylineId(String workspaceId, String waylineId) { + public Optional getWaylineByWaylineId(String workspaceId, String waylineId) { return Optional.ofNullable( this.entityConvertToDTO( mapper.selectOne( @@ -91,7 +93,7 @@ public class WaylineFileServiceImpl implements IWaylineFileService { @Override public URL getObjectUrl(String workspaceId, String waylineId) throws SQLException { - Optional waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId); + Optional waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId); if (waylineOpt.isEmpty()) { throw new SQLException(waylineId + " does not exist."); } @@ -145,11 +147,11 @@ public class WaylineFileServiceImpl implements IWaylineFileService { @Override public Boolean deleteByWaylineId(String workspaceId, String waylineId) { - Optional waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId); + Optional waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId); if (waylineOpt.isEmpty()) { return true; } - WaylineFileDTO wayline = waylineOpt.get(); + GetWaylineListResponse wayline = waylineOpt.get(); boolean isDel = mapper.delete(new LambdaUpdateWrapper() .eq(WaylineFileEntity::getWorkspaceId, workspaceId) .eq(WaylineFileEntity::getWaylineId, waylineId)) @@ -169,7 +171,6 @@ public class WaylineFileServiceImpl implements IWaylineFileService { try { WaylineFileDTO waylineFile = waylineFileOpt.get(); - waylineFile.setWaylineId(workspaceId); waylineFile.setUsername(creator); ossService.putObject(OssConfiguration.bucket, waylineFile.getObjectKey(), file.getInputStream()); @@ -218,12 +219,12 @@ public class WaylineFileServiceImpl implements IWaylineFileService { } return Optional.of(WaylineFileDTO.builder() - .droneModelKey(String.format("%s-%s-%s", DeviceDomainEnum.SUB_DEVICE.getVal(), type, subType)) - .payloadModelKeys(List.of(String.format("%s-%s-%s",DeviceDomainEnum.PAYLOAD.getVal(), payloadType, payloadSubType))) + .droneModelKey(String.format("%s-%s-%s", DeviceDomainEnum.DRONE.getDomain(), type, subType)) + .payloadModelKeys(List.of(String.format("%s-%s-%s",DeviceDomainEnum.PAYLOAD.getDomain(), payloadType, payloadSubType))) .objectKey(OssConfiguration.objectDirPrefix + File.separator + filename) .name(filename.substring(0, filename.lastIndexOf(WAYLINE_FILE_SUFFIX))) .sign(DigestUtils.md5DigestAsHex(file.getInputStream())) - .templateTypes(List.of(WaylineTemplateTypeEnum.find(templateType).map(WaylineTemplateTypeEnum::getVal).orElse(-1))) + .templateTypes(List.of(WaylineTypeEnum.find(templateType).getValue())) .build()); } @@ -237,25 +238,24 @@ public class WaylineFileServiceImpl implements IWaylineFileService { * @param entity * @return */ - private WaylineFileDTO entityConvertToDTO(WaylineFileEntity entity) { + private GetWaylineListResponse entityConvertToDTO(WaylineFileEntity entity) { 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) + return new GetWaylineListResponse() + .setDroneModelKey(DeviceEnum.find(entity.getDroneModelKey())) + .setFavorited(entity.getFavorited()) + .setName(entity.getName()) + .setPayloadModelKeys(entity.getPayloadModelKeys() != null ? + Arrays.stream(entity.getPayloadModelKeys().split(",")).map(DeviceEnum::find).collect(Collectors.toList()) : null) + .setTemplateTypes(Arrays.stream(entity.getTemplateTypes().split(",")) + .map(Integer::parseInt).map(WaylineTypeEnum::find) .collect(Collectors.toList())) - .username(entity.getUsername()) - .objectKey(entity.getObjectKey()) - .sign(entity.getSign()) - .updateTime(entity.getUpdateTime()) - .waylineId(entity.getWaylineId()) - .build(); + .setUsername(entity.getUsername()) + .setObjectKey(entity.getObjectKey()) + .setSign(entity.getSign()) + .setUpdateTime(entity.getUpdateTime()) + .setId(entity.getWaylineId()); } diff --git a/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java b/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java index 9d07192..923fd93 100644 --- a/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java +++ b/src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java @@ -3,52 +3,40 @@ 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.error.CommonErrorEnum; -import com.dji.sample.common.model.CustomClaim; -import com.dji.sample.common.model.Pagination; -import com.dji.sample.common.model.PaginationData; -import com.dji.sample.common.model.ResponseResult; -import com.dji.sample.component.mqtt.model.*; -import com.dji.sample.component.mqtt.service.IMessageSenderService; +import com.dji.sample.component.mqtt.model.EventsReceiver; import com.dji.sample.component.redis.RedisConst; import com.dji.sample.component.redis.RedisOpsUtils; -import com.dji.sample.control.model.param.DrcModeParam; -import com.dji.sample.control.service.IDrcService; import com.dji.sample.manage.model.dto.DeviceDTO; -import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; -import com.dji.sample.manage.model.enums.DockModeCodeEnum; -import com.dji.sample.manage.model.receiver.OsdDockReceiver; -import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; import com.dji.sample.manage.service.IDeviceRedisService; import com.dji.sample.manage.service.IDeviceService; import com.dji.sample.media.model.MediaFileCountDTO; -import com.dji.sample.media.model.MediaMethodEnum; import com.dji.sample.media.service.IFileService; import com.dji.sample.wayline.dao.IWaylineJobMapper; -import com.dji.sample.wayline.model.dto.*; +import com.dji.sample.wayline.model.dto.WaylineJobDTO; import com.dji.sample.wayline.model.entity.WaylineJobEntity; -import com.dji.sample.wayline.model.enums.*; +import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; import com.dji.sample.wayline.model.param.CreateJobParam; -import com.dji.sample.wayline.model.param.UpdateJobParam; import com.dji.sample.wayline.service.IWaylineFileService; import com.dji.sample.wayline.service.IWaylineJobService; import com.dji.sample.wayline.service.IWaylineRedisService; -import com.fasterxml.jackson.core.type.TypeReference; +import com.dji.sdk.cloudapi.device.DockModeCodeEnum; +import com.dji.sdk.cloudapi.device.DroneModeCodeEnum; +import com.dji.sdk.cloudapi.device.OsdDock; +import com.dji.sdk.cloudapi.device.OsdDockDrone; +import com.dji.sdk.cloudapi.wayline.*; +import com.dji.sdk.common.Pagination; +import com.dji.sdk.common.PaginationData; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.integration.mqtt.support.MqttHeaders; -import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import java.net.URL; -import java.sql.SQLException; -import java.time.*; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; @@ -71,18 +59,12 @@ public class WaylineJobServiceImpl implements IWaylineJobService { @Autowired private IDeviceService deviceService; - @Autowired - private IMessageSenderService messageSender; - @Autowired private ObjectMapper objectMapper; @Autowired private IFileService fileService; - @Autowired - private IDrcService drcService; - @Autowired private IDeviceRedisService deviceRedisService; @@ -113,9 +95,9 @@ public class WaylineJobServiceImpl implements IWaylineJobService { .beginTime(beginTime) .endTime(endTime) .status(WaylineJobStatusEnum.PENDING.getVal()) - .taskType(param.getTaskType().getVal()) - .waylineType(param.getWaylineType().getVal()) - .outOfControlAction(param.getOutOfControlAction()) + .taskType(param.getTaskType().getType()) + .waylineType(param.getWaylineType().getValue()) + .outOfControlAction(param.getOutOfControlAction().getAction()) .rthAltitude(param.getRthAltitude()) .mediaCount(0) .build(); @@ -140,238 +122,13 @@ public class WaylineJobServiceImpl implements IWaylineJobService { return this.insertWaylineJob(jobEntity); } - /** - * For immediate tasks, the server time shall prevail. - * @param param - */ - private void fillImmediateTime(CreateJobParam param) { - if (WaylineTaskTypeEnum.IMMEDIATE != param.getTaskType()) { - return; - } - long now = System.currentTimeMillis() / 1000; - param.setTaskDays(Collections.singletonList(now)); - param.setTaskPeriods(Collections.singletonList(Collections.singletonList(now))); - } - - @Override - public ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException { - fillImmediateTime(param); - - param.getTaskDays().sort((a, b) -> (int) (a - b)); - param.getTaskPeriods().sort((a, b) -> (int) (a.get(0) - b.get(0))); - for (Long taskDay : param.getTaskDays()) { - LocalDate date = LocalDate.ofInstant(Instant.ofEpochSecond(taskDay), ZoneId.systemDefault()); - for (List taskPeriod : param.getTaskPeriods()) { - long beginTime = LocalDateTime.of(date, LocalTime.ofInstant(Instant.ofEpochSecond(taskPeriod.get(0)), ZoneId.systemDefault())) - .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); - long endTime = taskPeriod.size() > 1 && Objects.nonNull(taskPeriod.get(1)) ? - LocalDateTime.of(date, LocalTime.ofInstant(Instant.ofEpochSecond(taskPeriod.get(1)), ZoneId.systemDefault())) - .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : beginTime; - if (WaylineTaskTypeEnum.IMMEDIATE != param.getTaskType() && endTime < System.currentTimeMillis()) { - return ResponseResult.error("The task has expired."); - } - Optional waylineJobOpt = this.createWaylineJob(param, customClaim.getWorkspaceId(), customClaim.getUsername(), beginTime, endTime); - if (waylineJobOpt.isEmpty()) { - return ResponseResult.error("Failed to create wayline job."); - } - - WaylineJobDTO waylineJob = waylineJobOpt.get(); - if (WaylineTaskTypeEnum.IMMEDIATE == param.getTaskType()) { - return this.publishOneFlightTask(waylineJob); - } - - // If it is a conditional task type, add conditions to the job parameters. - addPreparedJob(waylineJob, param, beginTime, endTime); - } - } - return ResponseResult.success(); - } - - private void addPreparedJob(WaylineJobDTO waylineJob, CreateJobParam param, Long beginTime, Long endTime) { - if (WaylineTaskTypeEnum.CONDITION == param.getTaskType()) { - waylineJob.setConditions( - WaylineTaskConditionDTO.builder() - .executableConditions(Objects.nonNull(param.getMinStorageCapacity()) ? - WaylineTaskExecutableConditionDTO.builder().storageCapacity(param.getMinStorageCapacity()).build() : null) - .readyConditions(WaylineTaskReadyConditionDTO.builder() - .batteryCapacity(param.getMinBatteryCapacity()) - .beginTime(beginTime) - .endTime(endTime) - .build()) - .build()); - - waylineRedisService.setConditionalWaylineJob(waylineJob); - } - // value: {workspace_id}:{dock_sn}:{job_id} - boolean isAdd = waylineRedisService.addPreparedWaylineJob(waylineJob); - if (!isAdd) { - throw new RuntimeException("Failed to create prepare job."); - } - } - - public ResponseResult publishOneFlightTask(WaylineJobDTO waylineJob) throws SQLException { - - boolean isSuccess = this.prepareFlightTask(waylineJob); - if (!isSuccess) { - return ResponseResult.error("Failed to prepare job."); - } - - // Issue an immediate task execution command. - if (WaylineTaskTypeEnum.IMMEDIATE == waylineJob.getTaskType()) { - boolean isExecuted = executeFlightTask(waylineJob.getWorkspaceId(), waylineJob.getJobId()); - if (!isExecuted) { - return ResponseResult.error("Failed to execute job."); - } - } - - return ResponseResult.success(); - } - - private Boolean prepareFlightTask(WaylineJobDTO waylineJob) throws SQLException { - - boolean isOnline = deviceRedisService.checkDeviceOnline(waylineJob.getDockSn()); - if (!isOnline) { - throw new RuntimeException("Dock is offline."); - } - - // get wayline file - Optional waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId()); - if (waylineFile.isEmpty()) { - throw new SQLException("Wayline file doesn't exist."); - } - - // get file url - URL url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getWaylineId()); - - WaylineTaskCreateDTO flightTask = WaylineTaskCreateDTO.builder() - .flightId(waylineJob.getJobId()) - .executeTime(waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) - .taskType(waylineJob.getTaskType()) - .waylineType(waylineJob.getWaylineType()) - .rthAltitude(waylineJob.getRthAltitude()) - .outOfControlAction(waylineJob.getOutOfControlAction()) - .file(WaylineTaskFileDTO.builder() - .url(url.toString()) - .fingerprint(waylineFile.get().getSign()) - .build()) - .build(); - - if (WaylineTaskTypeEnum.CONDITION == waylineJob.getTaskType()) { - if (Objects.isNull(waylineJob.getConditions())) { - throw new IllegalArgumentException(); - } - flightTask.setReadyConditions(waylineJob.getConditions().getReadyConditions()); - flightTask.setExecutableConditions(waylineJob.getConditions().getExecutableConditions()); - } - - ServiceReply serviceReply = messageSender.publishServicesTopic( - waylineJob.getDockSn(), WaylineMethodEnum.FLIGHT_TASK_PREPARE.getMethod(), flightTask, waylineJob.getJobId()); - if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { - log.info("Prepare task ====> Error code: {}", serviceReply.getResult()); - this.updateJob(WaylineJobDTO.builder() - .workspaceId(waylineJob.getWorkspaceId()) - .jobId(waylineJob.getJobId()) - .executeTime(LocalDateTime.now()) - .status(WaylineJobStatusEnum.FAILED.getVal()) - .completedTime(LocalDateTime.now()) - .code(serviceReply.getResult()).build()); - return false; - } - return true; - } - - @Override - public Boolean executeFlightTask(String workspaceId, String jobId) { - // get job - Optional waylineJob = this.getJobByJobId(workspaceId, jobId); - if (waylineJob.isEmpty()) { - throw new IllegalArgumentException("Job doesn't exist."); - } - - boolean isOnline = deviceRedisService.checkDeviceOnline(waylineJob.get().getDockSn()); - if (!isOnline) { - throw new RuntimeException("Dock is offline."); - } - - WaylineJobDTO job = waylineJob.get(); - WaylineTaskCreateDTO flightTask = WaylineTaskCreateDTO.builder().flightId(jobId).build(); - - ServiceReply serviceReply = messageSender.publishServicesTopic( - job.getDockSn(), WaylineMethodEnum.FLIGHT_TASK_EXECUTE.getMethod(), flightTask, jobId); - if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { - log.info("Execute job ====> Error code: {}", serviceReply.getResult()); - this.updateJob(WaylineJobDTO.builder() - .jobId(jobId) - .executeTime(LocalDateTime.now()) - .status(WaylineJobStatusEnum.FAILED.getVal()) - .completedTime(LocalDateTime.now()) - .code(serviceReply.getResult()).build()); - // The conditional task fails and enters the blocking status. - if (WaylineTaskTypeEnum.CONDITION == job.getTaskType() - && WaylineErrorCodeEnum.find(serviceReply.getResult()).isBlock()) { - waylineRedisService.setBlockedWaylineJob(job.getDockSn(), jobId); - } - return false; - } - - this.updateJob(WaylineJobDTO.builder() - .jobId(jobId) - .executeTime(LocalDateTime.now()) - .status(WaylineJobStatusEnum.IN_PROGRESS.getVal()) - .build()); - waylineRedisService.setRunningWaylineJob(job.getDockSn(), EventsReceiver.builder().bid(jobId).sn(job.getDockSn()).build()); - return true; - } - - @Override - public void cancelFlightTask(String workspaceId, Collection jobIds) { - List waylineJobs = getJobsByConditions(workspaceId, jobIds, WaylineJobStatusEnum.PENDING); - - Set waylineJobIds = waylineJobs.stream().map(WaylineJobDTO::getJobId).collect(Collectors.toSet()); - // Check if the task status is correct. - boolean isErr = !jobIds.removeAll(waylineJobIds) || !jobIds.isEmpty() ; - if (isErr) { - throw new IllegalArgumentException("These tasks have an incorrect status and cannot be canceled. " + Arrays.toString(jobIds.toArray())); - } - - // Group job id by dock sn. - Map> dockJobs = waylineJobs.stream() - .collect(Collectors.groupingBy(WaylineJobDTO::getDockSn, - Collectors.mapping(WaylineJobDTO::getJobId, Collectors.toList()))); - dockJobs.forEach((dockSn, idList) -> this.publishCancelTask(workspaceId, dockSn, idList)); - - } - - public void publishCancelTask(String workspaceId, String dockSn, List jobIds) { - boolean isOnline = deviceRedisService.checkDeviceOnline(dockSn); - if (!isOnline) { - throw new RuntimeException("Dock is offline."); - } - - ServiceReply serviceReply = messageSender.publishServicesTopic( - dockSn, WaylineMethodEnum.FLIGHT_TASK_CANCEL.getMethod(), Map.of(MapKeyConst.FLIGHT_IDS, jobIds)); - if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { - log.info("Cancel job ====> Error code: {}", serviceReply.getResult()); - throw new RuntimeException("Failed to cancel the wayline job of " + dockSn); - } - - for (String jobId : jobIds) { - this.updateJob(WaylineJobDTO.builder() - .workspaceId(workspaceId) - .jobId(jobId) - .status(WaylineJobStatusEnum.CANCEL.getVal()) - .completedTime(LocalDateTime.now()) - .build()); - } - - } - public List getJobsByConditions(String workspaceId, Collection jobIds, WaylineJobStatusEnum status) { return mapper.selectList( new LambdaQueryWrapper() .eq(WaylineJobEntity::getWorkspaceId, workspaceId) .eq(Objects.nonNull(status), WaylineJobEntity::getStatus, status.getVal()) - .in(!CollectionUtils.isEmpty(jobIds), WaylineJobEntity::getJobId, jobIds)) + .and(!CollectionUtils.isEmpty(jobIds), + wrapper -> jobIds.forEach(id -> wrapper.eq(WaylineJobEntity::getJobId, id).or()))) .stream() .map(this::entity2Dto) .collect(Collectors.toList()); @@ -408,85 +165,6 @@ public class WaylineJobServiceImpl implements IWaylineJobService { return new PaginationData(records, new Pagination(pageData)); } - - @Override - @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET, outputChannel = ChannelName.OUTBOUND) - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - public void flightTaskResourceGet(CommonTopicReceiver receiver, MessageHeaders headers) { - Map jobIdMap = objectMapper.convertValue(receiver.getData(), new TypeReference>() {}); - String jobId = jobIdMap.get(MapKeyConst.FLIGHT_ID); - - CommonTopicResponse.CommonTopicResponseBuilder builder = CommonTopicResponse.builder() - .tid(receiver.getTid()) - .bid(receiver.getBid()) - .method(RequestsMethodEnum.FLIGHT_TASK_RESOURCE_GET.getMethod()) - .timestamp(System.currentTimeMillis()); - - String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString() + TopicConst._REPLY_SUF; - - Optional deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway()); - if (deviceOpt.isEmpty()) { - return; - } - Optional waylineJobOpt = this.getJobByJobId(deviceOpt.get().getWorkspaceId(), jobId); - if (waylineJobOpt.isEmpty()) { - builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); - messageSender.publish(topic, builder.build()); - return; - } - - WaylineJobDTO waylineJob = waylineJobOpt.get(); - - // get wayline file - Optional waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId()); - if (waylineFile.isEmpty()) { - builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); - messageSender.publish(topic, builder.build()); - return; - } - - // get file url - URL url = null; - try { - url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getWaylineId()); - builder.data(RequestsReply.success(WaylineTaskCreateDTO.builder() - .file(WaylineTaskFileDTO.builder() - .url(url.toString()) - .fingerprint(waylineFile.get().getSign()) - .build()) - .build())); - - } catch (SQLException | NullPointerException e) { - e.printStackTrace(); - builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); - messageSender.publish(topic, builder.build()); - return; - } - - messageSender.publish(topic, builder.build()); - - } - - @Override - public void uploadMediaHighestPriority(String workspaceId, String jobId) { - Optional jobOpt = getJobByJobId(workspaceId, jobId); - if (jobOpt.isEmpty()) { - throw new RuntimeException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg()); - } - - String dockSn = jobOpt.get().getDockSn(); - String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + dockSn; - if (RedisOpsUtils.checkExist(key) && jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(key)).getJobId())) { - return; - } - - ServiceReply reply = messageSender.publishServicesTopic( - dockSn, MediaMethodEnum.UPLOAD_FLIGHT_TASK_MEDIA_PRIORITIZE.getMethod(), Map.of(MapKeyConst.FLIGHT_ID, jobId)); - if (ResponseResult.CODE_SUCCESS != reply.getResult()) { - throw new RuntimeException("Failed to set media job upload priority. Error Code: " + reply.getResult()); - } - } - private WaylineJobEntity dto2Entity(WaylineJobDTO dto) { WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder(); if (dto == null) { @@ -512,53 +190,31 @@ public class WaylineJobServiceImpl implements IWaylineJobService { .fileId(dto.getFileId()) .dockSn(dto.getDockSn()) .workspaceId(dto.getWorkspaceId()) - .taskType(Optional.ofNullable(dto.getTaskType()).map(WaylineTaskTypeEnum::getVal).orElse(null)) - .waylineType(Optional.ofNullable(dto.getWaylineType()).map(WaylineTemplateTypeEnum::getVal).orElse(null)) + .taskType(Optional.ofNullable(dto.getTaskType()).map(TaskTypeEnum::getType).orElse(null)) + .waylineType(Optional.ofNullable(dto.getWaylineType()).map(WaylineTypeEnum::getValue).orElse(null)) .username(dto.getUsername()) .rthAltitude(dto.getRthAltitude()) - .outOfControlAction(dto.getOutOfControlAction()) + .outOfControlAction(Optional.ofNullable(dto.getOutOfControlAction()) + .map(OutOfControlActionEnum::getAction).orElse(null)) .parentId(dto.getParentId()) .build(); } - @Override - public void updateJobStatus(String workspaceId, String jobId, UpdateJobParam param) { - Optional waylineJobOpt = this.getJobByJobId(workspaceId, jobId); - if (waylineJobOpt.isEmpty()) { - throw new RuntimeException("The job does not exist."); - } - WaylineJobDTO waylineJob = waylineJobOpt.get(); - WaylineJobStatusEnum statusEnum = this.getWaylineState(waylineJob.getDockSn()); - if (statusEnum.getEnd() || WaylineJobStatusEnum.PENDING == statusEnum) { - throw new RuntimeException("The wayline job status does not match, and the operation cannot be performed."); - } - - switch (param.getStatus()) { - case PAUSE: - pauseJob(workspaceId, waylineJob.getDockSn(), jobId, statusEnum); - break; - case RESUME: - resumeJob(workspaceId, waylineJob.getDockSn(), jobId, statusEnum); - break; - } - - } - public WaylineJobStatusEnum getWaylineState(String dockSn) { Optional dockOpt = deviceRedisService.getDeviceOnline(dockSn); if (dockOpt.isEmpty() || !StringUtils.hasText(dockOpt.get().getChildDeviceSn())) { return WaylineJobStatusEnum.UNKNOWN; } - Optional dockOsdOpt = deviceRedisService.getDeviceOsd(dockSn, OsdDockReceiver.class); - Optional deviceOsdOpt = deviceRedisService.getDeviceOsd(dockOpt.get().getChildDeviceSn(), OsdSubDeviceReceiver.class); + Optional dockOsdOpt = deviceRedisService.getDeviceOsd(dockSn, OsdDock.class); + Optional deviceOsdOpt = deviceRedisService.getDeviceOsd(dockOpt.get().getChildDeviceSn(), OsdDockDrone.class); if (dockOsdOpt.isEmpty() || deviceOsdOpt.isEmpty() || DockModeCodeEnum.WORKING != dockOsdOpt.get().getModeCode()) { return WaylineJobStatusEnum.UNKNOWN; } - OsdSubDeviceReceiver osdDevice = deviceOsdOpt.get(); - if (DeviceModeCodeEnum.WAYLINE == osdDevice.getModeCode() - || DeviceModeCodeEnum.MANUAL == osdDevice.getModeCode() - || DeviceModeCodeEnum.TAKEOFF_AUTO == osdDevice.getModeCode()) { + OsdDockDrone osdDevice = deviceOsdOpt.get(); + if (DroneModeCodeEnum.WAYLINE == osdDevice.getModeCode() + || DroneModeCodeEnum.MANUAL == osdDevice.getModeCode() + || DroneModeCodeEnum.TAKEOFF_AUTO == osdDevice.getModeCode()) { if (StringUtils.hasText(waylineRedisService.getPausedWaylineJobId(dockSn))) { return WaylineJobStatusEnum.PAUSED; } @@ -569,43 +225,6 @@ public class WaylineJobServiceImpl implements IWaylineJobService { return WaylineJobStatusEnum.UNKNOWN; } - private void pauseJob(String workspaceId, String dockSn, String jobId, WaylineJobStatusEnum statusEnum) { - if (WaylineJobStatusEnum.PAUSED == statusEnum && jobId.equals(waylineRedisService.getPausedWaylineJobId(dockSn))) { - waylineRedisService.setPausedWaylineJob(dockSn, jobId); - return; - } - - ServiceReply reply = messageSender.publishServicesTopic( - dockSn, WaylineMethodEnum.FLIGHT_TASK_PAUSE.getMethod(), "", jobId); - if (ResponseResult.CODE_SUCCESS != reply.getResult()) { - throw new RuntimeException("Failed to pause wayline job. Error Code: " + reply.getResult()); - } - waylineRedisService.delRunningWaylineJob(dockSn); - waylineRedisService.setPausedWaylineJob(dockSn, jobId); - } - - private void resumeJob(String workspaceId, String dockSn, String jobId, WaylineJobStatusEnum statusEnum) { - Optional> runningDataOpt = waylineRedisService.getRunningWaylineJob(dockSn); - if (WaylineJobStatusEnum.IN_PROGRESS == statusEnum && jobId.equals(runningDataOpt.map(EventsReceiver::getSn).get())) { - waylineRedisService.setRunningWaylineJob(dockSn, runningDataOpt.get()); - return; - } - ServiceReply reply = messageSender.publishServicesTopic( - dockSn, WaylineMethodEnum.FLIGHT_TASK_RESUME.getMethod(), "", jobId); - if (ResponseResult.CODE_SUCCESS != reply.getResult()) { - throw new RuntimeException("Failed to resume wayline job. Error Code: " + reply.getResult()); - } - - runningDataOpt.ifPresent(runningData -> waylineRedisService.setRunningWaylineJob(dockSn, runningData)); - waylineRedisService.delPausedWaylineJob(dockSn); - - if (deviceService.checkDockDrcMode(dockSn)) { - drcService.deviceDrcExit(workspaceId, DrcModeParam.builder().dockSn(dockSn) - .clientId(drcService.getDrcModeInRedis(dockSn)).build()); - } - - } - private WaylineJobDTO entity2Dto(WaylineJobEntity entity) { if (entity == null) { return null; @@ -616,7 +235,7 @@ public class WaylineJobServiceImpl implements IWaylineJobService { .jobName(entity.getName()) .fileId(entity.getFileId()) .fileName(waylineFileService.getWaylineByWaylineId(entity.getWorkspaceId(), entity.getFileId()) - .orElse(WaylineFileDTO.builder().build()).getName()) + .orElse(new GetWaylineListResponse()).getName()) .dockSn(entity.getDockSn()) .dockName(deviceService.getDeviceBySn(entity.getDockSn()) .orElse(DeviceDTO.builder().build()).getNickname()) @@ -633,10 +252,10 @@ public class WaylineJobServiceImpl implements IWaylineJobService { LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getExecuteTime()), ZoneId.systemDefault()) : null) .completedTime(WaylineJobStatusEnum.find(entity.getStatus()).getEnd() ? LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getUpdateTime()), ZoneId.systemDefault()) : null) - .taskType(WaylineTaskTypeEnum.find(entity.getTaskType())) - .waylineType(WaylineTemplateTypeEnum.find(entity.getWaylineType())) + .taskType(TaskTypeEnum.find(entity.getTaskType())) + .waylineType(WaylineTypeEnum.find(entity.getWaylineType())) .rthAltitude(entity.getRthAltitude()) - .outOfControlAction(entity.getOutOfControlAction()) + .outOfControlAction(OutOfControlActionEnum.find(entity.getOutOfControlAction())) .mediaCount(entity.getMediaCount()); if (Objects.nonNull(entity.getEndTime())) { @@ -645,8 +264,8 @@ public class WaylineJobServiceImpl implements IWaylineJobService { if (WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus()) { builder.progress(waylineRedisService.getRunningWaylineJob(entity.getDockSn()) .map(EventsReceiver::getOutput) - .map(WaylineTaskProgressReceiver::getProgress) - .map(WaylineTaskProgress::getPercent) + .map(FlighttaskProgress::getProgress) + .map(FlighttaskProgressData::getPercent) .orElse(null)); } diff --git a/src/main/java/com/dji/sample/wayline/service/impl/WaylineRedisServiceImpl.java b/src/main/java/com/dji/sample/wayline/service/impl/WaylineRedisServiceImpl.java index 3307533..72aadd6 100644 --- a/src/main/java/com/dji/sample/wayline/service/impl/WaylineRedisServiceImpl.java +++ b/src/main/java/com/dji/sample/wayline/service/impl/WaylineRedisServiceImpl.java @@ -3,10 +3,10 @@ package com.dji.sample.wayline.service.impl; import com.dji.sample.component.mqtt.model.EventsReceiver; import com.dji.sample.component.redis.RedisConst; import com.dji.sample.component.redis.RedisOpsUtils; +import com.dji.sample.wayline.model.dto.ConditionalWaylineJobKey; import com.dji.sample.wayline.model.dto.WaylineJobDTO; -import com.dji.sample.wayline.model.dto.WaylineJobKey; -import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; import com.dji.sample.wayline.service.IWaylineRedisService; +import com.dji.sdk.cloudapi.wayline.FlighttaskProgress; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -25,13 +25,13 @@ import java.util.Optional; public class WaylineRedisServiceImpl implements IWaylineRedisService { @Override - public void setRunningWaylineJob(String dockSn, EventsReceiver data) { + public void setRunningWaylineJob(String dockSn, EventsReceiver data) { RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + dockSn, data, RedisConst.DRC_MODE_ALIVE_SECOND); } @Override - public Optional> getRunningWaylineJob(String dockSn) { - return Optional.ofNullable((EventsReceiver) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + dockSn)); + public Optional> getRunningWaylineJob(String dockSn) { + return Optional.ofNullable((EventsReceiver) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + dockSn)); } @Override @@ -64,18 +64,13 @@ public class WaylineRedisServiceImpl implements IWaylineRedisService { return (String) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_BLOCK_PREFIX + dockSn); } - @Override - public Boolean delBlockedWaylineJobId(String dockSn) { - return RedisOpsUtils.del(RedisConst.WAYLINE_JOB_BLOCK_PREFIX + dockSn); - } - @Override public void setConditionalWaylineJob(WaylineJobDTO waylineJob) { if (!StringUtils.hasText(waylineJob.getJobId())) { throw new RuntimeException("Job id can't be null."); } RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_CONDITION_PREFIX + waylineJob.getJobId(), waylineJob, - Math.abs(Duration.between(waylineJob.getEndTime(), LocalDateTime.now()).getSeconds())); + (Duration.between(waylineJob.getEndTime(), LocalDateTime.now()).getSeconds())); } @Override @@ -89,29 +84,29 @@ public class WaylineRedisServiceImpl implements IWaylineRedisService { } @Override - public Boolean addPreparedWaylineJob(WaylineJobDTO waylineJob) { + public Boolean addPrepareConditionalWaylineJob(WaylineJobDTO waylineJob) { if (Objects.isNull(waylineJob.getBeginTime())) { return false; } // value: {workspace_id}:{dock_sn}:{job_id} - return RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_PREPARED, + return RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_CONDITION_PREPARE, waylineJob.getWorkspaceId() + RedisConst.DELIMITER + waylineJob.getDockSn() + RedisConst.DELIMITER + waylineJob.getJobId(), waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } @Override - public Optional getNearestPreparedWaylineJob() { - return Optional.ofNullable(RedisOpsUtils.zGetMin(RedisConst.WAYLINE_JOB_PREPARED)) - .map(Object::toString).map(WaylineJobKey::new); + public Optional getNearestConditionalWaylineJob() { + return Optional.ofNullable(RedisOpsUtils.zGetMin(RedisConst.WAYLINE_JOB_CONDITION_PREPARE)) + .map(Object::toString).map(ConditionalWaylineJobKey::new); } @Override - public Double getPreparedWaylineJobTime(WaylineJobKey jobKey) { - return RedisOpsUtils.zScore(RedisConst.WAYLINE_JOB_PREPARED, jobKey.getKey()); + public Double getConditionalWaylineJobTime(ConditionalWaylineJobKey jobKey) { + return RedisOpsUtils.zScore(RedisConst.WAYLINE_JOB_CONDITION_PREPARE, jobKey.getKey()); } @Override - public Boolean removePreparedWaylineJob(WaylineJobKey jobKey) { - return RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_PREPARED, jobKey.getKey()); + public Boolean removePrepareConditionalWaylineJob(ConditionalWaylineJobKey jobKey) { + return RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_CONDITION_PREPARE, jobKey.getKey()); } } diff --git a/src/main/java/com/dji/sdk/README.md b/src/main/java/com/dji/sdk/README.md new file mode 100644 index 0000000..f3f5ff5 --- /dev/null +++ b/src/main/java/com/dji/sdk/README.md @@ -0,0 +1,48 @@ +# 如何接入CloudSDK +### 1. 在组件扫描中增加包名:com.dji.sdk +### [2. 连接MQTT](#如何连接MQTT) +### [3. 实现SDK 的方法](#如何实现SDK的方法) +### [4. 调用SDK 的方法](#如何调用SDK的方法) + + +## 如何连接MQTT +- 在spring容器中注入MqttConnectOptions和MqttPahoClientFactory; + ![1](../../../../resources/image/6.png) + +- 在application.yml中配置cloud-sdk.mqtt.inbound-topic,未配置则不进行初始化订阅。 + + +## 如何实现SDK的方法 + - 定义一个类,继承com.dji.sdk.cloudapi.*.api包中的抽象类; + - 重写具体的方法以实现功能; + - 将定义的类放入spring容器中,由spring管理bean的生命周期。 +### 【设备上线】示例: + - 定义一个类:SDKDeviceService 继承AbstractDeviceService; +![1](../../../../resources/image/1.png) + - 重写方法updateTopoOnline,实现设备上线功能。 +![1](../../../../resources/image/2.png) + +## 如何调用SDK的方法 + - 定义一个类,继承com.dji.sdk.cloudapi.*.api包中的抽象类; + - 在需要调用的类中注入该类; + - 调用具体的方法。 +### 【航线预下发命令】示例: + - 定义一个类:SDKWaylineService 继承 AbstractWaylineService; +![1](../../../../resources/image/3.png) + - 在WaylineJobServiceImpl中注入该类; +![1](../../../../resources/image/4.png) + - 调用下发命令的方法: +![1](../../../../resources/image/5.png) + +## 如何实现CloudAPI 定义的http 接口 + - 定义一个类,实现com.dji.sdk.cloudapi.*.api包中的http接口类; + - 重写具体的方法以实现接口,无需定义请求地址和方法等数据。 + ![1](../../../../resources/image/7.png) + +## 如何查看CloudAPI 定义的所有http 接口 + - 启动程序 + - 浏览器打开:http://localhost:6789/swagger-ui/index.html + +## 如何接入WebSocket + - CloudSDK 已经定义了WebSocket服务,但是没有实现WebSocket管理。默认地址为:http://localhost:6789/api/v1/ws + - 自定义接入参考:com.dji.sample.component.websocket.config \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/annotations/CloudSDKVersion.java b/src/main/java/com/dji/sdk/annotations/CloudSDKVersion.java new file mode 100644 index 0000000..e7476d1 --- /dev/null +++ b/src/main/java/com/dji/sdk/annotations/CloudSDKVersion.java @@ -0,0 +1,26 @@ +package com.dji.sdk.annotations; + +import com.dji.sdk.common.CloudSDKVersionEnum; +import com.dji.sdk.common.GatewayTypeEnum; + +import java.lang.annotation.*; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/22 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface CloudSDKVersion { + + CloudSDKVersionEnum since() default CloudSDKVersionEnum.V0_0_1; + + CloudSDKVersionEnum deprecated() default CloudSDKVersionEnum.V99; + + GatewayTypeEnum[] include() default {}; + + GatewayTypeEnum[] exclude() default {}; + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/config/ConfigScopeEnum.java b/src/main/java/com/dji/sdk/cloudapi/config/ConfigScopeEnum.java new file mode 100644 index 0000000..bc7004f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/config/ConfigScopeEnum.java @@ -0,0 +1,34 @@ +package com.dji.sdk.cloudapi.config; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/10 + */ +public enum ConfigScopeEnum { + + PRODUCT("product"); + + private final String scope; + + ConfigScopeEnum(String scope) { + this.scope = scope; + } + + @JsonValue + public String getScope() { + return scope; + } + + @JsonCreator + public static ConfigScopeEnum find(String scope) { + return Arrays.stream(values()).filter(scopeEnum -> scopeEnum.scope.equals(scope)).findAny() + .orElseThrow(() -> new CloudSDKException(ConfigScopeEnum.class, scope)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/config/ConfigTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/config/ConfigTypeEnum.java new file mode 100644 index 0000000..f8bf6d0 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/config/ConfigTypeEnum.java @@ -0,0 +1,34 @@ +package com.dji.sdk.cloudapi.config; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/10 + */ +public enum ConfigTypeEnum { + + JSON("json"); + + private final String type; + + ConfigTypeEnum(String type) { + this.type = type; + } + + @JsonValue + public String getType() { + return type; + } + + @JsonCreator + public static ConfigTypeEnum find(String type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type.equals(type)).findAny() + .orElseThrow(() -> new CloudSDKException(ConfigTypeEnum.class, type)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/config/ProductConfigResponse.java b/src/main/java/com/dji/sdk/cloudapi/config/ProductConfigResponse.java new file mode 100644 index 0000000..f3a9bd2 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/config/ProductConfigResponse.java @@ -0,0 +1,73 @@ +package com.dji.sdk.cloudapi.config; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/10 + */ +public class ProductConfigResponse extends BaseModel { + + private String ntpServerHost; + + @NotNull + private String appId; + + @NotNull + private String appKey; + + @NotNull + private String appLicense; + + public ProductConfigResponse() { + } + + @Override + public String toString() { + return "ProductConfigResponse{" + + "ntpServerHost='" + ntpServerHost + '\'' + + ", appId='" + appId + '\'' + + ", appKey='" + appKey + '\'' + + ", appLicense='" + appLicense + '\'' + + '}'; + } + + public String getNtpServerHost() { + return ntpServerHost; + } + + public ProductConfigResponse setNtpServerHost(String ntpServerHost) { + this.ntpServerHost = ntpServerHost; + return this; + } + + public String getAppId() { + return appId; + } + + public ProductConfigResponse setAppId(String appId) { + this.appId = appId; + return this; + } + + public String getAppKey() { + return appKey; + } + + public ProductConfigResponse setAppKey(String appKey) { + this.appKey = appKey; + return this; + } + + public String getAppLicense() { + return appLicense; + } + + public ProductConfigResponse setAppLicense(String appLicense) { + this.appLicense = appLicense; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/config/RequestsConfigRequest.java b/src/main/java/com/dji/sdk/cloudapi/config/RequestsConfigRequest.java new file mode 100644 index 0000000..c25d861 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/config/RequestsConfigRequest.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.config; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/10 + */ +public class RequestsConfigRequest { + + private ConfigTypeEnum configType; + + private ConfigScopeEnum configScope; + + public RequestsConfigRequest() { + } + + @Override + public String toString() { + return "RequestsConfigRequest{" + + "configType=" + configType + + ", configScope=" + configScope + + '}'; + } + + public ConfigTypeEnum getConfigType() { + return configType; + } + + public RequestsConfigRequest setConfigType(ConfigTypeEnum configType) { + this.configType = configType; + return this; + } + + public ConfigScopeEnum getConfigScope() { + return configScope; + } + + public RequestsConfigRequest setConfigScope(ConfigScopeEnum configScope) { + this.configScope = configScope; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/config/api/AbstractConfigService.java b/src/main/java/com/dji/sdk/cloudapi/config/api/AbstractConfigService.java new file mode 100644 index 0000000..36b866f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/config/api/AbstractConfigService.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.config.api; + +import com.dji.sdk.cloudapi.config.ProductConfigResponse; +import com.dji.sdk.cloudapi.config.RequestsConfigRequest; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public abstract class AbstractConfigService { + + /** + * Inform of file uploading progress + * @param request data + * @param headers The headers for a {@link Message}. + * @return requests_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_CONFIG, outputChannel = ChannelName.OUTBOUND_REQUESTS) + public TopicRequestsResponse requestsConfig(TopicRequestsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("requestsConfig not implemented"); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/CameraAimRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/CameraAimRequest.java new file mode 100644 index 0000000..ab9ea9d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/CameraAimRequest.java @@ -0,0 +1,99 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public class CameraAimRequest extends BaseModel { + + @NotNull + private PayloadIndex payloadIndex; + + @NotNull + private CameraTypeEnum cameraType; + + /** + * true: Lock the gimbal, the gimbal and the drone rotate together. + * false: Only the gimbal rotates, but the drone does not. + */ + @NotNull + private Boolean locked; + + /** + * upper left corner as center point + */ + @Min(0) + @Max(1) + private Float x; + + @Min(0) + @Max(1) + private Float y; + + public CameraAimRequest() { + } + + @Override + public String toString() { + return "CameraAimRequest{" + + "payloadIndex=" + payloadIndex + + ", cameraType=" + cameraType + + ", locked=" + locked + + ", x=" + x + + ", y=" + y + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public CameraAimRequest setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public CameraTypeEnum getCameraType() { + return cameraType; + } + + public CameraAimRequest setCameraType(CameraTypeEnum cameraType) { + this.cameraType = cameraType; + return this; + } + + public Boolean getLocked() { + return locked; + } + + public CameraAimRequest setLocked(Boolean locked) { + this.locked = locked; + return this; + } + + public Float getX() { + return x; + } + + public CameraAimRequest setX(Float x) { + this.x = x; + return this; + } + + public Float getY() { + return y; + } + + public CameraAimRequest setY(Float y) { + this.y = y; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/CameraFocalLengthSetRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/CameraFocalLengthSetRequest.java new file mode 100644 index 0000000..73de6a0 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/CameraFocalLengthSetRequest.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public class CameraFocalLengthSetRequest extends BaseModel { + + @NotNull + private PayloadIndex payloadIndex; + + @NotNull + private ZoomCameraTypeEnum cameraType; + + @Min(2) + @Max(200) + @NotNull + private Float zoomFactor; + + public CameraFocalLengthSetRequest() { + } + + @Override + public String toString() { + return "CameraFocalLengthSetRequest{" + + "payloadIndex=" + payloadIndex + + ", cameraType=" + cameraType + + ", zoomFactor=" + zoomFactor + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public CameraFocalLengthSetRequest setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public ZoomCameraTypeEnum getCameraType() { + return cameraType; + } + + public CameraFocalLengthSetRequest setCameraType(ZoomCameraTypeEnum cameraType) { + this.cameraType = cameraType; + return this; + } + + public Float getZoomFactor() { + return zoomFactor; + } + + public CameraFocalLengthSetRequest setZoomFactor(Float zoomFactor) { + this.zoomFactor = zoomFactor; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/CameraModeSwitchRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/CameraModeSwitchRequest.java new file mode 100644 index 0000000..37d00c8 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/CameraModeSwitchRequest.java @@ -0,0 +1,50 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.CameraModeEnum; +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public class CameraModeSwitchRequest extends BaseModel { + + @NotNull + private PayloadIndex payloadIndex; + + @NotNull + private CameraModeEnum cameraMode; + + public CameraModeSwitchRequest() { + } + + @Override + public String toString() { + return "CameraModeSwitchRequest{" + + "payloadIndex=" + payloadIndex + + ", cameraMode=" + cameraMode + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public CameraModeSwitchRequest setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public CameraModeEnum getCameraMode() { + return cameraMode; + } + + public CameraModeSwitchRequest setCameraMode(CameraModeEnum cameraMode) { + this.cameraMode = cameraMode; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/CameraPhotoTakeRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/CameraPhotoTakeRequest.java new file mode 100644 index 0000000..a2fef96 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/CameraPhotoTakeRequest.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public class CameraPhotoTakeRequest extends BaseModel { + + @NotNull + private PayloadIndex payloadIndex; + + public CameraPhotoTakeRequest() { + } + + @Override + public String toString() { + return "CameraPhotoTakeRequest{" + + "payloadIndex=" + payloadIndex + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public CameraPhotoTakeRequest setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/CameraRecordingStartRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/CameraRecordingStartRequest.java new file mode 100644 index 0000000..f695c7d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/CameraRecordingStartRequest.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public class CameraRecordingStartRequest extends BaseModel { + + @NotNull + private PayloadIndex payloadIndex; + + public CameraRecordingStartRequest() { + } + + @Override + public String toString() { + return "CameraRecordingStartRequest{" + + "payloadIndex=" + payloadIndex + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public CameraRecordingStartRequest setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/CameraRecordingStopRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/CameraRecordingStopRequest.java new file mode 100644 index 0000000..f98e268 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/CameraRecordingStopRequest.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public class CameraRecordingStopRequest extends BaseModel { + + @NotNull + private PayloadIndex payloadIndex; + + public CameraRecordingStopRequest() { + } + + @Override + public String toString() { + return "CameraRecordingStopRequest{" + + "payloadIndex=" + payloadIndex + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public CameraRecordingStopRequest setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } +} diff --git a/src/main/java/com/dji/sample/control/model/enums/CameraTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/CameraTypeEnum.java similarity index 55% rename from src/main/java/com/dji/sample/control/model/enums/CameraTypeEnum.java rename to src/main/java/com/dji/sdk/cloudapi/control/CameraTypeEnum.java index cda43e1..03dd866 100644 --- a/src/main/java/com/dji/sample/control/model/enums/CameraTypeEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/control/CameraTypeEnum.java @@ -1,8 +1,8 @@ -package com.dji.sample.control.model.enums; +package com.dji.sdk.cloudapi.control; +import com.dji.sdk.exception.CloudSDKException; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import lombok.Getter; import java.util.Arrays; @@ -11,7 +11,6 @@ import java.util.Arrays; * @version 1.4 * @date 2023/3/3 */ -@Getter public enum CameraTypeEnum { ZOOM("zoom"), @@ -20,7 +19,7 @@ public enum CameraTypeEnum { IR("ir"); - String type; + private final String type; CameraTypeEnum(String type) { this.type = type; @@ -32,7 +31,8 @@ public enum CameraTypeEnum { } @JsonCreator - public static CameraTypeEnum find(String cameraType) { - return Arrays.stream(CameraTypeEnum.values()).filter(typeEnum -> typeEnum.type.equals(cameraType)).findAny().get(); + public static CameraTypeEnum find(String type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type.equals(type)).findAny() + .orElseThrow(() -> new CloudSDKException(CameraTypeEnum.class, type)); } } diff --git a/src/main/java/com/dji/sdk/cloudapi/control/CommanderFlightModeEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/CommanderFlightModeEnum.java new file mode 100644 index 0000000..9e2ea90 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/CommanderFlightModeEnum.java @@ -0,0 +1,37 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/8/7 + */ +public enum CommanderFlightModeEnum { + + SMART_HEIGHT(0), + + SETTING_HEIGHT(1); + + private final int mode; + + CommanderFlightModeEnum(int mode) { + this.mode = mode; + } + + @JsonValue + public int getMode() { + return mode; + } + + @JsonCreator + public static CommanderFlightModeEnum find(int mode) { + return Arrays.stream(values()).filter(modeEnum -> modeEnum.mode == mode).findAny() + .orElseThrow(() -> new CloudSDKException(CommanderFlightModeEnum.class, mode)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/CommanderModeLostActionEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/CommanderModeLostActionEnum.java new file mode 100755 index 0000000..f3f630e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/CommanderModeLostActionEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public enum CommanderModeLostActionEnum { + + CONTINUE(0), + + EXECUTE_RC_LOST_ACTION(1); + + private final int action; + + CommanderModeLostActionEnum(int action) { + this.action = action; + } + + @JsonValue + public int getAction() { + return action; + } + + @JsonCreator + public static CommanderModeLostActionEnum find(int action) { + return Arrays.stream(values()).filter(actionEnum -> actionEnum.action == action).findAny() + .orElseThrow(() -> new CloudSDKException(CommanderModeLostActionEnum.class, action)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/ControlErrorCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/ControlErrorCodeEnum.java new file mode 100644 index 0000000..90c782c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/ControlErrorCodeEnum.java @@ -0,0 +1,91 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.mqtt.events.IEventsErrorCode; +import com.dji.sdk.mqtt.services.IServicesErrorCode; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public enum ControlErrorCodeEnum implements IServicesErrorCode, IEventsErrorCode, IErrorInfo { + + SETTING_RTH_FAILED(327000, "Height of return to home setting failed."), + + SETTING_LOST_ACTION_FAILED(327001, "Signal lost action setting failed."), + + OBTAIN_CONTROL_FAILED(327002, "Failed to obtain control."), + + DEVICE_OFFLINE(327003, "Failed to obtain control. Device offline."), + + DRAG_LIVESTREAM_VIEW_FAILED(327004, "Failed to drag livestream view."), + + AIM_FAILED(327005, "Failed to double tab to be AIM."), + + TAKE_PHOTO_FAILED(327006, "Failed to take photo."), + + START_RECORDING_FAILED(327007, "Failed to start recording."), + + STOP_RECORDING_FAILED(327008, "Failed to stop recording."), + + SWITCH_CAMERA_MODE_FAILED(327009, "Failed to switch camera modes."), + + ZOOM_CAMERA_ZOOM_FAILED(327010, "Failed to zoom in/out with zoom camera."), + + IR_CAMERA_ZOOM_FAILED(327011, "Failed to zoom in/out with IR camera."), + + DEVICE_LOCK(327012, "Failed to obtain control. Device is locked."), + + SETTING_WAYLINE_LOST_ACTION_FAILED(327013, "Wayline signal lost action setting failed."), + + GIMBAL_REACH_LIMIT(327014, "Gimbal reached movement limit."), + + WRONG_LENS_TYPE(327015, "Invalid camera lens type."), + + + DRC_ABNORMAL(514300, "DRC abnormal."), + + DRC_HEARTBEAT_TIMED_OUT(514301, "DRC heartbeat timed out."), + + DRC_CERTIFICATE_ABNORMAL(514302, "DRC certificate is abnormal."), + + DRC_LINK_LOST(514303, "DRC link is lost."), + + DRC_LINK_REFUSED(514304, "DRC link is refused."), + + UNKNOWN(-1, "UNKNOWN"), + + ; + + + private final String msg; + + private final int code; + + ControlErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String getMessage() { + return this.msg; + } + + @Override + public Integer getCode() { + return this.code; + } + + /** + * @param code error code + * @return enumeration object + */ + public static ControlErrorCodeEnum find(int code) { + return Arrays.stream(values()).filter(codeEnum -> codeEnum.code == code).findAny().orElse(UNKNOWN); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/ControlMethodEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/ControlMethodEnum.java new file mode 100644 index 0000000..4aab79d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/ControlMethodEnum.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.control; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/2 + */ +public enum ControlMethodEnum { + + FLIGHT_AUTHORITY_GRAB("flight_authority_grab"), + + PAYLOAD_AUTHORITY_GRAB("payload_authority_grab"), + + DRC_MODE_ENTER("drc_mode_enter"), + + DRC_MODE_EXIT("drc_mode_exit"), + + FLY_TO_POINT("fly_to_point"), + + FLY_TO_POINT_STOP("fly_to_point_stop"), + + TAKEOFF_TO_POINT("takeoff_to_point"), + + CAMERA_MODE_SWITCH("camera_mode_switch"), + + CAMERA_PHOTO_TAKE("camera_photo_take"), + + CAMERA_RECORDING_START("camera_recording_start"), + + CAMERA_RECORDING_STOP("camera_recording_stop"), + + CAMERA_AIM("camera_aim"), + + CAMERA_FOCAL_LENGTH_SET("camera_focal_length_set"), + + GIMBAL_RESET("gimbal_reset"), + + DRONE_CONTROL("drone_control"), + + DRONE_EMERGENCY_STOP("drone_emergency_stop"), + + HEART_BEAT("heart_beat"); + + private final String method; + + ControlMethodEnum(String method) { + this.method = method; + } + + public String getMethod() { + return method; + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/DelayInfoPush.java b/src/main/java/com/dji/sdk/cloudapi/control/DelayInfoPush.java new file mode 100644 index 0000000..d7a8caa --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/DelayInfoPush.java @@ -0,0 +1,44 @@ +package com.dji.sdk.cloudapi.control; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class DelayInfoPush { + + private Integer sdrCmdDelay; + + private List liveviewDelayList; + + public DelayInfoPush() { + } + + @Override + public String toString() { + return "DelayInfoPush{" + + "sdrCmdDelay=" + sdrCmdDelay + + ", liveviewDelayList=" + liveviewDelayList + + '}'; + } + + public Integer getSdrCmdDelay() { + return sdrCmdDelay; + } + + public DelayInfoPush setSdrCmdDelay(Integer sdrCmdDelay) { + this.sdrCmdDelay = sdrCmdDelay; + return this; + } + + public List getLiveviewDelayList() { + return liveviewDelayList; + } + + public DelayInfoPush setLiveviewDelayList(List liveviewDelayList) { + this.liveviewDelayList = liveviewDelayList; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/DrcModeEnterRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/DrcModeEnterRequest.java new file mode 100644 index 0000000..7c5bcf2 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/DrcModeEnterRequest.java @@ -0,0 +1,75 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2023/1/12 + */ +public class DrcModeEnterRequest extends BaseModel { + + @NotNull + @Valid + private DrcModeMqttBroker mqttBroker; + + /** + * range: 1 - 30 + */ + @Min(1) + @Max(30) + @NotNull + private Integer osdFrequency; + + /** + * range: 1 - 30 + */ + @Min(1) + @Max(30) + @NotNull + private Integer hsiFrequency; + + public DrcModeEnterRequest() { + } + + @Override + public String toString() { + return "DrcModeEnterRequest{" + + "mqttBroker=" + mqttBroker + + ", osdFrequency=" + osdFrequency + + ", hsiFrequency=" + hsiFrequency + + '}'; + } + + public DrcModeMqttBroker getMqttBroker() { + return mqttBroker; + } + + public DrcModeEnterRequest setMqttBroker(DrcModeMqttBroker mqttBroker) { + this.mqttBroker = mqttBroker; + return this; + } + + public Integer getOsdFrequency() { + return osdFrequency; + } + + public DrcModeEnterRequest setOsdFrequency(Integer osdFrequency) { + this.osdFrequency = osdFrequency; + return this; + } + + public Integer getHsiFrequency() { + return hsiFrequency; + } + + public DrcModeEnterRequest setHsiFrequency(Integer hsiFrequency) { + this.hsiFrequency = hsiFrequency; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/DrcModeMqttBroker.java b/src/main/java/com/dji/sdk/cloudapi/control/DrcModeMqttBroker.java new file mode 100644 index 0000000..7b16c71 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/DrcModeMqttBroker.java @@ -0,0 +1,102 @@ +package com.dji.sdk.cloudapi.control; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2023/1/11 + */ +public class DrcModeMqttBroker { + + @NotNull + private String address; + + @NotNull + private String username; + + @NotNull + private String password; + + @NotNull + private String clientId; + + @NotNull + @Min(1234567890) + @Max(9999999999L) + private Long expireTime; + + @NotNull + private Boolean enableTls; + + public DrcModeMqttBroker() { + } + + @Override + public String toString() { + return "DrcModeMqttBroker{" + + "address='" + address + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", clientId='" + clientId + '\'' + + ", expireTime=" + expireTime + + ", enableTls=" + enableTls + + '}'; + } + + public String getAddress() { + return address; + } + + public DrcModeMqttBroker setAddress(String address) { + this.address = address; + return this; + } + + public String getUsername() { + return username; + } + + public DrcModeMqttBroker setUsername(String username) { + this.username = username; + return this; + } + + public String getPassword() { + return password; + } + + public DrcModeMqttBroker setPassword(String password) { + this.password = password; + return this; + } + + public String getClientId() { + return clientId; + } + + public DrcModeMqttBroker setClientId(String clientId) { + this.clientId = clientId; + return this; + } + + public Long getExpireTime() { + return expireTime; + } + + public DrcModeMqttBroker setExpireTime(Long expireTime) { + this.expireTime = expireTime; + return this; + } + + public Boolean getEnableTls() { + return enableTls; + } + + public DrcModeMqttBroker setEnableTls(Boolean enableTls) { + this.enableTls = enableTls; + return this; + } +} diff --git a/src/main/java/com/dji/sample/control/model/enums/DrcStatusErrorEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/DrcStatusErrorEnum.java similarity index 72% rename from src/main/java/com/dji/sample/control/model/enums/DrcStatusErrorEnum.java rename to src/main/java/com/dji/sdk/cloudapi/control/DrcStatusErrorEnum.java index 1cee634..34dc7b4 100644 --- a/src/main/java/com/dji/sample/control/model/enums/DrcStatusErrorEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/control/DrcStatusErrorEnum.java @@ -1,6 +1,7 @@ -package com.dji.sample.control.model.enums; +package com.dji.sdk.cloudapi.control; -import com.dji.sample.common.error.IErrorInfo; +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.exception.CloudSDKException; import com.fasterxml.jackson.annotation.JsonCreator; import java.util.Arrays; @@ -22,13 +23,11 @@ public enum DrcStatusErrorEnum implements IErrorInfo { MQTT_LOST(514303, "The dock network is abnormal and the mqtt connection is lost."), - MQTT_REFUSE(514304, "The dock connection to mqtt server was refused."), + MQTT_REFUSE(514304, "The dock connection to mqtt server was refused."); - UNKNOWN(-1, "Unknown"); + private final String msg; - String msg; - - int code; + private final int code; DrcStatusErrorEnum(int code, String msg) { this.code = code; @@ -36,17 +35,18 @@ public enum DrcStatusErrorEnum implements IErrorInfo { } @Override - public String getErrorMsg() { + public String getMessage() { return msg; } @Override - public Integer getErrorCode() { + public Integer getCode() { return code; } @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public static DrcStatusErrorEnum find(int code) { - return Arrays.stream(values()).filter(error -> error.code == code).findAny().orElse(UNKNOWN); + return Arrays.stream(values()).filter(error -> error.code == code).findAny() + .orElseThrow(() -> new CloudSDKException(DrcStatusErrorEnum.class, code)); } } diff --git a/src/main/java/com/dji/sdk/cloudapi/control/DrcStatusNotify.java b/src/main/java/com/dji/sdk/cloudapi/control/DrcStatusNotify.java new file mode 100644 index 0000000..4b04670 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/DrcStatusNotify.java @@ -0,0 +1,44 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.DrcStateEnum; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/17 + */ +public class DrcStatusNotify { + + private DrcStatusErrorEnum result; + + private DrcStateEnum drcState; + + public DrcStatusNotify() { + } + + @Override + public String toString() { + return "DrcStatusNotify{" + + "result=" + result + + ", drcState=" + drcState + + '}'; + } + + public DrcStatusErrorEnum getResult() { + return result; + } + + public DrcStatusNotify setResult(DrcStatusErrorEnum result) { + this.result = result; + return this; + } + + public DrcStateEnum getDrcState() { + return drcState; + } + + public DrcStatusNotify setDrcState(DrcStateEnum drcState) { + this.drcState = drcState; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/DroneControlRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/DroneControlRequest.java new file mode 100644 index 0000000..e6ba180 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/DroneControlRequest.java @@ -0,0 +1,121 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class DroneControlRequest extends BaseModel { + + @NotNull + private Long seq; + + @Min(-17) + @Max(17) + private Float x; + + @Min(-17) + @Max(17) + private Float y; + + @Min(-4) + @Max(5) + private Float h; + + @Min(-90) + @Max(90) + private Float w; + + @Min(2) + @Max(10) + private Integer freq; + + @Min(100) + @Max(1000) + private Integer delayTime; + + public DroneControlRequest() { + } + + @Override + public String toString() { + return "DroneControlRequest{" + + "seq=" + seq + + ", x=" + x + + ", y=" + y + + ", h=" + h + + ", w=" + w + + ", freq=" + freq + + ", delayTime=" + delayTime + + '}'; + } + + public Long getSeq() { + return seq; + } + + public DroneControlRequest setSeq(Long seq) { + this.seq = seq; + return this; + } + + public Float getX() { + return x; + } + + public DroneControlRequest setX(Float x) { + this.x = x; + return this; + } + + public Float getY() { + return y; + } + + public DroneControlRequest setY(Float y) { + this.y = y; + return this; + } + + public Float getH() { + return h; + } + + public DroneControlRequest setH(Float h) { + this.h = h; + return this; + } + + public Float getW() { + return w; + } + + public DroneControlRequest setW(Float w) { + this.w = w; + return this; + } + + public Integer getFreq() { + return freq; + } + + public DroneControlRequest setFreq(Integer freq) { + this.freq = freq; + return this; + } + + public Integer getDelayTime() { + return delayTime; + } + + public DroneControlRequest setDelayTime(Integer delayTime) { + this.delayTime = delayTime; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/DroneControlResponse.java b/src/main/java/com/dji/sdk/cloudapi/control/DroneControlResponse.java new file mode 100644 index 0000000..d47b61b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/DroneControlResponse.java @@ -0,0 +1,31 @@ +package com.dji.sdk.cloudapi.control; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class DroneControlResponse { + + private Long seq; + + public DroneControlResponse() { + } + + @Override + public String toString() { + return "DroneControlResponse{" + + "seq=" + seq + + '}'; + } + + public Long getSeq() { + return seq; + } + + public DroneControlResponse setSeq(Long seq) { + this.seq = seq; + return this; + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/FlyToPointProgress.java b/src/main/java/com/dji/sdk/cloudapi/control/FlyToPointProgress.java new file mode 100644 index 0000000..ffdc8bc --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/FlyToPointProgress.java @@ -0,0 +1,68 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.wayline.WaylineErrorCodeEnum; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/14 + */ +public class FlyToPointProgress { + + private WaylineErrorCodeEnum result; + + private FlyToStatusEnum status; + + private String flyToId; + + private Integer wayPointIndex; + + public FlyToPointProgress() { + } + + @Override + public String toString() { + return "FlyToPointProgress{" + + "result=" + result + + ", status=" + status + + ", flyToId='" + flyToId + '\'' + + ", wayPointIndex=" + wayPointIndex + + '}'; + } + + public WaylineErrorCodeEnum getResult() { + return result; + } + + public FlyToPointProgress setResult(WaylineErrorCodeEnum result) { + this.result = result; + return this; + } + + public FlyToStatusEnum getStatus() { + return status; + } + + public FlyToPointProgress setStatus(FlyToStatusEnum status) { + this.status = status; + return this; + } + + public String getFlyToId() { + return flyToId; + } + + public FlyToPointProgress setFlyToId(String flyToId) { + this.flyToId = flyToId; + return this; + } + + public Integer getWayPointIndex() { + return wayPointIndex; + } + + public FlyToPointProgress setWayPointIndex(Integer wayPointIndex) { + this.wayPointIndex = wayPointIndex; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/FlyToPointRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/FlyToPointRequest.java new file mode 100644 index 0000000..8af4ca9 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/FlyToPointRequest.java @@ -0,0 +1,71 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.*; +import java.util.List; + +/** + * @author sean + * @version 1.3 + * @date 2023/2/14 + */ +public class FlyToPointRequest extends BaseModel { + + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @NotNull + private String flyToId; + + @Min(1) + @Max(15) + @NotNull + private Integer maxSpeed; + + /** + * The M30 series only support one point. + */ + @Size(min = 1) + @Valid + @NotNull + private List points; + + public FlyToPointRequest() { + } + + @Override + public String toString() { + return "FlyToPointRequest{" + + "flyToId='" + flyToId + '\'' + + ", maxSpeed=" + maxSpeed + + ", points=" + points + + '}'; + } + + public String getFlyToId() { + return flyToId; + } + + public FlyToPointRequest setFlyToId(String flyToId) { + this.flyToId = flyToId; + return this; + } + + public Integer getMaxSpeed() { + return maxSpeed; + } + + public FlyToPointRequest setMaxSpeed(Integer maxSpeed) { + this.maxSpeed = maxSpeed; + return this; + } + + public List getPoints() { + return points; + } + + public FlyToPointRequest setPoints(List points) { + this.points = points; + return this; + } +} diff --git a/src/main/java/com/dji/sample/control/model/enums/FlyToStatusEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/FlyToStatusEnum.java similarity index 60% rename from src/main/java/com/dji/sample/control/model/enums/FlyToStatusEnum.java rename to src/main/java/com/dji/sdk/cloudapi/control/FlyToStatusEnum.java index 4291769..f1d32a2 100644 --- a/src/main/java/com/dji/sample/control/model/enums/FlyToStatusEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/control/FlyToStatusEnum.java @@ -1,6 +1,8 @@ -package com.dji.sample.control.model.enums; +package com.dji.sdk.cloudapi.control; +import com.dji.sdk.exception.CloudSDKException; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; import java.util.Arrays; @@ -13,15 +15,15 @@ public enum FlyToStatusEnum { WAYLINE_PROGRESS("wayline_progress", "The FlyTo job is in progress."), - WAYLINE_FAILED("wayline_failed", "The Fly To task execution failed."), + WAYLINE_FAILED("wayline_failed", "The FlyTo job execution failed."), WAYLINE_OK("wayline_ok", "The FlyTo job executed successfully."), WAYLINE_CANCEL("wayline_cancel", "The FlyTo job is closed."); - String status; + private final String status; - String message; + private final String message; FlyToStatusEnum(String status, String message) { this.status = status; @@ -32,8 +34,14 @@ public enum FlyToStatusEnum { return message; } + @JsonValue + public String getStatus() { + return status; + } + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public static FlyToStatusEnum find(String status) { - return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny().get(); + return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny() + .orElseThrow(() -> new CloudSDKException(FlyToStatusEnum.class, status)); } } diff --git a/src/main/java/com/dji/sdk/cloudapi/control/GimbalResetModeEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/GimbalResetModeEnum.java new file mode 100644 index 0000000..8c14b6b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/GimbalResetModeEnum.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/13 + */ +public enum GimbalResetModeEnum { + + RECENTER(0), + + DOWN(1), + + RECENTER_PAN(2), + + PITCH_DOWN(3); + + private final int mode; + + GimbalResetModeEnum(int mode) { + this.mode = mode; + } + + @JsonValue + public int getMode() { + return mode; + } + + @JsonCreator + public static GimbalResetModeEnum find(int mode) { + return Arrays.stream(values()).filter(resetModeEnum -> resetModeEnum.ordinal() == mode).findAny() + .orElseThrow(() -> new CloudSDKException(GimbalResetModeEnum.class, mode)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/GimbalResetRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/GimbalResetRequest.java new file mode 100644 index 0000000..8030180 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/GimbalResetRequest.java @@ -0,0 +1,49 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public class GimbalResetRequest extends BaseModel { + + @NotNull + private PayloadIndex payloadIndex; + + @NotNull + private GimbalResetModeEnum resetMode; + + public GimbalResetRequest() { + } + + @Override + public String toString() { + return "GimbalResetRequest{" + + "payloadIndex=" + payloadIndex + + ", resetMode=" + resetMode + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public GimbalResetRequest setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public GimbalResetModeEnum getResetMode() { + return resetMode; + } + + public GimbalResetRequest setResetMode(GimbalResetModeEnum resetMode) { + this.resetMode = resetMode; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/HeartBeatRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/HeartBeatRequest.java new file mode 100644 index 0000000..8ae23af --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/HeartBeatRequest.java @@ -0,0 +1,50 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class HeartBeatRequest extends BaseModel { + + @NotNull + private Long seq; + + @NotNull + @Min(123456789012L) + private Long timestamp; + + public HeartBeatRequest() { + } + + @Override + public String toString() { + return "HeartBeatRequest{" + + "seq=" + seq + + ", timestamp=" + timestamp + + '}'; + } + + public Long getSeq() { + return seq; + } + + public HeartBeatRequest setSeq(Long seq) { + this.seq = seq; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public HeartBeatRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/HsiInfoPush.java b/src/main/java/com/dji/sdk/cloudapi/control/HsiInfoPush.java new file mode 100644 index 0000000..c9c5712 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/HsiInfoPush.java @@ -0,0 +1,248 @@ +package com.dji.sdk.cloudapi.control; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class HsiInfoPush { + + private Integer upDistance; + + private Integer downDistance; + + private List aroundDistance; + + private Boolean upEnable; + + private Boolean upWork; + + private Boolean downEnable; + + private Boolean downWork; + + private Boolean leftEnable; + + private Boolean leftWork; + + private Boolean rightEnable; + + private Boolean rightWork; + + private Boolean frontEnable; + + private Boolean frontWork; + + private Boolean backEnable; + + private Boolean backWork; + + private Boolean verticalEnable; + + private Boolean verticalWork; + + private Boolean horizontalEnable; + + private Boolean horizontalWork; + + public HsiInfoPush() { + } + + @Override + public String toString() { + return "HsiInfoPush{" + + "upDistance=" + upDistance + + ", downDistance=" + downDistance + + ", aroundDistance=" + aroundDistance + + ", upEnable=" + upEnable + + ", upWork=" + upWork + + ", downEnable=" + downEnable + + ", downWork=" + downWork + + ", leftEnable=" + leftEnable + + ", leftWork=" + leftWork + + ", rightEnable=" + rightEnable + + ", rightWork=" + rightWork + + ", frontEnable=" + frontEnable + + ", frontWork=" + frontWork + + ", backEnable=" + backEnable + + ", backWork=" + backWork + + ", verticalEnable=" + verticalEnable + + ", verticalWork=" + verticalWork + + ", horizontalEnable=" + horizontalEnable + + ", horizontalWork=" + horizontalWork + + '}'; + } + + public Integer getUpDistance() { + return upDistance; + } + + public HsiInfoPush setUpDistance(Integer upDistance) { + this.upDistance = upDistance; + return this; + } + + public Integer getDownDistance() { + return downDistance; + } + + public HsiInfoPush setDownDistance(Integer downDistance) { + this.downDistance = downDistance; + return this; + } + + public List getAroundDistance() { + return aroundDistance; + } + + public HsiInfoPush setAroundDistance(List aroundDistance) { + this.aroundDistance = aroundDistance; + return this; + } + + public Boolean getUpEnable() { + return upEnable; + } + + public HsiInfoPush setUpEnable(Boolean upEnable) { + this.upEnable = upEnable; + return this; + } + + public Boolean getUpWork() { + return upWork; + } + + public HsiInfoPush setUpWork(Boolean upWork) { + this.upWork = upWork; + return this; + } + + public Boolean getDownEnable() { + return downEnable; + } + + public HsiInfoPush setDownEnable(Boolean downEnable) { + this.downEnable = downEnable; + return this; + } + + public Boolean getDownWork() { + return downWork; + } + + public HsiInfoPush setDownWork(Boolean downWork) { + this.downWork = downWork; + return this; + } + + public Boolean getLeftEnable() { + return leftEnable; + } + + public HsiInfoPush setLeftEnable(Boolean leftEnable) { + this.leftEnable = leftEnable; + return this; + } + + public Boolean getLeftWork() { + return leftWork; + } + + public HsiInfoPush setLeftWork(Boolean leftWork) { + this.leftWork = leftWork; + return this; + } + + public Boolean getRightEnable() { + return rightEnable; + } + + public HsiInfoPush setRightEnable(Boolean rightEnable) { + this.rightEnable = rightEnable; + return this; + } + + public Boolean getRightWork() { + return rightWork; + } + + public HsiInfoPush setRightWork(Boolean rightWork) { + this.rightWork = rightWork; + return this; + } + + public Boolean getFrontEnable() { + return frontEnable; + } + + public HsiInfoPush setFrontEnable(Boolean frontEnable) { + this.frontEnable = frontEnable; + return this; + } + + public Boolean getFrontWork() { + return frontWork; + } + + public HsiInfoPush setFrontWork(Boolean frontWork) { + this.frontWork = frontWork; + return this; + } + + public Boolean getBackEnable() { + return backEnable; + } + + public HsiInfoPush setBackEnable(Boolean backEnable) { + this.backEnable = backEnable; + return this; + } + + public Boolean getBackWork() { + return backWork; + } + + public HsiInfoPush setBackWork(Boolean backWork) { + this.backWork = backWork; + return this; + } + + public Boolean getVerticalEnable() { + return verticalEnable; + } + + public HsiInfoPush setVerticalEnable(Boolean verticalEnable) { + this.verticalEnable = verticalEnable; + return this; + } + + public Boolean getVerticalWork() { + return verticalWork; + } + + public HsiInfoPush setVerticalWork(Boolean verticalWork) { + this.verticalWork = verticalWork; + return this; + } + + public Boolean getHorizontalEnable() { + return horizontalEnable; + } + + public HsiInfoPush setHorizontalEnable(Boolean horizontalEnable) { + this.horizontalEnable = horizontalEnable; + return this; + } + + public Boolean getHorizontalWork() { + return horizontalWork; + } + + public HsiInfoPush setHorizontalWork(Boolean horizontalWork) { + this.horizontalWork = horizontalWork; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/JoystickInvalidNotify.java b/src/main/java/com/dji/sdk/cloudapi/control/JoystickInvalidNotify.java new file mode 100644 index 0000000..29e2f79 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/JoystickInvalidNotify.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.control; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/14 + */ +public class JoystickInvalidNotify { + + private JoystickInvalidReasonEnum reason; + + public JoystickInvalidNotify() { + } + + @Override + public String toString() { + return "JoystickInvalidNotify{" + + "reason=" + reason + + '}'; + } + + public JoystickInvalidReasonEnum getReason() { + return reason; + } + + public JoystickInvalidNotify setReason(JoystickInvalidReasonEnum reason) { + this.reason = reason; + return this; + } +} diff --git a/src/main/java/com/dji/sample/control/model/enums/DrcModeReasonEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/JoystickInvalidReasonEnum.java similarity index 57% rename from src/main/java/com/dji/sample/control/model/enums/DrcModeReasonEnum.java rename to src/main/java/com/dji/sdk/cloudapi/control/JoystickInvalidReasonEnum.java index ceb20ac..190783e 100644 --- a/src/main/java/com/dji/sample/control/model/enums/DrcModeReasonEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/control/JoystickInvalidReasonEnum.java @@ -1,6 +1,8 @@ -package com.dji.sample.control.model.enums; +package com.dji.sdk.cloudapi.control; +import com.dji.sdk.exception.CloudSDKException; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; import java.util.Arrays; @@ -9,9 +11,7 @@ import java.util.Arrays; * @version 1.4 * @date 2023/3/14 */ -public enum DrcModeReasonEnum { - - UNKNOWN(-1, "unknown"), +public enum JoystickInvalidReasonEnum { RC_LOST(0, "The remote controller is lost."), @@ -23,17 +23,18 @@ public enum DrcModeReasonEnum { RC_AUTHORITY(4, "The remote controller grabs control authority."); - int val; + private final int reason; - String message; + private final String message; - DrcModeReasonEnum(int val, String message) { - this.val = val; + JoystickInvalidReasonEnum(int reason, String message) { + this.reason = reason; this.message = message; } + @JsonValue public int getVal() { - return val; + return reason; } public String getMessage() { @@ -41,7 +42,8 @@ public enum DrcModeReasonEnum { } @JsonCreator(mode = JsonCreator.Mode.DELEGATING) - public static DrcModeReasonEnum find(int val) { - return Arrays.stream(values()).filter(reasonEnum -> reasonEnum.val == val).findAny().orElse(UNKNOWN); + public static JoystickInvalidReasonEnum find(int reason) { + return Arrays.stream(values()).filter(reasonEnum -> reasonEnum.reason == reason).findAny() + .orElseThrow(() -> new CloudSDKException(JoystickInvalidReasonEnum.class, reason)); } } diff --git a/src/main/java/com/dji/sdk/cloudapi/control/LiveviewDelay.java b/src/main/java/com/dji/sdk/cloudapi/control/LiveviewDelay.java new file mode 100644 index 0000000..7e7fb1e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/LiveviewDelay.java @@ -0,0 +1,35 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.VideoId; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class LiveviewDelay { + + private Integer liveviewDelayTime; + + private VideoId videoId; + + public LiveviewDelay() { + } + + @Override + public String toString() { + return "LiveviewDelay{" + + "liveviewDelayTime=" + liveviewDelayTime + + ", videoId=" + videoId + + '}'; + } + + public Integer getLiveviewDelayTime() { + return liveviewDelayTime; + } + + public LiveviewDelay setLiveviewDelayTime(Integer liveviewDelayTime) { + this.liveviewDelayTime = liveviewDelayTime; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/OsdInfoPush.java b/src/main/java/com/dji/sdk/cloudapi/control/OsdInfoPush.java new file mode 100644 index 0000000..5e80741 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/OsdInfoPush.java @@ -0,0 +1,138 @@ +package com.dji.sdk.cloudapi.control; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class OsdInfoPush { + + private Float attitudeHead; + + private Float latitude; + + private Float longitude; + + private Float height; + + private Float speedX; + + private Float speedY; + + private Float speedZ; + + private Float gimbalPitch; + + private Float gimbalRoll; + + private Float gimbalYaw; + + public OsdInfoPush() { + } + + @Override + public String toString() { + return "OsdInfoPush{" + + "attitudeHead=" + attitudeHead + + ", latitude=" + latitude + + ", longitude=" + longitude + + ", height=" + height + + ", speedX=" + speedX + + ", speedY=" + speedY + + ", speedZ=" + speedZ + + ", gimbalPitch=" + gimbalPitch + + ", gimbalRoll=" + gimbalRoll + + ", gimbalYaw=" + gimbalYaw + + '}'; + } + + public Float getAttitudeHead() { + return attitudeHead; + } + + public OsdInfoPush setAttitudeHead(Float attitudeHead) { + this.attitudeHead = attitudeHead; + return this; + } + + public Float getLatitude() { + return latitude; + } + + public OsdInfoPush setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public OsdInfoPush setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public Float getHeight() { + return height; + } + + public OsdInfoPush setHeight(Float height) { + this.height = height; + return this; + } + + public Float getSpeedX() { + return speedX; + } + + public OsdInfoPush setSpeedX(Float speedX) { + this.speedX = speedX; + return this; + } + + public Float getSpeedY() { + return speedY; + } + + public OsdInfoPush setSpeedY(Float speedY) { + this.speedY = speedY; + return this; + } + + public Float getSpeedZ() { + return speedZ; + } + + public OsdInfoPush setSpeedZ(Float speedZ) { + this.speedZ = speedZ; + return this; + } + + public Float getGimbalPitch() { + return gimbalPitch; + } + + public OsdInfoPush setGimbalPitch(Float gimbalPitch) { + this.gimbalPitch = gimbalPitch; + return this; + } + + public Float getGimbalRoll() { + return gimbalRoll; + } + + public OsdInfoPush setGimbalRoll(Float gimbalRoll) { + this.gimbalRoll = gimbalRoll; + return this; + } + + public Float getGimbalYaw() { + return gimbalYaw; + } + + public OsdInfoPush setGimbalYaw(Float gimbalYaw) { + this.gimbalYaw = gimbalYaw; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/PayloadAuthorityGrabRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/PayloadAuthorityGrabRequest.java new file mode 100644 index 0000000..4b99053 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/PayloadAuthorityGrabRequest.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class PayloadAuthorityGrabRequest extends BaseModel { + + @NotNull + private PayloadIndex payloadIndex; + + public PayloadAuthorityGrabRequest() { + } + + @Override + public String toString() { + return "PayloadAuthorityGrabRequest{" + + "payloadIndex=" + payloadIndex + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public PayloadAuthorityGrabRequest setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/PayloadControlMethodEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/PayloadControlMethodEnum.java new file mode 100644 index 0000000..d9a14aa --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/PayloadControlMethodEnum.java @@ -0,0 +1,51 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/2 + */ +public enum PayloadControlMethodEnum { + + CAMERA_MODE_SWITCH(ControlMethodEnum.CAMERA_MODE_SWITCH, CameraModeSwitchRequest.class), + + CAMERA_PHOTO_TAKE(ControlMethodEnum.CAMERA_PHOTO_TAKE, CameraPhotoTakeRequest.class), + + CAMERA_RECORDING_START(ControlMethodEnum.CAMERA_RECORDING_START, CameraRecordingStartRequest.class), + + CAMERA_RECORDING_STOP(ControlMethodEnum.CAMERA_RECORDING_STOP, CameraRecordingStopRequest.class), + + CAMERA_AIM(ControlMethodEnum.CAMERA_AIM, CameraAimRequest.class), + + CAMERA_FOCAL_LENGTH_SET(ControlMethodEnum.CAMERA_FOCAL_LENGTH_SET, CameraFocalLengthSetRequest.class), + + GIMBAL_RESET(ControlMethodEnum.GIMBAL_RESET, GimbalResetRequest.class), + ; + + private final ControlMethodEnum payloadMethod; + + private final Class clazz; + + PayloadControlMethodEnum(ControlMethodEnum payloadMethod, Class clazz) { + this.payloadMethod = payloadMethod; + this.clazz = clazz; + } + + public ControlMethodEnum getPayloadMethod() { + return payloadMethod; + } + + public Class getClazz() { + return clazz; + } + + public static PayloadControlMethodEnum find(String method) { + return Arrays.stream(values()).filter(methodEnum -> methodEnum.payloadMethod.getMethod().equals(method)).findAny() + .orElseThrow(() -> new CloudSDKException(PayloadControlMethodEnum.class, method)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/Point.java b/src/main/java/com/dji/sdk/cloudapi/control/Point.java new file mode 100644 index 0000000..f44e76c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/Point.java @@ -0,0 +1,71 @@ +package com.dji.sdk.cloudapi.control; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2023/2/14 + */ +public class Point { + + @Min(-90) + @Max(90) + @NotNull + private Float latitude; + + @NotNull + @Min(-180) + @Max(180) + private Float longitude; + + /** + * WGS84 + * The M30 series are ellipsoidal heights. + */ + @NotNull + @Min(2) + @Max(10000) + private Float height; + + public Point() { + } + + @Override + public String toString() { + return "Point{" + + "latitude=" + latitude + + ", longitude=" + longitude + + ", height=" + height + + '}'; + } + + public Float getLatitude() { + return latitude; + } + + public Point setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public Point setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public Float getHeight() { + return height; + } + + public Point setHeight(Float height) { + this.height = height; + return this; + } +} diff --git a/src/main/java/com/dji/sample/control/model/enums/TakeoffStatusEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/TakeoffStatusEnum.java similarity index 74% rename from src/main/java/com/dji/sample/control/model/enums/TakeoffStatusEnum.java rename to src/main/java/com/dji/sdk/cloudapi/control/TakeoffStatusEnum.java index 94ae3de..968255b 100644 --- a/src/main/java/com/dji/sample/control/model/enums/TakeoffStatusEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/control/TakeoffStatusEnum.java @@ -1,6 +1,8 @@ -package com.dji.sample.control.model.enums; +package com.dji.sdk.cloudapi.control; +import com.dji.sdk.exception.CloudSDKException; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; import java.util.Arrays; @@ -23,15 +25,16 @@ public enum TakeoffStatusEnum { TASK_FINISH("task_finish", "The drone takeoff job is completed."); - String status; + private final String status; - String message; + private final String message; TakeoffStatusEnum(String status, String message) { this.status = status; this.message = message; } + @JsonValue public String getStatus() { return status; } @@ -42,6 +45,7 @@ public enum TakeoffStatusEnum { @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public static TakeoffStatusEnum find(String status) { - return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny().get(); + return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny() + .orElseThrow(() -> new CloudSDKException(TakeoffStatusEnum.class, status)); } } diff --git a/src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointProgress.java b/src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointProgress.java new file mode 100644 index 0000000..008e238 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointProgress.java @@ -0,0 +1,80 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.cloudapi.wayline.WaylineErrorCodeEnum; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/14 + */ +public class TakeoffToPointProgress { + + private WaylineErrorCodeEnum result; + + private TakeoffStatusEnum status; + + private String flightId; + + private String trackId; + + private Integer wayPointIndex; + + public TakeoffToPointProgress() { + } + + @Override + public String toString() { + return "TakeoffToPointProgress{" + + "result=" + result + + ", status=" + status + + ", flightId='" + flightId + '\'' + + ", trackId='" + trackId + '\'' + + ", wayPointIndex=" + wayPointIndex + + '}'; + } + + public WaylineErrorCodeEnum getResult() { + return result; + } + + public TakeoffToPointProgress setResult(WaylineErrorCodeEnum result) { + this.result = result; + return this; + } + + public TakeoffStatusEnum getStatus() { + return status; + } + + public TakeoffToPointProgress setStatus(TakeoffStatusEnum status) { + this.status = status; + return this; + } + + public String getFlightId() { + return flightId; + } + + public TakeoffToPointProgress setFlightId(String flightId) { + this.flightId = flightId; + return this; + } + + public String getTrackId() { + return trackId; + } + + public TakeoffToPointProgress setTrackId(String trackId) { + this.trackId = trackId; + return this; + } + + public Integer getWayPointIndex() { + return wayPointIndex; + } + + public TakeoffToPointProgress setWayPointIndex(Integer wayPointIndex) { + this.wayPointIndex = wayPointIndex; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointRequest.java b/src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointRequest.java new file mode 100644 index 0000000..2c5424e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointRequest.java @@ -0,0 +1,218 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.cloudapi.device.ExitWaylineWhenRcLostEnum; +import com.dji.sdk.cloudapi.device.RcLostActionEnum; +import com.dji.sdk.cloudapi.wayline.RthModeEnum; +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.common.CloudSDKVersionEnum; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public class TakeoffToPointRequest extends BaseModel { + + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @NotNull + private String flightId; + + @Min(-180) + @Max(180) + @NotNull + private Float targetLongitude; + + @Min(-90) + @Max(90) + @NotNull + private Float targetLatitude; + + @Min(2) + @Max(10000) + @NotNull + private Float targetHeight; + + @Min(2) + @Max(1500) + @NotNull + private Float securityTakeoffHeight; + + @Min(2) + @Max(1500) + @NotNull + private Float rthAltitude; + + @NotNull + private RcLostActionEnum rcLostAction; + + @NotNull + private ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost; + + @Min(1) + @Max(15) + @NotNull + private Integer maxSpeed; + + @CloudSDKVersion(since = CloudSDKVersionEnum.V1_0_0) + @NotNull + private RthModeEnum rthMode; + + @CloudSDKVersion(since = CloudSDKVersionEnum.V1_0_0) + @NotNull + private CommanderModeLostActionEnum commanderModeLostAction; + + @CloudSDKVersion(since = CloudSDKVersionEnum.V1_0_0) + @NotNull + private CommanderFlightModeEnum commanderFlightMode; + + @CloudSDKVersion(since = CloudSDKVersionEnum.V1_0_0) + @NotNull + @Min(2) + @Max(3000) + private Float commanderFlightHeight; + + public TakeoffToPointRequest() { + } + + @Override + public String toString() { + return "TakeoffToPointRequest{" + + "flightId='" + flightId + '\'' + + ", targetLongitude=" + targetLongitude + + ", targetLatitude=" + targetLatitude + + ", targetHeight=" + targetHeight + + ", securityTakeoffHeight=" + securityTakeoffHeight + + ", rthAltitude=" + rthAltitude + + ", rcLostAction=" + rcLostAction + + ", exitWaylineWhenRcLost=" + exitWaylineWhenRcLost + + ", maxSpeed=" + maxSpeed + + ", rthMode=" + rthMode + + ", commanderModeLostAction=" + commanderModeLostAction + + ", commanderFlightMode=" + commanderFlightMode + + ", commanderFlightHeight=" + commanderFlightHeight + + '}'; + } + + public String getFlightId() { + return flightId; + } + + public TakeoffToPointRequest setFlightId(String flightId) { + this.flightId = flightId; + return this; + } + + public Float getTargetLongitude() { + return targetLongitude; + } + + public TakeoffToPointRequest setTargetLongitude(Float targetLongitude) { + this.targetLongitude = targetLongitude; + return this; + } + + public Float getTargetLatitude() { + return targetLatitude; + } + + public TakeoffToPointRequest setTargetLatitude(Float targetLatitude) { + this.targetLatitude = targetLatitude; + return this; + } + + public Float getTargetHeight() { + return targetHeight; + } + + public TakeoffToPointRequest setTargetHeight(Float targetHeight) { + this.targetHeight = targetHeight; + return this; + } + + public Float getSecurityTakeoffHeight() { + return securityTakeoffHeight; + } + + public TakeoffToPointRequest setSecurityTakeoffHeight(Float securityTakeoffHeight) { + this.securityTakeoffHeight = securityTakeoffHeight; + return this; + } + + public Float getRthAltitude() { + return rthAltitude; + } + + public TakeoffToPointRequest setRthAltitude(Float rthAltitude) { + this.rthAltitude = rthAltitude; + return this; + } + + public RcLostActionEnum getRcLostAction() { + return rcLostAction; + } + + public TakeoffToPointRequest setRcLostAction(RcLostActionEnum rcLostAction) { + this.rcLostAction = rcLostAction; + return this; + } + + public ExitWaylineWhenRcLostEnum getExitWaylineWhenRcLost() { + return exitWaylineWhenRcLost; + } + + public TakeoffToPointRequest setExitWaylineWhenRcLost(ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost) { + this.exitWaylineWhenRcLost = exitWaylineWhenRcLost; + return this; + } + + public Integer getMaxSpeed() { + return maxSpeed; + } + + public RthModeEnum getRthMode() { + return rthMode; + } + + public TakeoffToPointRequest setRthMode(RthModeEnum rthMode) { + this.rthMode = rthMode; + return this; + } + + public CommanderModeLostActionEnum getCommanderModeLostAction() { + return commanderModeLostAction; + } + + public TakeoffToPointRequest setCommanderModeLostAction(CommanderModeLostActionEnum commanderModeLostAction) { + this.commanderModeLostAction = commanderModeLostAction; + return this; + } + + public CommanderFlightModeEnum getCommanderFlightMode() { + return commanderFlightMode; + } + + public TakeoffToPointRequest setCommanderFlightMode(CommanderFlightModeEnum commanderFlightMode) { + this.commanderFlightMode = commanderFlightMode; + return this; + } + + public Float getCommanderFlightHeight() { + return commanderFlightHeight; + } + + public TakeoffToPointRequest setCommanderFlightHeight(Float commanderFlightHeight) { + this.commanderFlightHeight = commanderFlightHeight; + return this; + } + + public TakeoffToPointRequest setMaxSpeed(Integer maxSpeed) { + this.maxSpeed = maxSpeed; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/ZoomCameraTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/control/ZoomCameraTypeEnum.java new file mode 100644 index 0000000..fef98dd --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/ZoomCameraTypeEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.control; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/3 + */ +public enum ZoomCameraTypeEnum { + + ZOOM("zoom"), + + IR("ir"); + + private final String type; + + ZoomCameraTypeEnum(String type) { + this.type = type; + } + + @JsonValue + public String getType() { + return type; + } + + @JsonCreator + public static ZoomCameraTypeEnum find(String type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type.equals(type)).findAny() + .orElseThrow(() -> new CloudSDKException(ZoomCameraTypeEnum.class, type)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/control/api/AbstractControlService.java b/src/main/java/com/dji/sdk/cloudapi/control/api/AbstractControlService.java new file mode 100644 index 0000000..8ea9e77 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/control/api/AbstractControlService.java @@ -0,0 +1,399 @@ +package com.dji.sdk.cloudapi.control.api; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.cloudapi.control.*; +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.common.Common; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.GatewayTypeEnum; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.drc.DrcDownPublish; +import com.dji.sdk.mqtt.drc.DrcUpData; +import com.dji.sdk.mqtt.drc.TopicDrcRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.services.ServicesPublish; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; +import java.util.Objects; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public abstract class AbstractControlService { + + @Resource + private ServicesPublish servicesPublish; + + @Resource + private DrcDownPublish drcDownPublish; + + /** + * Event notification of flyto result + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse flyToPointProgress(TopicEventsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("flyToPointProgress not implemented"); + } + + /** + * Event notification of one-key taking off result + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_TAKEOFF_TO_POINT_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse takeoffToPointProgress(TopicEventsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("takeoffToPointProgress not implemented"); + } + + /** + * Notification of DRC link state + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_DRC_STATUS_NOTIFY, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse drcStatusNotify(TopicEventsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("drcStatusNotify not implemented"); + } + + /** + * Reason notification of invalid Joystick control + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_JOYSTICK_INVALID_NOTIFY, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse joystickInvalidNotify(TopicEventsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("joystickInvalidNotify not implemented"); + } + + /** + * Flight control authority grabbing + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flightAuthorityGrab(GatewayManager gateway) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.FLIGHT_AUTHORITY_GRAB.getMethod()); + } + + /** + * Payload control authority grabbing + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse payloadAuthorityGrab(GatewayManager gateway, PayloadAuthorityGrabRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.PAYLOAD_AUTHORITY_GRAB.getMethod(), + request); + } + + /** + * Enter the live flight controls mode + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse drcModeEnter(GatewayManager gateway, DrcModeEnterRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.DRC_MODE_ENTER.getMethod(), + request); + } + + /** + * Exit the live flight controls mode + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse drcModeExit(GatewayManager gateway) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.DRC_MODE_EXIT.getMethod()); + } + + /** + * One-key taking off + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse takeoffToPoint(GatewayManager gateway, TakeoffToPointRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.TAKEOFF_TO_POINT.getMethod(), + request); + } + + /** + * flyto target point + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flyToPoint(GatewayManager gateway, FlyToPointRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.FLY_TO_POINT.getMethod(), + request); + } + + /** + * End the task of flying to target point + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flyToPointStop(GatewayManager gateway) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.FLY_TO_POINT_STOP.getMethod()); + } + + /** + * Payload control - switch the camera mode + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse cameraModeSwitch(GatewayManager gateway, CameraModeSwitchRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.CAMERA_MODE_SWITCH.getMethod(), + request); + } + + /** + * Payload control - take single photo + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse cameraPhotoTake(GatewayManager gateway, CameraPhotoTakeRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.CAMERA_PHOTO_TAKE.getMethod(), + request); + } + + /** + * Payload control - start recording + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse cameraRecordingStart(GatewayManager gateway, CameraRecordingStartRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.CAMERA_RECORDING_START.getMethod(), + request); + } + + /** + * Payload control - stop recording + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse cameraRecordingStop(GatewayManager gateway, CameraRecordingStopRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.CAMERA_RECORDING_STOP.getMethod(), + request); + } + + /** + * Payload control - double tab to become AIM + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse cameraAim(GatewayManager gateway, CameraAimRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.CAMERA_AIM.getMethod(), + request); + } + + /** + * Payload control - zoom + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse cameraFocalLengthSet(GatewayManager gateway, CameraFocalLengthSetRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.CAMERA_FOCAL_LENGTH_SET.getMethod(), + request); + } + + /** + * Payload control - reset the gimbal + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse gimbalReset(GatewayManager gateway, GimbalResetRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.GIMBAL_RESET.getMethod(), + request); + } + + /** + * Payload control + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse payloadControl(GatewayManager gateway, PayloadControlMethodEnum methodEnum, BaseModel request) { + if (Objects.isNull(request) || request.getClass() != methodEnum.getClazz()) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + Common.validateModel(request); + return servicesPublish.publish( + gateway.getGatewaySn(), + methodEnum.getPayloadMethod().getMethod(), + request); + } + + + /** + * DRC-flight control + * @param gateway + * @param request data + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + protected void droneControlDown(GatewayManager gateway, DroneControlRequest request) { + drcDownPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.DRONE_CONTROL.getMethod(), + request); + } + + /** + * Drc up notification of drone control result + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_DRC_UP_DRONE_CONTROL) + public void droneControlUp(TopicDrcRequest> request, MessageHeaders headers) { + throw new UnsupportedOperationException("droneControlUp not implemented"); + } + + /** + * DRC-drone emergency stop + * @param gateway + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public void droneEmergencyStopDown(GatewayManager gateway) { + drcDownPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.DRONE_EMERGENCY_STOP.getMethod()); + } + + /** + * Drc up notification of drone emergency stop result + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_DRC_UP_DRONE_EMERGENCY_STOP) + public void droneEmergencyStopUp(TopicDrcRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("droneEmergencyStopUp not implemented"); + } + + + /** + * DRC-heart beat + * @param gateway + * @param request data + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public void heartBeatDown(GatewayManager gateway, HeartBeatRequest request) { + drcDownPublish.publish( + gateway.getGatewaySn(), + ControlMethodEnum.HEART_BEAT.getMethod(), + request); + } + + /** + * Drc up notification of heart beat result + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_DRC_UP_HEART_BEAT) + public void heartBeatUp(TopicDrcRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("heartBeatUp not implemented"); + } + + /** + * DRC-obstacle avoidance information pushing + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_DRC_UP_HSI_INFO_PUSH) + public void hsiInfoPush(TopicDrcRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("hsiInfoPush not implemented"); + } + + /** + * DRC-delay information pushing of image transmission link + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_DRC_UP_DELAY_INFO_PUSH) + public void delayInfoPush(TopicDrcRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("delayInfoPush not implemented"); + } + + /** + * DRC-high frequency osd information pushing + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_DRC_UP_OSD_INFO_PUSH) + public void osdInfoPush(TopicDrcRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("osdInfoPush not implemented"); + } + + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/AirConditionerModeSwitchActionEnum.java b/src/main/java/com/dji/sdk/cloudapi/debug/AirConditionerModeSwitchActionEnum.java new file mode 100644 index 0000000..f35ce91 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/AirConditionerModeSwitchActionEnum.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/10/28 + */ +public enum AirConditionerModeSwitchActionEnum { + + IDLE_MODE(0), + + COOLING_MODE(1), + + heating_mode(2), + + DEHUMIDIFICATION_MODE(3); + + private final int action; + + AirConditionerModeSwitchActionEnum(int action) { + this.action = action; + } + + @JsonValue + public int getAction() { + return action; + } + + @JsonCreator + public static AirConditionerModeSwitchActionEnum find(int action) { + return Arrays.stream(values()).filter(actionEnum -> actionEnum.action == action).findAny() + .orElseThrow(() -> new CloudSDKException(AirConditionerModeSwitchActionEnum.class, action)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/AirConditionerModeSwitchRequest.java b/src/main/java/com/dji/sdk/cloudapi/debug/AirConditionerModeSwitchRequest.java new file mode 100644 index 0000000..78698bc --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/AirConditionerModeSwitchRequest.java @@ -0,0 +1,35 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/25 + */ +public class AirConditionerModeSwitchRequest extends BaseModel { + + @NotNull + private AirConditionerModeSwitchActionEnum action; + + public AirConditionerModeSwitchRequest() { + } + + @Override + public String toString() { + return "AirConditionerModeSwitchRequest{" + + "action=" + action + + '}'; + } + + public AirConditionerModeSwitchActionEnum getAction() { + return action; + } + + public AirConditionerModeSwitchRequest setAction(AirConditionerModeSwitchActionEnum action) { + this.action = action; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/AlarmStateSwitchRequest.java b/src/main/java/com/dji/sdk/cloudapi/debug/AlarmStateSwitchRequest.java new file mode 100644 index 0000000..0f7c584 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/AlarmStateSwitchRequest.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.cloudapi.device.SwitchActionEnum; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/25 + */ +public class AlarmStateSwitchRequest extends BaseModel { + + @NotNull + private SwitchActionEnum action; + + public AlarmStateSwitchRequest() { + } + + @Override + public String toString() { + return "AlarmStateSwitchRequest{" + + "action=" + action + + '}'; + } + + public SwitchActionEnum getAction() { + return action; + } + + public AlarmStateSwitchRequest setAction(SwitchActionEnum action) { + this.action = action; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/BatteryMaintenanceSwitchRequest.java b/src/main/java/com/dji/sdk/cloudapi/debug/BatteryMaintenanceSwitchRequest.java new file mode 100644 index 0000000..c25f1ca --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/BatteryMaintenanceSwitchRequest.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.cloudapi.device.SwitchActionEnum; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/25 + */ +public class BatteryMaintenanceSwitchRequest extends BaseModel { + + @NotNull + private SwitchActionEnum action; + + public BatteryMaintenanceSwitchRequest() { + } + + @Override + public String toString() { + return "BatteryMaintenanceSwitchRequest{" + + "action=" + action + + '}'; + } + + public SwitchActionEnum getAction() { + return action; + } + + public BatteryMaintenanceSwitchRequest setAction(SwitchActionEnum action) { + this.action = action; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/BatteryStoreModeSwitchRequest.java b/src/main/java/com/dji/sdk/cloudapi/debug/BatteryStoreModeSwitchRequest.java new file mode 100644 index 0000000..974bbcc --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/BatteryStoreModeSwitchRequest.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.cloudapi.device.BatteryStoreModeEnum; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/25 + */ +public class BatteryStoreModeSwitchRequest extends BaseModel { + + @NotNull + private BatteryStoreModeEnum action; + + public BatteryStoreModeSwitchRequest() { + } + + @Override + public String toString() { + return "BatteryStoreModeSwitchRequest{" + + "action=" + action + + '}'; + } + + public BatteryStoreModeEnum getAction() { + return action; + } + + public BatteryStoreModeSwitchRequest setAction(BatteryStoreModeEnum action) { + this.action = action; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/DebugErrorCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/debug/DebugErrorCodeEnum.java new file mode 100644 index 0000000..982de18 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/DebugErrorCodeEnum.java @@ -0,0 +1,168 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.mqtt.events.IEventsErrorCode; +import com.dji.sdk.mqtt.services.IServicesErrorCode; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public enum DebugErrorCodeEnum implements IServicesErrorCode, IEventsErrorCode, IErrorInfo { + + AIRCRAFT_NO_DONGLE(326002, "The DJI Cellular module is not installed on the aircraft."), + + AIRCRAFT_DONGLE_NO_SIM(326003, "There is no SIM card installed in the DJI Cellular module of the aircraft."), + + AIRCRAFT_DONGLE_NEED_UPGRADE(326004, "The DJI Cellular module of the aircraft needs to be upgraded, otherwise it cannot be used."), + + ESTABLISH_CONNECTION_FAILED(326005, "The 4G transmission of the aircraft fails to be enabled, and the 4G transmission cannot establish a connection. Please check the 4G signal strength, or consult the operator to check the package traffic and APN settings."), + + SDR_SWITCH_FAILED(326006, "The 4G transmission switch failed, please try again later."), + + WRONG_COMMAND_FORMAT(326007, "The command format is wrong."), + + DOCK_NO_DONGLE(326008, "The DJI Cellular module is not installed on the dock."), + + DOCK_DONGLE_NO_SIM(326009, "There is no SIM card installed in the DJI Cellular module of the dock."), + + DOCK_DONGLE_NEED_UPGRADE(326010, "The DJI Cellular module of the dock needs to be upgraded, otherwise it cannot be used."), + + COMMAND_NOT_SUPPORTED(514100, "Dock error. Restart dock and try again."), + + PUSH_DRIVING_RODS_FAILED(514101, "Failed to push driving rods into place."), + + PULL_DRIVING_RODS_FAILED(514102, "Failed to pull driving rods back."), + + LOW_POWER_1(514103, "Aircraft battery level low. Unable to perform task. Wait until aircraft is charged up to 50% and try again."), + + CHARGE_FAILED(514104, "Failed to charge battery."), + + STOP_CHARGING_FAILED(514105, "Failed to stop charging battery."), + + REBOOT_DRONE_FAILED(514106, "Failed to reboot drone."), + + OPEN_DOCK_COVER_FAILED(514107, "Failed to open dock cover."), + + CLOSE_DOCK_COVER_FAILED(514108, "Failed to close dock cover."), + + POWER_ON_AIRCRAFT_FAILED(514109, "Failed to power on aircraft."), + + POWER_OFF_AIRCRAFT_FAILED(514110, "Failed to power off aircraft."), + + OPEN_SLOW_MOTION_FAILED(514111, "Propeller error in opening slow motion mode"), + + CLOSE_SLOW_MOTION_FAILED(514112, "Propeller error in closing slow motion mode"), + + AIRCRAFT_NOT_FOUND_1(514113, "Connection error between driving rod and aircraft. Check if aircraft is inside dock, driving rods are stuck, or charging connector is stained or damaged."), + + OBTAIN_BATTERY_FAILED(514114, "Failed to obtain aircraft battery status. Restart dock and try again."), + + DOCK_BUSY(514116, "Unable to perform operation. Dock is executing other command. Try again later."), + + OBTAIN_DOCK_COVER_FAILED(514117, "Dock cover is open or not fully closed. Restart dock and try again"), + + OBTAIN_DRIVING_RODS_FAILED(514118, "Driving rods pulled back or not pushed into place. Restart dock and try again."), + + TRANSMISSION_ERROR(514120, "Dock and aircraft disconnected. Restart dock and try again or relink dock and aircraft."), + + EMERGENCY_BUTTON_PRESSED_DOWN(514121, "Emergency stop button pressed down. Release button."), + + OBTAIN_CHARGING_STATUS_FAILED(514122, "Failed to obtain aircraft charging status. Restart dock and try again."), + + LOW_POWER_2(514123, "Aircraft battery level too low. Unable to power on aircraft."), + + OBTAIN_BATTERY_STATUS_FAILED(514124, "Failed to obtain aircraft battery information."), + + BATTERY_FULL(514125, "Aircraft battery level almost full. Unable to start charging. Charge battery when battery level is lower than 95%."), + + HEAVY_RAINFALL(514134, "Heavy rainfall. Unable to perform task. Try again later."), + + HIGH_WIND(514135, "Wind speed too high (≥12 m/s). Unable to perform task. Try again later."), + + POWER_SUPPLY_ERROR(514136, "Dock power supply error. Unable to perform task. Resume power supply and try again."), + + LOW_ENVIRONMENT_TEMPERATURE(514137, "Environment temperature too low (lower than -20° C). Unable to perform task. Try again later."), + + BATTERY_MAINTAINING(514138, "Maintaining aircraft battery. Unable to perform task. Wait until maintenance is complete."), + + MAINTAIN_BATTERY_FAILED(514139, "Failed to maintain aircraft battery. No maintenance required."), + + SETTING_BATTERY_STORAGE_FAILED(514140, "Failed to set battery storage mode."), + + DOCK_SYSTEM_ERROR(514141, "Dock system error. Restart dock and try again."), + + AIRCRAFT_NOT_FOUND_2(514142, "Connection error between driving rod and aircraft before takeoff. Check if aircraft is inside dock, driving rods are stuck, or charging connector is stained or damaged."), + + DRIVING_RODS_ERROR(514143, "Driving rods pulled back or not pushed into place. Try again later or restart dock and try again."), + + DOCK_COVER_ERROR(514144, "Dock cover is open or not fully closed."), + + ONSITE_DEBUGGING_MODE(514145, "Dock in onsite debugging mode. Unable to perform current operation or task."), + + REMOTE_DEBUGGING_MODE(514146, "Dock in remote debugging mode. Unable to perform task."), + + FIRMWARE_UPDATING(514147, "Updating device firmware. Unable to perform task."), + + WORKING(514148, "Task in progress. Dock unable to enter remote debugging mode or perform task again. "), + + WRONG_STATUS(514149, "The airport is not in operation mode, but an operation mode-related command has been issued."), + + RESTARTING(514150, "Restarting device."), + + UPDATING(514151, "Updating device firmware."), + + NOT_REMOTE_DEBUGGING_MODE(514153, "Dock exited remote debugging mode. Unable to perform current operation."), + + INITIALIZING(514170, "Initializing dock. Unable to perform operation. Wait until initialization completes."), + + WRONG_PARAMETER(514171, "Cloud command parameter error. Dock unable to execute command."), + + DISABLE_AC_FAILED(514180, "Failed to disable AC cooling or heating."), + + ENABLE_AC_COOLING_FAILED(514181, "Failed to enable AC cooling."), + + ENABLE_AC_HEATING_FAILED(514182, "Failed to enable AC heating."), + + ENABLE_AC_DEHUMIDIFYING_FAILED(514183, "Failed to enable AC dehumidifying."), + + LOW_TEMPERATURE(514184, "Ambient temperature below 0° C. Unable to enable AC cooling."), + + HIGH_TEMPERATURE(514185, "Ambient temperature above 45° C. Unable to enable AC heating"), + + UNKNOWN(-1, "UNKNOWN"), + + ; + + + private final String msg; + + private final int code; + + DebugErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String getMessage() { + return this.msg; + } + + @Override + public Integer getCode() { + return this.code; + } + + /** + * @param code error code + * @return enumeration object + */ + public static DebugErrorCodeEnum find(int code) { + return Arrays.stream(values()).filter(codeEnum -> codeEnum.code == code).findAny().orElse(UNKNOWN); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/DebugMethodEnum.java b/src/main/java/com/dji/sdk/cloudapi/debug/DebugMethodEnum.java new file mode 100644 index 0000000..ec69c0f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/DebugMethodEnum.java @@ -0,0 +1,76 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +public enum DebugMethodEnum { + + DEBUG_MODE_OPEN("debug_mode_open", null), + + DEBUG_MODE_CLOSE("debug_mode_close", null), + + SUPPLEMENT_LIGHT_OPEN("supplement_light_open", null), + + SUPPLEMENT_LIGHT_CLOSE("supplement_light_close", null), + + DEVICE_REBOOT("device_reboot", null), + + DRONE_OPEN("drone_open", null), + + DRONE_CLOSE("drone_close", null), + + DRONE_FORMAT("drone_format", null), + + DEVICE_FORMAT("device_format", null), + + COVER_OPEN("cover_open", null), + + COVER_CLOSE("cover_close", null), + + PUTTER_OPEN("putter_open", null), + + PUTTER_CLOSE("putter_close", null), + + CHARGE_OPEN("charge_open", null), + + CHARGE_CLOSE("charge_close", null), + + BATTERY_MAINTENANCE_SWITCH("battery_maintenance_switch", BatteryMaintenanceSwitchRequest.class), + + ALARM_STATE_SWITCH("alarm_state_switch", AlarmStateSwitchRequest.class), + + BATTERY_STORE_MODE_SWITCH("battery_store_mode_switch", BatteryStoreModeSwitchRequest.class), + + SDR_WORKMODE_SWITCH("sdr_workmode_switch", SdrWorkmodeSwitchRequest.class), + + AIR_CONDITIONER_MODE_SWITCH("air_conditioner_mode_switch", AirConditionerModeSwitchRequest.class); + + private final String method; + + private final Class clazz; + + DebugMethodEnum(String method, Class clazz) { + this.method = method; + this.clazz = clazz; + } + + public String getMethod() { + return method; + } + + public Class getClazz() { + return clazz; + } + + public static DebugMethodEnum find(String method) { + return Arrays.stream(values()).filter(methodEnum -> methodEnum.method.equals(method)).findAny() + .orElseThrow(() -> new CloudSDKException(DebugMethodEnum.class, method)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugProgress.java b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugProgress.java new file mode 100644 index 0000000..f5a1b75 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugProgress.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.debug; + +/** + * @author sean + * @version 1.2 + * @date 2022/7/29 + */ +public class RemoteDebugProgress { + + private RemoteDebugStatusEnum status; + + private RemoteDebugProgressData progress; + + public RemoteDebugProgress() { + } + + @Override + public String toString() { + return "RemoteDebugProgress{" + + "status=" + status + + ", progress=" + progress + + '}'; + } + + public RemoteDebugStatusEnum getStatus() { + return status; + } + + public RemoteDebugProgress setStatus(RemoteDebugStatusEnum status) { + this.status = status; + return this; + } + + public RemoteDebugProgressData getProgress() { + return progress; + } + + public RemoteDebugProgress setProgress(RemoteDebugProgressData progress) { + this.progress = progress; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugProgressData.java b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugProgressData.java new file mode 100644 index 0000000..c512c8f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugProgressData.java @@ -0,0 +1,78 @@ +package com.dji.sdk.cloudapi.debug; + +/** + * @author sean + * @version 1.2 + * @date 2022/7/29 + */ +public class RemoteDebugProgressData { + + private Integer percent; + + private Integer currentStep; + + private Integer totalSteps; + + private RemoteDebugStepKeyEnum stepKey; + + private Integer stepResult; + + public RemoteDebugProgressData() { + } + + @Override + public String toString() { + return "RemoteDebugProgressData{" + + "percent=" + percent + + ", currentStep=" + currentStep + + ", totalSteps=" + totalSteps + + ", stepKey='" + stepKey + '\'' + + ", stepResult=" + stepResult + + '}'; + } + + public Integer getPercent() { + return percent; + } + + public RemoteDebugProgressData setPercent(Integer percent) { + this.percent = percent; + return this; + } + + public Integer getCurrentStep() { + return currentStep; + } + + public RemoteDebugProgressData setCurrentStep(Integer currentStep) { + this.currentStep = currentStep; + return this; + } + + public Integer getTotalSteps() { + return totalSteps; + } + + public RemoteDebugProgressData setTotalSteps(Integer totalSteps) { + this.totalSteps = totalSteps; + return this; + } + + public RemoteDebugStepKeyEnum getStepKey() { + return stepKey; + } + + public RemoteDebugProgressData setStepKey(RemoteDebugStepKeyEnum stepKey) { + this.stepKey = stepKey; + return this; + } + + public Integer getStepResult() { + return stepResult; + } + + public RemoteDebugProgressData setStepResult(Integer stepResult) { + this.stepResult = stepResult; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugResponse.java b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugResponse.java new file mode 100644 index 0000000..5e2f371 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugResponse.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.debug; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class RemoteDebugResponse { + + private RemoteDebugStatusEnum status; + + public RemoteDebugResponse() { + } + + @Override + public String toString() { + return "RemoteDebugResponse{" + + "status=" + status + + '}'; + } + + public RemoteDebugStatusEnum getStatus() { + return status; + } + + public RemoteDebugResponse setStatus(RemoteDebugStatusEnum status) { + this.status = status; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStatusEnum.java b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStatusEnum.java new file mode 100644 index 0000000..164d6af --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStatusEnum.java @@ -0,0 +1,57 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.2 + * @date 2022/8/17 + */ +public enum RemoteDebugStatusEnum { + + SENT("sent", false), + + IN_PROGRESS("in_progress", false), + + OK("ok", true), + + PAUSED("paused", false), + + REJECTED("rejected", true), + + FAILED("failed", true), + + CANCELED("canceled", true), + + TIMEOUT("timeout", true); + + private final String status; + + private final boolean end; + + RemoteDebugStatusEnum(String status, boolean end) { + this.status = status; + this.end = end; + } + + @JsonValue + public String getStatus() { + return status; + } + + public boolean isEnd() { + return end; + } + + @JsonCreator + public static RemoteDebugStatusEnum find(String status) { + return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny() + .orElseThrow(() -> new CloudSDKException(RemoteDebugStatusEnum.class, status)); + } +} + + diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStepKeyEnum.java b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStepKeyEnum.java new file mode 100644 index 0000000..ddeca50 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStepKeyEnum.java @@ -0,0 +1,88 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public enum RemoteDebugStepKeyEnum { + + GET_BID("get_bid", "Get bid"), + + UPGRADING_PREVENT_REBOOT("upgrading_prevent_reboot", "Check if the device is being updated"), + + CHECK_WORK_MODE("check_work_mode", "Check whether to enter remote debugging mode"), + + CHECK_TASK_STATE("check_task_state", "Check if the DJI Dock is free"), + + LAND_MCU_REBOOT("land_mcu_reboot", "Land MCU reboot"), + + RAIN_MCU_REBOOT("rain_mcu_reboot", "Weather station MCU reboot"), + + CORE_MCU_REBOOT("core_mcu_reboot", "Central control MCU reboot"), + + SDR_REBOOT("sdr_reboot", "SDR reboot"), + + WRITE_REBOOT_PARAM_FILE("write_reboot_param_file", "Write reboot flag"), + + GET_DRONE_POWER_STATE("get_drone_power_state", "Get battery charge state"), + + CLOSE_PUTTER("close_putter", "Close the putter"), + + CHECK_WIRED_CONNECT_STATE("check_wired_connect_state", "Get aircraft state"), + + OPEN_DRONE("open_drone", "Open the plane"), + + OPEN_ALARM("open_alarm", "Open sound and light alarm"), + + CHECK_SCRAM_STATE("check_scram_state", "Check if the emergency stop switch is pressed"), + + OPEN_COVER("open_cover", "Open the hatch"), + + CHECK_DRONE_SDR_CONNECT_STATE("check_drone_sdr_connect_state", "Establish SDR wireless connection"), + + TURN_ON_DRONE("turn_on_drone", "Turn the plane on"), + + DRONE_PADDLE_FORWARD("drone_paddle_forward", "Turn on forward paddle"), + + CLOSE_COVER("close_cover", "Close the hatch"), + + DRONE_PADDLE_REVERSE("drone_paddle_reverse", "Turn on reverse paddle"), + + DRONE_PADDLE_STOP("drone_paddle_stop", "Stop Paddle Rotation"), + + FREE_PUTTER("free_putter", "Free Putter"), + + STOP_CHARGE("stop_charge", "Stop charging"); + + private final String stepKey; + + private final String message; + + RemoteDebugStepKeyEnum(String stepKey, String message) { + this.stepKey = stepKey; + this.message = message; + } + + @JsonValue + public String getStepKey() { + return stepKey; + } + + public String getMessage() { + return message; + } + + @JsonCreator + public static RemoteDebugStepKeyEnum find(String stepKey) { + return Arrays.stream(values()).filter(stepKeyEnum -> stepKeyEnum.stepKey.equals(stepKey)).findAny() + .orElseThrow(() -> new CloudSDKException(RemoteDebugStepKeyEnum.class,stepKey)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/SdrWorkmodeSwitchRequest.java b/src/main/java/com/dji/sdk/cloudapi/debug/SdrWorkmodeSwitchRequest.java new file mode 100644 index 0000000..2298a28 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/SdrWorkmodeSwitchRequest.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.debug; + +import com.dji.sdk.cloudapi.device.LinkWorkModeEnum; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/25 + */ +public class SdrWorkmodeSwitchRequest extends BaseModel { + + @NotNull + private LinkWorkModeEnum linkWorkmode; + + public SdrWorkmodeSwitchRequest() { + } + + @Override + public String toString() { + return "SdrWorkmodeSwitchRequest{" + + "linkWorkmode=" + linkWorkmode + + '}'; + } + + public LinkWorkModeEnum getLinkWorkmode() { + return linkWorkmode; + } + + public SdrWorkmodeSwitchRequest setLinkWorkmode(LinkWorkModeEnum linkWorkmode) { + this.linkWorkmode = linkWorkmode; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/debug/api/AbstractDebugService.java b/src/main/java/com/dji/sdk/cloudapi/debug/api/AbstractDebugService.java new file mode 100644 index 0000000..1d3e6a1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/debug/api/AbstractDebugService.java @@ -0,0 +1,340 @@ +package com.dji.sdk.cloudapi.debug.api; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.cloudapi.debug.*; +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.common.Common; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.GatewayTypeEnum; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.services.ServicesPublish; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; +import java.util.Objects; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public abstract class AbstractDebugService { + + @Resource + private ServicesPublish servicesPublish; + + /** + * Open the debug mode + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> debugModeOpen(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.DEBUG_MODE_OPEN.getMethod()); + } + + /** + * Close the debug mode + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> debugModeClose(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.DEBUG_MODE_CLOSE.getMethod()); + } + + /** + * Open the supplement light + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> supplementLightOpen(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.SUPPLEMENT_LIGHT_OPEN.getMethod()); + } + + /** + * Close the supplement light + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> supplementLightClose(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.SUPPLEMENT_LIGHT_CLOSE.getMethod()); + } + + /** + * Maintenance state switch of battery + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> batteryMaintenanceSwitch(GatewayManager gateway, BatteryMaintenanceSwitchRequest request) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.BATTERY_MAINTENANCE_SWITCH.getMethod(), + request); + } + + /** + * Air conditioner working mode switch of dock + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> airConditionerModeSwitch(GatewayManager gateway, AirConditionerModeSwitchRequest request) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.AIR_CONDITIONER_MODE_SWITCH.getMethod(), + request); + } + + /** + * Sound and light alarm switch of dock + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> alarmStateSwitch(GatewayManager gateway, AlarmStateSwitchRequest request) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.AIR_CONDITIONER_MODE_SWITCH.getMethod(), + request); + } + + /** + * Battery storage mode switch of dock + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> batteryStoreModeSwitch(GatewayManager gateway, BatteryStoreModeSwitchRequest request) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.BATTERY_STORE_MODE_SWITCH.getMethod(), + request); + } + + /** + * Reboot the dock + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> deviceReboot(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.DEVICE_REBOOT.getMethod()); + } + + /** + * Power on the aircraft + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> droneOpen(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.DRONE_OPEN.getMethod()); + } + + /** + * Power off the aircraft + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> droneClose(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.DRONE_CLOSE.getMethod()); + } + + /** + * Format the dock data + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> deviceFormat(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.DEVICE_FORMAT.getMethod()); + } + + /** + * Format the aircraft data + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> droneFormat(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.DRONE_FORMAT.getMethod()); + } + + /** + * Open the dock cover + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> coverOpen(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.COVER_OPEN.getMethod()); + } + + /** + * Close the dock cover + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> coverClose(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.COVER_CLOSE.getMethod()); + } + + /** + * Open the putter + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> putterOpen(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.PUTTER_OPEN.getMethod()); + } + + /** + * Close the putter + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> putterClose(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.PUTTER_CLOSE.getMethod()); + } + + /** + * Turn on charging + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> chargeOpen(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.CHARGE_OPEN.getMethod()); + } + + /** + * Turn off charging + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> chargeClose(GatewayManager gateway) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.CHARGE_CLOSE.getMethod()); + } + + /** + * Switch of 4G enhancement mode + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> sdrWorkmodeSwitch(GatewayManager gateway, SdrWorkmodeSwitchRequest request) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + DebugMethodEnum.SDR_WORKMODE_SWITCH.getMethod(), + request); + } + + /** + * Common interface for remote debugging + * @param gateway + * @param methodEnum + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse> remoteDebug(GatewayManager gateway, DebugMethodEnum methodEnum, BaseModel request) { + if (Objects.nonNull(methodEnum.getClazz())) { + if (methodEnum.getClazz() != request.getClass()) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + Common.validateModel(request); + } + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + methodEnum.getMethod(), + request); + } + + /** + * Inform of remote debug progress + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse remoteDebugProgress(TopicEventsRequest> request, MessageHeaders headers) { + throw new UnsupportedOperationException("remoteDebugProgress not implemented"); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/AirConditioner.java b/src/main/java/com/dji/sdk/cloudapi/device/AirConditioner.java new file mode 100644 index 0000000..7be54b4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/AirConditioner.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class AirConditioner { + + private AirConditionerStateEnum airConditionerState; + + private Integer switchTime; + + public AirConditioner() { + } + + @Override + public String toString() { + return "AirConditioner{" + + "airConditionerState=" + airConditionerState + + ", switchTime=" + switchTime + + '}'; + } + + public AirConditionerStateEnum getAirConditionerState() { + return airConditionerState; + } + + public AirConditioner setAirConditionerState(AirConditionerStateEnum airConditionerState) { + this.airConditionerState = airConditionerState; + return this; + } + + public Integer getSwitchTime() { + return switchTime; + } + + public AirConditioner setSwitchTime(Integer switchTime) { + this.switchTime = switchTime; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/AirConditionerStateEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/AirConditionerStateEnum.java new file mode 100644 index 0000000..f58728f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/AirConditionerStateEnum.java @@ -0,0 +1,56 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum AirConditionerStateEnum { + + IDLE(0), + + COOL(1), + + HEAT(2), + + DEHUMIDIFICATION(3), + + COOLING_EXIT(4), + + HEATING_EXIT(5), + + DEHUMIDIFICATION_EXIT(6), + + COOLING_PREPARATION(7), + + HEATING_PREPARATION(8), + + DEHUMIDIFICATION_PREPARATION(9), + + DISCONNECTED(32767), + ; + + private final int state; + + AirConditionerStateEnum(int state) { + this.state = state; + } + + @JsonValue + public int getState() { + return state; + } + + @JsonCreator + public static AirConditionerStateEnum find(int state) { + return Arrays.stream(values()).filter(stateEnum -> stateEnum.state == state).findAny() + .orElseThrow(() -> new CloudSDKException(AirConditionerStateEnum.class, state)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/AlternateLandPoint.java b/src/main/java/com/dji/sdk/cloudapi/device/AlternateLandPoint.java new file mode 100644 index 0000000..8b1e144 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/AlternateLandPoint.java @@ -0,0 +1,69 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +public class AlternateLandPoint { + + private Float latitude; + + private Float longitude; + + private Float safeLandHeight; + + @JsonProperty("is_configured") + private Boolean configured; + + public AlternateLandPoint() { + } + + @Override + public String toString() { + return "AlternateLandPoint{" + + "latitude=" + latitude + + ", longitude=" + longitude + + ", safeLandHeight=" + safeLandHeight + + ", configured=" + configured + + '}'; + } + + public Float getLatitude() { + return latitude; + } + + public AlternateLandPoint setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public AlternateLandPoint setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public Float getSafeLandHeight() { + return safeLandHeight; + } + + public AlternateLandPoint setSafeLandHeight(Float safeLandHeight) { + this.safeLandHeight = safeLandHeight; + return this; + } + + public Boolean getConfigured() { + return configured; + } + + public AlternateLandPoint setConfigured(Boolean configured) { + this.configured = configured; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/BackupBattery.java b/src/main/java/com/dji/sdk/cloudapi/device/BackupBattery.java new file mode 100644 index 0000000..0b3e753 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/BackupBattery.java @@ -0,0 +1,57 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/3 + */ +public class BackupBattery { + + private Integer voltage; + + private Float temperature; + + @JsonProperty("switch") + private Boolean batterySwitch; + + public BackupBattery() { + } + + @Override + public String toString() { + return "BackupBattery{" + + "voltage=" + voltage + + ", temperature=" + temperature + + ", batterySwitch=" + batterySwitch + + '}'; + } + + public Integer getVoltage() { + return voltage; + } + + public BackupBattery setVoltage(Integer voltage) { + this.voltage = voltage; + return this; + } + + public Float getTemperature() { + return temperature; + } + + public BackupBattery setTemperature(Float temperature) { + this.temperature = temperature; + return this; + } + + public Boolean getBatterySwitch() { + return batterySwitch; + } + + public BackupBattery setBatterySwitch(Boolean batterySwitch) { + this.batterySwitch = batterySwitch; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/Battery.java b/src/main/java/com/dji/sdk/cloudapi/device/Battery.java new file mode 100644 index 0000000..15e51bd --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/Battery.java @@ -0,0 +1,138 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/24 + */ +public class Battery { + + private String firmwareVersion; + + private BatteryIndexEnum index; + + private Integer loopTimes; + + private Integer capacityPercent; + + private String sn; + + private Integer subType; + + private Float temperature; + + private Integer type; + + private Integer voltage; + + private Integer highVoltageStorageDays; + + public Battery() { + } + + @Override + public String toString() { + return "Battery{" + + "firmwareVersion='" + firmwareVersion + '\'' + + ", index=" + index + + ", loopTimes=" + loopTimes + + ", capacityPercent=" + capacityPercent + + ", sn='" + sn + '\'' + + ", subType=" + subType + + ", temperature=" + temperature + + ", type=" + type + + ", voltage=" + voltage + + ", highVoltageStorageDays=" + highVoltageStorageDays + + '}'; + } + + public String getFirmwareVersion() { + return firmwareVersion; + } + + public Battery setFirmwareVersion(String firmwareVersion) { + this.firmwareVersion = firmwareVersion; + return this; + } + + public BatteryIndexEnum getIndex() { + return index; + } + + public Battery setIndex(BatteryIndexEnum index) { + this.index = index; + return this; + } + + public Integer getLoopTimes() { + return loopTimes; + } + + public Battery setLoopTimes(Integer loopTimes) { + this.loopTimes = loopTimes; + return this; + } + + public Integer getCapacityPercent() { + return capacityPercent; + } + + public Battery setCapacityPercent(Integer capacityPercent) { + this.capacityPercent = capacityPercent; + return this; + } + + public String getSn() { + return sn; + } + + public Battery setSn(String sn) { + this.sn = sn; + return this; + } + + public Integer getSubType() { + return subType; + } + + public Battery setSubType(Integer subType) { + this.subType = subType; + return this; + } + + public Float getTemperature() { + return temperature; + } + + public Battery setTemperature(Float temperature) { + this.temperature = temperature; + return this; + } + + public Integer getType() { + return type; + } + + public Battery setType(Integer type) { + this.type = type; + return this; + } + + public Integer getVoltage() { + return voltage; + } + + public Battery setVoltage(Integer voltage) { + this.voltage = voltage; + return this; + } + + public Integer getHighVoltageStorageDays() { + return highVoltageStorageDays; + } + + public Battery setHighVoltageStorageDays(Integer highVoltageStorageDays) { + this.highVoltageStorageDays = highVoltageStorageDays; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/BatteryIndexEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/BatteryIndexEnum.java new file mode 100644 index 0000000..073c20f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/BatteryIndexEnum.java @@ -0,0 +1,37 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum BatteryIndexEnum { + + LEFT(0), + + RIGHT(1); + + private final int index; + + BatteryIndexEnum(int index) { + this.index = index; + } + + @JsonValue + public int getIndex() { + return index; + } + + @JsonCreator + public static BatteryIndexEnum find(int index) { + return Arrays.stream(values()).filter(indexEnum -> indexEnum.index == index).findAny() + .orElseThrow(() -> new CloudSDKException(BatteryIndexEnum.class, index)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/BatteryStoreModeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/BatteryStoreModeEnum.java new file mode 100644 index 0000000..f60590b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/BatteryStoreModeEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +public enum BatteryStoreModeEnum { + + PLAN(1), + + EMERGENCY(2); + + private final int mode; + + BatteryStoreModeEnum(int mode) { + this.mode = mode; + } + + @JsonValue + public int getMode() { + return mode; + } + + @JsonCreator + public static BatteryStoreModeEnum find(int mode) { + return Arrays.stream(BatteryStoreModeEnum.values()).filter(modeEnum -> modeEnum.mode == mode).findAny() + .orElseThrow(() -> new CloudSDKException(BatteryStoreModeEnum.class, mode)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/CameraModeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/CameraModeEnum.java new file mode 100644 index 0000000..3f82d43 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/CameraModeEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/3 + */ +public enum CameraModeEnum { + + PHOTO(0), + + VIDEO(1); + + private final int mode; + + CameraModeEnum(int mode) { + this.mode = mode; + } + + @JsonValue + public int getMode() { + return mode; + } + + @JsonCreator + public static CameraModeEnum find(int mode) { + return Arrays.stream(values()).filter(modeEnum -> modeEnum.mode == mode).findAny() + .orElseThrow(() -> new CloudSDKException(CameraModeEnum.class, mode)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/CameraStateEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/CameraStateEnum.java new file mode 100644 index 0000000..b0adf26 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/CameraStateEnum.java @@ -0,0 +1,37 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/4/23 + */ +public enum CameraStateEnum { + + IDLE(0), + + WORKING(1), + ; + + private final int state; + + CameraStateEnum(int state) { + this.state = state; + } + + @JsonValue + public int getState() { + return state; + } + + @JsonCreator + public static CameraStateEnum find(int state) { + return Arrays.stream(values()).filter(stateEnum -> stateEnum.state == state).findAny() + .orElseThrow(() -> new CloudSDKException(CameraStateEnum.class, state)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/ControlSourceEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/ControlSourceEnum.java new file mode 100644 index 0000000..d2d7793 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/ControlSourceEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/16 + */ +public enum ControlSourceEnum { + + A("A"), + + B("B"), + + UNKNOWN(""); + + private final String controlSource; + + ControlSourceEnum(String controlSource) { + this.controlSource = controlSource; + } + + @JsonValue + public String getControlSource() { + return controlSource; + } + + @JsonCreator + public static ControlSourceEnum find(String controlSource) { + return Arrays.stream(values()).filter(controlSourceEnum -> controlSourceEnum.controlSource.equals(controlSource)).findAny() + .orElseThrow(() -> new CloudSDKException(ControlSourceEnum.class, controlSource)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/CoverStateEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/CoverStateEnum.java new file mode 100644 index 0000000..a7cc62f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/CoverStateEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum CoverStateEnum { + + CLOSED(0), + + OPENED(1), + + HALF_OPEN(2), + + ABNORMAL(3), + ; + + private final int state; + + CoverStateEnum(int state) { + this.state = state; + } + + @JsonValue + public int getState() { + return state; + } + + @JsonCreator + public static CoverStateEnum find(int state) { + return Arrays.stream(values()).filter(stateEnum -> stateEnum.state == state).findAny() + .orElseThrow(() -> new CloudSDKException(CoverStateEnum.class, state)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DeviceDomainEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/DeviceDomainEnum.java new file mode 100644 index 0000000..ff94533 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DeviceDomainEnum.java @@ -0,0 +1,43 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Arrays; + +/** + * + * @author sean.zhou + * @date 2021/11/15 + * @version 0.1 + */ +@Schema(description = "device domain", enumAsRef = true) +public enum DeviceDomainEnum { + + DRONE(0), + + PAYLOAD(1), + + REMOTER_CONTROL(2), + + DOCK (3); + + private final int domain; + + DeviceDomainEnum(int domain) { + this.domain = domain; + } + + @JsonCreator + public static DeviceDomainEnum find(int domain) { + return Arrays.stream(values()).filter(domainEnum -> domainEnum.domain == domain).findAny() + .orElseThrow(() -> new CloudSDKException(DeviceDomainEnum.class, domain)); + } + + @JsonValue + public int getDomain() { + return domain; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DeviceEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/DeviceEnum.java new file mode 100644 index 0000000..0009374 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DeviceEnum.java @@ -0,0 +1,121 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +@Schema(description = "device model key.", format = "domain-type-subType", enumAsRef = true, example = "0-89-0") +public enum DeviceEnum { + + M350(DeviceDomainEnum.DRONE, DeviceTypeEnum.M350, DeviceSubTypeEnum.ZERO), + + M300(DeviceDomainEnum.DRONE, DeviceTypeEnum.M300, DeviceSubTypeEnum.ZERO), + + M30(DeviceDomainEnum.DRONE, DeviceTypeEnum.M30, DeviceSubTypeEnum.ZERO), + + M30T(DeviceDomainEnum.DRONE, DeviceTypeEnum.M30, DeviceSubTypeEnum.ONE), + + M3E(DeviceDomainEnum.DRONE, DeviceTypeEnum.M3E, DeviceSubTypeEnum.ZERO), + + M3T(DeviceDomainEnum.DRONE, DeviceTypeEnum.M3E, DeviceSubTypeEnum.ONE), + + M3M(DeviceDomainEnum.DRONE, DeviceTypeEnum.M3E, DeviceSubTypeEnum.TWO), + + Z30(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.Z30, DeviceSubTypeEnum.ZERO), + + XT2(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.XT2, DeviceSubTypeEnum.ZERO), + + FPV(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.FPV, DeviceSubTypeEnum.ZERO), + + XTS(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.XTS, DeviceSubTypeEnum.ZERO), + + H20(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.H20, DeviceSubTypeEnum.ZERO), + + H20T(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.H20T, DeviceSubTypeEnum.ZERO), + + P1(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.P1, DeviceSubTypeEnum._65535), + + M30_CAMERA(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.M30_CAMERA, DeviceSubTypeEnum.ZERO), + + M30T_CAMERA(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.M30T_CAMERA, DeviceSubTypeEnum.ZERO), + + H20N(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.H20N, DeviceSubTypeEnum.ZERO), + + DOCK_CAMERA(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.DOCK_CAMERA, DeviceSubTypeEnum.ZERO), + + L1(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.L1, DeviceSubTypeEnum.ZERO), + + M3E_CAMERA(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.M3E_CAMERA, DeviceSubTypeEnum.ZERO), + + M3T_CAMERA(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.M3T_CAMERA, DeviceSubTypeEnum.ZERO), + + M3M_CAMERA(DeviceDomainEnum.PAYLOAD, DeviceTypeEnum.M3M_CAMERA, DeviceSubTypeEnum.ZERO), + + RC(DeviceDomainEnum.REMOTER_CONTROL, DeviceTypeEnum.RC, DeviceSubTypeEnum.ZERO), + + RC_PLUS(DeviceDomainEnum.REMOTER_CONTROL, DeviceTypeEnum.RC_PLUS, DeviceSubTypeEnum.ZERO), + + RC_PRO(DeviceDomainEnum.REMOTER_CONTROL, DeviceTypeEnum.RC_PRO, DeviceSubTypeEnum.ZERO), + + DOCK(DeviceDomainEnum.DOCK, DeviceTypeEnum.DOCK, DeviceSubTypeEnum.ZERO), + ; + + @Schema(enumAsRef = true) + private final DeviceDomainEnum domain; + + @Schema(enumAsRef = true) + private final DeviceTypeEnum type; + + @Schema(enumAsRef = true) + private final DeviceSubTypeEnum subType; + + DeviceEnum(DeviceDomainEnum domain, DeviceTypeEnum type, DeviceSubTypeEnum subType) { + this.domain = domain; + this.type = type; + this.subType = subType; + } + + public DeviceDomainEnum getDomain() { + return domain; + } + + public DeviceTypeEnum getType() { + return type; + } + + public DeviceSubTypeEnum getSubType() { + return subType; + } + + @JsonValue + public String getDevice() { + return String.format("%s-%s-%s", domain.getDomain(), type.getType(), subType.getSubType()); + } + + public static DeviceEnum find(DeviceDomainEnum domain, DeviceTypeEnum type, DeviceSubTypeEnum subType) { + return Arrays.stream(values()).filter(device -> device.domain == domain && device.type == type && device.subType == subType) + .findAny().orElseThrow(() -> new CloudSDKException(DeviceEnum.class, + String.format("%s-%s-%s", domain.getDomain(), type.getType(), subType.getSubType()))); + } + + public static DeviceEnum find(int domain, int type, int subType) { + return Arrays.stream(values()).filter(device -> device.domain.getDomain() == domain && + device.type.getType() == type && device.subType.getSubType() == subType) + .findAny().orElseThrow(() -> new CloudSDKException(DeviceEnum.class, + String.format("%s-%s-%s", domain, type, subType))); + } + + @JsonCreator + public static DeviceEnum find(String key) { + return Arrays.stream(values()).filter(device -> device.getDevice().equals(key)) + .findAny().orElseThrow(() -> new CloudSDKException(DeviceEnum.class, key)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DeviceModelEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/DeviceModelEnum.java new file mode 100644 index 0000000..2049b88 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DeviceModelEnum.java @@ -0,0 +1,11 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/29 + */ +public enum DeviceModelEnum { + + RC, DOCK, DRONE; +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DeviceOsdHost.java b/src/main/java/com/dji/sdk/cloudapi/device/DeviceOsdHost.java new file mode 100644 index 0000000..f540f48 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DeviceOsdHost.java @@ -0,0 +1,124 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/8 + */ +public class DeviceOsdHost { + + @Schema(description = "device latitude") + @NotNull + private Float latitude; + + @Schema(description = "device longitude") + @NotNull + private Float longitude; + + @Schema(description = "device ellipsoid height") + @NotNull + private Float height; + + @Schema(description = "device head facing angle") + @NotNull + @JsonProperty("attitude_head") + private Float attitudeHead; + + @Schema(description = "height relative to the takeoff point") + @NotNull + private Float elevation; + + @Schema(description = "horizontal speed") + @NotNull + @JsonProperty("horizontal_speed") + private Float horizontalSpeed; + + @Schema(description = "vertical speed") + @NotNull + @JsonProperty("vertical_speed") + private Float verticalSpeed; + + public DeviceOsdHost() { + } + + @Override + public String toString() { + return "DeviceOsdHost{" + + "latitude=" + latitude + + ", longitude=" + longitude + + ", height=" + height + + ", attitudeHead=" + attitudeHead + + ", elevation=" + elevation + + ", horizontalSpeed=" + horizontalSpeed + + ", verticalSpeed=" + verticalSpeed + + '}'; + } + + public Float getLatitude() { + return latitude; + } + + public DeviceOsdHost setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public DeviceOsdHost setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public Float getHeight() { + return height; + } + + public DeviceOsdHost setHeight(Float height) { + this.height = height; + return this; + } + + public Float getAttitudeHead() { + return attitudeHead; + } + + public DeviceOsdHost setAttitudeHead(Float attitudeHead) { + this.attitudeHead = attitudeHead; + return this; + } + + public Float getElevation() { + return elevation; + } + + public DeviceOsdHost setElevation(Float elevation) { + this.elevation = elevation; + return this; + } + + public Float getHorizontalSpeed() { + return horizontalSpeed; + } + + public DeviceOsdHost setHorizontalSpeed(Float horizontalSpeed) { + this.horizontalSpeed = horizontalSpeed; + return this; + } + + public Float getVerticalSpeed() { + return verticalSpeed; + } + + public DeviceOsdHost setVerticalSpeed(Float verticalSpeed) { + this.verticalSpeed = verticalSpeed; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DeviceOsdWsResponse.java b/src/main/java/com/dji/sdk/cloudapi/device/DeviceOsdWsResponse.java new file mode 100644 index 0000000..57e17f5 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DeviceOsdWsResponse.java @@ -0,0 +1,53 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.common.BaseModel; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/8 + */ +@Schema(name = "DeviceOsdWsResponse", description = "BizCode: device_osd.

Websocket response data when device topology changes.

") +public class DeviceOsdWsResponse extends BaseModel { + + @NotNull + @Schema(description = "drone sn", example = "1AD3CA2VL3LAD6") + private String sn; + + @NotNull + @Valid + private DeviceOsdHost host; + + public DeviceOsdWsResponse() { + } + + @Override + public String toString() { + return "DeviceOsdWsResponse{" + + "sn='" + sn + '\'' + + ", host=" + host + + '}'; + } + + public String getSn() { + return sn; + } + + public DeviceOsdWsResponse setSn(String sn) { + this.sn = sn; + return this; + } + + public DeviceOsdHost getHost() { + return host; + } + + public DeviceOsdWsResponse setHost(DeviceOsdHost host) { + this.host = host; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DeviceSubTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/DeviceSubTypeEnum.java new file mode 100644 index 0000000..04d7cfe --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DeviceSubTypeEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/26 + */ +@Schema(description = "device subType", enumAsRef = true) +public enum DeviceSubTypeEnum { + + ZERO(0), + + ONE(1), + + TWO(2), + + _65535(65535); + + private final int subType; + + DeviceSubTypeEnum(int subType) { + this.subType = subType; + } + + @JsonValue + public int getSubType() { + return subType; + } + + @JsonCreator + public static DeviceSubTypeEnum find(int subType) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.subType == subType).findAny() + .orElseThrow(() -> new CloudSDKException(DeviceSubTypeEnum.class, subType)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DeviceTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/DeviceTypeEnum.java new file mode 100644 index 0000000..a4b5520 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DeviceTypeEnum.java @@ -0,0 +1,81 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/26 + */ +@Schema(description = "device type", enumAsRef = true) +public enum DeviceTypeEnum { + + M350(89), + + M300(60), + + M30(67), + + M3E(77), + + Z30(20), + + XT2(26), + + FPV(39), + + XTS(41), + + H20(42), + + H20T(43), + + P1(50), + + M30_CAMERA(52), + + M30T_CAMERA(53), + + H20N(61), + + DOCK_CAMERA(165), + + L1(90742), + + M3E_CAMERA(66), + + M3T_CAMERA(67), + + M3M_CAMERA(68), + + RC(56), + + RC_PLUS(119), + + RC_PRO(144), + + DOCK(1) + ; + + private final int type; + + DeviceTypeEnum(int type) { + this.type = type; + } + + @JsonValue + public int getType() { + return type; + } + + @JsonCreator + public static DeviceTypeEnum find(int type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(DeviceTypeEnum.class, type)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockDistanceLimitStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/DockDistanceLimitStatus.java new file mode 100644 index 0000000..e53ee8e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockDistanceLimitStatus.java @@ -0,0 +1,58 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * The state of the drone's limited distance + * @author sean + * @version 1.3 + * @date 2022/10/27 + */ +public class DockDistanceLimitStatus { + + private SwitchActionEnum state; + + private Integer distanceLimit; + + @JsonProperty("is_near_distance_limit") + private Boolean nearDistanceLimit; + + public DockDistanceLimitStatus() { + } + + @Override + public String toString() { + return "DockDistanceLimitStatusSet{" + + "state=" + state + + ", distanceLimit=" + distanceLimit + + ", nearDistanceLimit=" + nearDistanceLimit + + '}'; + } + + public SwitchActionEnum getState() { + return state; + } + + public DockDistanceLimitStatus setState(SwitchActionEnum state) { + this.state = state; + return this; + } + + public Integer getDistanceLimit() { + return distanceLimit; + } + + public DockDistanceLimitStatus setDistanceLimit(Integer distanceLimit) { + this.distanceLimit = distanceLimit; + return this; + } + + public Boolean getNearDistanceLimit() { + return nearDistanceLimit; + } + + public DockDistanceLimitStatus setNearDistanceLimit(Boolean nearDistanceLimit) { + this.nearDistanceLimit = nearDistanceLimit; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockDroneControlSource.java b/src/main/java/com/dji/sdk/cloudapi/device/DockDroneControlSource.java new file mode 100644 index 0000000..1b12a6c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockDroneControlSource.java @@ -0,0 +1,116 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class DockDroneControlSource { + + private ControlSourceEnum controlSource; + + private Float homeLatitude; + + private Float homeLongitude; + + private Integer lowBatteryWarningThreshold; + + private Integer seriousLowBatteryWarningThreshold; + + private List payloads; + + private Boolean locked; + + private ModeCodeReasonEnum modeCodeReason; + + public DockDroneControlSource() { + } + + @Override + public String toString() { + return "DockDroneControlSource{" + + "controlSource=" + controlSource + + ", homeLatitude=" + homeLatitude + + ", homeLongitude=" + homeLongitude + + ", lowBatteryWarningThreshold=" + lowBatteryWarningThreshold + + ", seriousLowBatteryWarningThreshold=" + seriousLowBatteryWarningThreshold + + ", payloads=" + payloads + + ", locked=" + locked + + ", modeCodeReason=" + modeCodeReason + + '}'; + } + + public ControlSourceEnum getControlSource() { + return controlSource; + } + + public DockDroneControlSource setControlSource(ControlSourceEnum controlSource) { + this.controlSource = controlSource; + return this; + } + + public Float getHomeLatitude() { + return homeLatitude; + } + + public DockDroneControlSource setHomeLatitude(Float homeLatitude) { + this.homeLatitude = homeLatitude; + return this; + } + + public Float getHomeLongitude() { + return homeLongitude; + } + + public DockDroneControlSource setHomeLongitude(Float homeLongitude) { + this.homeLongitude = homeLongitude; + return this; + } + + public Integer getLowBatteryWarningThreshold() { + return lowBatteryWarningThreshold; + } + + public DockDroneControlSource setLowBatteryWarningThreshold(Integer lowBatteryWarningThreshold) { + this.lowBatteryWarningThreshold = lowBatteryWarningThreshold; + return this; + } + + public Integer getSeriousLowBatteryWarningThreshold() { + return seriousLowBatteryWarningThreshold; + } + + public DockDroneControlSource setSeriousLowBatteryWarningThreshold(Integer seriousLowBatteryWarningThreshold) { + this.seriousLowBatteryWarningThreshold = seriousLowBatteryWarningThreshold; + return this; + } + + public List getPayloads() { + return payloads; + } + + public DockDroneControlSource setPayloads(List payloads) { + this.payloads = payloads; + return this; + } + + public Boolean getLocked() { + return locked; + } + + public DockDroneControlSource setLocked(Boolean locked) { + this.locked = locked; + return this; + } + + public ModeCodeReasonEnum getModeCodeReason() { + return modeCodeReason; + } + + public DockDroneControlSource setModeCodeReason(ModeCodeReasonEnum modeCodeReason) { + this.modeCodeReason = modeCodeReason; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockDronePayload.java b/src/main/java/com/dji/sdk/cloudapi/device/DockDronePayload.java new file mode 100644 index 0000000..8351aa1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockDronePayload.java @@ -0,0 +1,236 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/6 + */ +public class DockDronePayload { + + private PayloadIndex payloadIndex; + + private Float gimbalPitch; + + private Float gimbalRoll; + + private Float gimbalYaw; + + private Float measureTargetAltitude; + + private Float measureTargetDistance; + + private Float measureTargetLatitude; + + private Float measureTargetLongitude; + + private MeasureTargetStateEnum measureTargetErrorState; + + private Integer version; + + private ThermalPaletteStyleEnum thermalCurrentPaletteStyle; + + private ThermalGainModeEnum thermalGainMode; + + private Float thermalGlobalTemperatureMax; + + private Float thermalGlobalTemperatureMin; + + private Integer thermalIsothermLowerLimit; + + private SwitchActionEnum thermalIsothermState; + + private Integer thermalIsothermUpperLimit; + + private List smartTrackPoint; + + public DockDronePayload() { + } + + @Override + public String toString() { + return "DockDronePayload{" + + "payloadIndex=" + payloadIndex + + ", gimbalPitch=" + gimbalPitch + + ", gimbalRoll=" + gimbalRoll + + ", gimbalYaw=" + gimbalYaw + + ", measureTargetAltitude=" + measureTargetAltitude + + ", measureTargetDistance=" + measureTargetDistance + + ", measureTargetLatitude=" + measureTargetLatitude + + ", measureTargetLongitude=" + measureTargetLongitude + + ", measureTargetErrorState=" + measureTargetErrorState + + ", version=" + version + + ", thermalCurrentPaletteStyle=" + thermalCurrentPaletteStyle + + ", thermalGainMode=" + thermalGainMode + + ", thermalGlobalTemperatureMax=" + thermalGlobalTemperatureMax + + ", thermalGlobalTemperatureMin=" + thermalGlobalTemperatureMin + + ", thermalIsothermLowerLimit=" + thermalIsothermLowerLimit + + ", thermalIsothermState=" + thermalIsothermState + + ", thermalIsothermUpperLimit=" + thermalIsothermUpperLimit + + ", smartTrackPoint=" + smartTrackPoint + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public DockDronePayload setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public Float getGimbalPitch() { + return gimbalPitch; + } + + public DockDronePayload setGimbalPitch(Float gimbalPitch) { + this.gimbalPitch = gimbalPitch; + return this; + } + + public Float getGimbalRoll() { + return gimbalRoll; + } + + public DockDronePayload setGimbalRoll(Float gimbalRoll) { + this.gimbalRoll = gimbalRoll; + return this; + } + + public Float getGimbalYaw() { + return gimbalYaw; + } + + public DockDronePayload setGimbalYaw(Float gimbalYaw) { + this.gimbalYaw = gimbalYaw; + return this; + } + + public Float getMeasureTargetAltitude() { + return measureTargetAltitude; + } + + public DockDronePayload setMeasureTargetAltitude(Float measureTargetAltitude) { + this.measureTargetAltitude = measureTargetAltitude; + return this; + } + + public Float getMeasureTargetDistance() { + return measureTargetDistance; + } + + public DockDronePayload setMeasureTargetDistance(Float measureTargetDistance) { + this.measureTargetDistance = measureTargetDistance; + return this; + } + + public Float getMeasureTargetLatitude() { + return measureTargetLatitude; + } + + public DockDronePayload setMeasureTargetLatitude(Float measureTargetLatitude) { + this.measureTargetLatitude = measureTargetLatitude; + return this; + } + + public Float getMeasureTargetLongitude() { + return measureTargetLongitude; + } + + public DockDronePayload setMeasureTargetLongitude(Float measureTargetLongitude) { + this.measureTargetLongitude = measureTargetLongitude; + return this; + } + + public MeasureTargetStateEnum getMeasureTargetErrorState() { + return measureTargetErrorState; + } + + public DockDronePayload setMeasureTargetErrorState(MeasureTargetStateEnum measureTargetErrorState) { + this.measureTargetErrorState = measureTargetErrorState; + return this; + } + + public Integer getVersion() { + return version; + } + + public DockDronePayload setVersion(Integer version) { + this.version = version; + return this; + } + + public ThermalPaletteStyleEnum getThermalCurrentPaletteStyle() { + return thermalCurrentPaletteStyle; + } + + public DockDronePayload setThermalCurrentPaletteStyle(ThermalPaletteStyleEnum thermalCurrentPaletteStyle) { + this.thermalCurrentPaletteStyle = thermalCurrentPaletteStyle; + return this; + } + + public ThermalGainModeEnum getThermalGainMode() { + return thermalGainMode; + } + + public DockDronePayload setThermalGainMode(ThermalGainModeEnum thermalGainMode) { + this.thermalGainMode = thermalGainMode; + return this; + } + + public Float getThermalGlobalTemperatureMax() { + return thermalGlobalTemperatureMax; + } + + public DockDronePayload setThermalGlobalTemperatureMax(Float thermalGlobalTemperatureMax) { + this.thermalGlobalTemperatureMax = thermalGlobalTemperatureMax; + return this; + } + + public Float getThermalGlobalTemperatureMin() { + return thermalGlobalTemperatureMin; + } + + public DockDronePayload setThermalGlobalTemperatureMin(Float thermalGlobalTemperatureMin) { + this.thermalGlobalTemperatureMin = thermalGlobalTemperatureMin; + return this; + } + + public Integer getThermalIsothermLowerLimit() { + return thermalIsothermLowerLimit; + } + + public DockDronePayload setThermalIsothermLowerLimit(Integer thermalIsothermLowerLimit) { + this.thermalIsothermLowerLimit = thermalIsothermLowerLimit; + return this; + } + + public SwitchActionEnum getThermalIsothermState() { + return thermalIsothermState; + } + + public DockDronePayload setThermalIsothermState(SwitchActionEnum thermalIsothermState) { + this.thermalIsothermState = thermalIsothermState; + return this; + } + + public Integer getThermalIsothermUpperLimit() { + return thermalIsothermUpperLimit; + } + + public DockDronePayload setThermalIsothermUpperLimit(Integer thermalIsothermUpperLimit) { + this.thermalIsothermUpperLimit = thermalIsothermUpperLimit; + return this; + } + + public List getSmartTrackPoint() { + return smartTrackPoint; + } + + public DockDronePayload setSmartTrackPoint(List smartTrackPoint) { + this.smartTrackPoint = smartTrackPoint; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockFirmwareVersion.java b/src/main/java/com/dji/sdk/cloudapi/device/DockFirmwareVersion.java new file mode 100644 index 0000000..2f123bf --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockFirmwareVersion.java @@ -0,0 +1,57 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/28 + */ +public class DockFirmwareVersion { + + private String firmwareVersion; + + @JsonProperty("compatible_status") + private Boolean needCompatibleStatus; + + private Boolean firmwareUpgradeStatus; + + public DockFirmwareVersion() { + } + + @Override + public String toString() { + return "DockFirmwareVersion{" + + "firmwareVersion='" + firmwareVersion + '\'' + + ", compatibleStatus=" + needCompatibleStatus + + ", firmwareUpgradeStatus=" + firmwareUpgradeStatus + + '}'; + } + + public String getFirmwareVersion() { + return firmwareVersion; + } + + public DockFirmwareVersion setFirmwareVersion(String firmwareVersion) { + this.firmwareVersion = firmwareVersion; + return this; + } + + public Boolean getNeedCompatibleStatus() { + return needCompatibleStatus; + } + + public DockFirmwareVersion setNeedCompatibleStatus(Boolean needCompatibleStatus) { + this.needCompatibleStatus = needCompatibleStatus; + return this; + } + + public Boolean getFirmwareUpgradeStatus() { + return firmwareUpgradeStatus; + } + + public DockFirmwareVersion setFirmwareUpgradeStatus(Boolean firmwareUpgradeStatus) { + this.firmwareUpgradeStatus = firmwareUpgradeStatus; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockLiveErrorStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/DockLiveErrorStatus.java new file mode 100644 index 0000000..aa67276 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockLiveErrorStatus.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.cloudapi.livestream.LiveErrorCodeEnum; +import com.dji.sdk.common.ErrorCodeSourceEnum; +import com.dji.sdk.mqtt.MqttReply; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/23 + */ +public class DockLiveErrorStatus { + + private static final int MOD = 100_000; + + private ErrorCodeSourceEnum source = ErrorCodeSourceEnum.DOCK; + + private LiveErrorCodeEnum errorCode; + + private boolean success; + + @Override + public String toString() { + return "{" + + "errorCode=" + getCode() + + ", errorMsg=" + getMessage() + + '}'; + } + + @JsonCreator + public DockLiveErrorStatus(int code) { + if (MqttReply.CODE_SUCCESS == code) { + this.success = true; + return; + } + this.source = ErrorCodeSourceEnum.find(code / MOD); + this.errorCode = LiveErrorCodeEnum.find(code % MOD); + } + + public String getMessage() { + return errorCode.getMessage(); + } + + @JsonValue + public Integer getCode() { + return source.getSource() * MOD + errorCode.getCode(); + } + + public boolean isSuccess() { + return success; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockLiveStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/DockLiveStatus.java new file mode 100644 index 0000000..e4720a7 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockLiveStatus.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/23 + */ +public class DockLiveStatus { + + private List liveStatus; + + public DockLiveStatus() { + } + + @Override + public String toString() { + return "DockLiveStatus{" + + "liveStatus=" + liveStatus + + '}'; + } + + public List getLiveStatus() { + return liveStatus; + } + + public DockLiveStatus setLiveStatus(List liveStatus) { + this.liveStatus = liveStatus; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockLiveStatusData.java b/src/main/java/com/dji/sdk/cloudapi/device/DockLiveStatusData.java new file mode 100644 index 0000000..6c28087 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockLiveStatusData.java @@ -0,0 +1,81 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.cloudapi.livestream.VideoQualityEnum; +import com.dji.sdk.cloudapi.livestream.VideoTypeEnum; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/23 + */ +public class DockLiveStatusData { + + private Boolean status; + + private VideoId videoId; + + private VideoQualityEnum videoQuality; + + private VideoTypeEnum videoType; + + private DockLiveErrorStatus errorStatus; + + public DockLiveStatusData() { + } + + @Override + public String toString() { + return "DockLiveStatusData{" + + "status=" + status + + ", videoId=" + videoId + + ", videoQuality=" + videoQuality + + ", videoType=" + videoType + + ", errorStatus=" + errorStatus + + '}'; + } + + public Boolean getStatus() { + return status; + } + + public DockLiveStatusData setStatus(Boolean status) { + this.status = status; + return this; + } + + public VideoId getVideoId() { + return videoId; + } + + public DockLiveStatusData setVideoId(VideoId videoId) { + this.videoId = videoId; + return this; + } + + public VideoQualityEnum getVideoQuality() { + return videoQuality; + } + + public DockLiveStatusData setVideoQuality(VideoQualityEnum videoQuality) { + this.videoQuality = videoQuality; + return this; + } + + public VideoTypeEnum getVideoType() { + return videoType; + } + + public DockLiveStatusData setVideoType(VideoTypeEnum videoType) { + this.videoType = videoType; + return this; + } + + public DockLiveErrorStatus getErrorStatus() { + return errorStatus; + } + + public DockLiveStatusData setErrorStatus(DockLiveErrorStatus errorStatus) { + this.errorStatus = errorStatus; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockMaintainStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/DockMaintainStatus.java new file mode 100644 index 0000000..155842e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockMaintainStatus.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class DockMaintainStatus { + + private Integer lastMaintainFlightSorties; + + private Long lastMaintainTime; + + private MaintainTypeEnum lastMaintainType; + + private Boolean state; + + public DockMaintainStatus() { + } + + @Override + public String toString() { + return "DroneMaintainStatus{" + + "lastMaintainFlightSorties=" + lastMaintainFlightSorties + + ", lastMaintainTime=" + lastMaintainTime + + ", lastMaintainType=" + lastMaintainType + + ", state=" + state + + '}'; + } + + public Integer getLastMaintainFlightSorties() { + return lastMaintainFlightSorties; + } + + public DockMaintainStatus setLastMaintainFlightSorties(Integer lastMaintainFlightSorties) { + this.lastMaintainFlightSorties = lastMaintainFlightSorties; + return this; + } + + public Long getLastMaintainTime() { + return lastMaintainTime; + } + + public DockMaintainStatus setLastMaintainTime(Long lastMaintainTime) { + this.lastMaintainTime = lastMaintainTime; + return this; + } + + public MaintainTypeEnum getLastMaintainType() { + return lastMaintainType; + } + + public DockMaintainStatus setLastMaintainType(MaintainTypeEnum lastMaintainType) { + this.lastMaintainType = lastMaintainType; + return this; + } + + public Boolean getState() { + return state; + } + + public DockMaintainStatus setState(Boolean state) { + this.state = state; + return this; + } +} diff --git a/src/main/java/com/dji/sample/manage/model/enums/DockModeCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/DockModeCodeEnum.java similarity index 51% rename from src/main/java/com/dji/sample/manage/model/enums/DockModeCodeEnum.java rename to src/main/java/com/dji/sdk/cloudapi/device/DockModeCodeEnum.java index ae1aa91..882a5c6 100644 --- a/src/main/java/com/dji/sample/manage/model/enums/DockModeCodeEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockModeCodeEnum.java @@ -1,5 +1,6 @@ -package com.dji.sample.manage.model.enums; +package com.dji.sdk.cloudapi.device; +import com.dji.sdk.exception.CloudSDKException; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; @@ -20,23 +21,22 @@ public enum DockModeCodeEnum { UPGRADING(3), - WORKING(4), + WORKING(4); - DISCONNECTED(-1); + private final int code; - int val; - - DockModeCodeEnum(int val) { - this.val = val; + DockModeCodeEnum(int code) { + this.code = code; } @JsonValue - public int getVal() { - return val; + public int getCode() { + return code; } @JsonCreator - public static DockModeCodeEnum find(int val) { - return Arrays.stream(values()).filter(modeCode -> modeCode.getVal() == val).findAny().orElse(DISCONNECTED); + public static DockModeCodeEnum find(int code) { + return Arrays.stream(values()).filter(modeCode -> modeCode.code == code).findAny() + .orElseThrow(() -> new CloudSDKException(DockModeCodeEnum.class, code)); } } diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockPayload.java b/src/main/java/com/dji/sdk/cloudapi/device/DockPayload.java new file mode 100644 index 0000000..723719f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockPayload.java @@ -0,0 +1,56 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class DockPayload { + + private PayloadIndex payloadIndex; + + private ThermalPaletteStyleEnum[] thermalSupportedPaletteStyles; + + private Integer version; + + public DockPayload() { + } + + @Override + public String toString() { + return "DockPayload{" + + "payloadIndex=" + payloadIndex + + ", thermalSupportedPaletteStyles=" + Arrays.toString(thermalSupportedPaletteStyles) + + ", version=" + version + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public DockPayload setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public ThermalPaletteStyleEnum[] getThermalSupportedPaletteStyles() { + return thermalSupportedPaletteStyles; + } + + public DockPayload setThermalSupportedPaletteStyles(ThermalPaletteStyleEnum[] thermalSupportedPaletteStyles) { + this.thermalSupportedPaletteStyles = thermalSupportedPaletteStyles; + return this; + } + + public Integer getVersion() { + return version; + } + + public DockPayload setVersion(Integer version) { + this.version = version; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockPayloadControlSource.java b/src/main/java/com/dji/sdk/cloudapi/device/DockPayloadControlSource.java new file mode 100644 index 0000000..545e129 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockPayloadControlSource.java @@ -0,0 +1,55 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class DockPayloadControlSource { + + private ControlSourceEnum controlSource; + + private PayloadIndex payloadIndex; + + private String sn; + + public DockPayloadControlSource() { + } + + @Override + public String toString() { + return "RcPayloadControlSource{" + + "controlSource=" + controlSource + + ", payloadIndex=" + payloadIndex + + ", sn='" + sn + '\'' + + '}'; + } + + public ControlSourceEnum getControlSource() { + return controlSource; + } + + public DockPayloadControlSource setControlSource(ControlSourceEnum controlSource) { + this.controlSource = controlSource; + return this; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public DockPayloadControlSource setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public String getSn() { + return sn; + } + + public DockPayloadControlSource setSn(String sn) { + this.sn = sn; + return this; + } + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockPositionState.java b/src/main/java/com/dji/sdk/cloudapi/device/DockPositionState.java new file mode 100644 index 0000000..268f7c4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockPositionState.java @@ -0,0 +1,81 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author sean + * @version 0.3 + * @date 2022/1/27 + */ +public class DockPositionState { + + @JsonProperty("is_calibration") + private Boolean calibration; + + private Integer gpsNumber; + + private PositionFixedEnum isFixed; + + private Integer quality; + + private Integer rtkNumber; + + public DockPositionState() { + } + + @Override + public String toString() { + return "DockPositionState{" + + "Calibration=" + calibration + + ", gpsNumber=" + gpsNumber + + ", isFixed=" + isFixed + + ", quality=" + quality + + ", rtkNumber=" + rtkNumber + + '}'; + } + + public Boolean getCalibration() { + return calibration; + } + + public DockPositionState setCalibration(Boolean calibration) { + calibration = calibration; + return this; + } + + public Integer getGpsNumber() { + return gpsNumber; + } + + public DockPositionState setGpsNumber(Integer gpsNumber) { + this.gpsNumber = gpsNumber; + return this; + } + + public PositionFixedEnum getIsFixed() { + return isFixed; + } + + public DockPositionState setIsFixed(PositionFixedEnum isFixed) { + this.isFixed = isFixed; + return this; + } + + public Integer getQuality() { + return quality; + } + + public DockPositionState setQuality(Integer quality) { + this.quality = quality; + return this; + } + + public Integer getRtkNumber() { + return rtkNumber; + } + + public DockPositionState setRtkNumber(Integer rtkNumber) { + this.rtkNumber = rtkNumber; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockSubDevice.java b/src/main/java/com/dji/sdk/cloudapi/device/DockSubDevice.java new file mode 100644 index 0000000..ef5daf6 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockSubDevice.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +public class DockSubDevice { + + private String deviceSn; + + private Boolean deviceOnlineStatus; + + private Boolean devicePaired; + + private DeviceEnum deviceModelKey; + + public DockSubDevice() { + } + + @Override + public String toString() { + return "DockSubDevice{" + + "deviceSn='" + deviceSn + '\'' + + ", deviceOnlineStatus=" + deviceOnlineStatus + + ", devicePaired=" + devicePaired + + ", deviceModelKey=" + deviceModelKey + + '}'; + } + + public String getDeviceSn() { + return deviceSn; + } + + public DockSubDevice setDeviceSn(String deviceSn) { + this.deviceSn = deviceSn; + return this; + } + + public Boolean getDeviceOnlineStatus() { + return deviceOnlineStatus; + } + + public DockSubDevice setDeviceOnlineStatus(Boolean deviceOnlineStatus) { + this.deviceOnlineStatus = deviceOnlineStatus; + return this; + } + + public Boolean getDevicePaired() { + return devicePaired; + } + + public DockSubDevice setDevicePaired(Boolean devicePaired) { + this.devicePaired = devicePaired; + return this; + } + + public DeviceEnum getDeviceModelKey() { + return deviceModelKey; + } + + public DockSubDevice setDeviceModelKey(DeviceEnum deviceModelKey) { + this.deviceModelKey = deviceModelKey; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DockWpmzVersion.java b/src/main/java/com/dji/sdk/cloudapi/device/DockWpmzVersion.java new file mode 100644 index 0000000..29c6bef --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DockWpmzVersion.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class DockWpmzVersion { + + private String wpmzVersion; + + public DockWpmzVersion() { + } + + @Override + public String toString() { + return "DockWpmzVersion{" + + "wpmzVersion='" + wpmzVersion + '\'' + + '}'; + } + + public String getWpmzVersion() { + return wpmzVersion; + } + + public DockWpmzVersion setWpmzVersion(String wpmzVersion) { + this.wpmzVersion = wpmzVersion; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DrcStateEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/DrcStateEnum.java new file mode 100644 index 0000000..ee9b36f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DrcStateEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/2/28 + */ +public enum DrcStateEnum { + + DISCONNECTED(0), + + CONNECTING(1), + + CONNECTED(2); + + private final int state; + + DrcStateEnum(int state) { + this.state = state; + } + + @JsonValue + public int getState() { + return state; + } + + @JsonCreator + public static DrcStateEnum find(int state) { + return Arrays.stream(values()).filter(drcState -> drcState.state == state).findAny() + .orElseThrow(() -> new CloudSDKException(DrcStateEnum.class, state)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DroneBattery.java b/src/main/java/com/dji/sdk/cloudapi/device/DroneBattery.java new file mode 100644 index 0000000..b40e770 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DroneBattery.java @@ -0,0 +1,80 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean + * @version 0.3 + * @date 2022/1/27 + */ +public class DroneBattery { + + private List batteries; + + private Integer capacityPercent; + + private Integer landingPower; + + private Integer remainFlightTime; + + private Integer returnHomePower; + + public DroneBattery() { + } + + @Override + public String toString() { + return "DroneBattery{" + + "batteries=" + batteries + + ", capacityPercent=" + capacityPercent + + ", landingPower=" + landingPower + + ", remainFlightTime=" + remainFlightTime + + ", returnHomePower=" + returnHomePower + + '}'; + } + + public List getBatteries() { + return batteries; + } + + public DroneBattery setBatteries(List batteries) { + this.batteries = batteries; + return this; + } + + public Integer getCapacityPercent() { + return capacityPercent; + } + + public DroneBattery setCapacityPercent(Integer capacityPercent) { + this.capacityPercent = capacityPercent; + return this; + } + + public Integer getLandingPower() { + return landingPower; + } + + public DroneBattery setLandingPower(Integer landingPower) { + this.landingPower = landingPower; + return this; + } + + public Integer getRemainFlightTime() { + return remainFlightTime; + } + + public DroneBattery setRemainFlightTime(Integer remainFlightTime) { + this.remainFlightTime = remainFlightTime; + return this; + } + + public Integer getReturnHomePower() { + return returnHomePower; + } + + public DroneBattery setReturnHomePower(Integer returnHomePower) { + this.returnHomePower = returnHomePower; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DroneBatteryMaintenance.java b/src/main/java/com/dji/sdk/cloudapi/device/DroneBatteryMaintenance.java new file mode 100644 index 0000000..22d1833 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DroneBatteryMaintenance.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class DroneBatteryMaintenance { + + private BatteryIndexEnum index; + + private Integer capacityPercent; + + private Integer voltage; + + private Float temperature; + + public DroneBatteryMaintenance() { + } + + @Override + public String toString() { + return "DroneBatteryMaintenance{" + + "index=" + index + + ", capacityPercent=" + capacityPercent + + ", voltage=" + voltage + + ", temperature=" + temperature + + '}'; + } + + public BatteryIndexEnum getIndex() { + return index; + } + + public DroneBatteryMaintenance setIndex(BatteryIndexEnum index) { + this.index = index; + return this; + } + + public Integer getCapacityPercent() { + return capacityPercent; + } + + public DroneBatteryMaintenance setCapacityPercent(Integer capacityPercent) { + this.capacityPercent = capacityPercent; + return this; + } + + public Integer getVoltage() { + return voltage; + } + + public DroneBatteryMaintenance setVoltage(Integer voltage) { + this.voltage = voltage; + return this; + } + + public Float getTemperature() { + return temperature; + } + + public DroneBatteryMaintenance setTemperature(Float temperature) { + this.temperature = temperature; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DroneBatteryMaintenanceInfo.java b/src/main/java/com/dji/sdk/cloudapi/device/DroneBatteryMaintenanceInfo.java new file mode 100644 index 0000000..af85d37 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DroneBatteryMaintenanceInfo.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean + * @version 1.4 + * @date 2022/11/3 + */ +public class DroneBatteryMaintenanceInfo { + + private List batteries; + + public DroneBatteryMaintenanceInfo() { + } + + @Override + public String toString() { + return "DroneBatteryMaintenanceInfo{" + + "batteries=" + batteries + + '}'; + } + + public List getBatteries() { + return batteries; + } + + public DroneBatteryMaintenanceInfo setBatteries(List batteries) { + this.batteries = batteries; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DroneChargeState.java b/src/main/java/com/dji/sdk/cloudapi/device/DroneChargeState.java new file mode 100644 index 0000000..a873c76 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DroneChargeState.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +public class DroneChargeState { + + private Boolean state; + + private Integer capacityPercent; + + public DroneChargeState() { + } + + @Override + public String toString() { + return "DroneChargeState{" + + "state=" + state + + ", capacityPercent=" + capacityPercent + + '}'; + } + + public Boolean getState() { + return state; + } + + public DroneChargeState setState(Boolean state) { + this.state = state; + return this; + } + + public Integer getCapacityPercent() { + return capacityPercent; + } + + public DroneChargeState setCapacityPercent(Integer capacityPercent) { + this.capacityPercent = capacityPercent; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DroneMaintainStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/DroneMaintainStatus.java new file mode 100644 index 0000000..3ba6af1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DroneMaintainStatus.java @@ -0,0 +1,78 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class DroneMaintainStatus { + + private Integer lastMaintainFlightSorties; + + private Integer lastMaintainFlightTime; + + private Long lastMaintainTime; + + private MaintainTypeEnum lastMaintainType; + + private Boolean state; + + public DroneMaintainStatus() { + } + + @Override + public String toString() { + return "DroneMaintainStatus{" + + "lastMaintainFlightSorties=" + lastMaintainFlightSorties + + ", lastMaintainFlightTime=" + lastMaintainFlightTime + + ", lastMaintainTime=" + lastMaintainTime + + ", lastMaintainType=" + lastMaintainType + + ", state=" + state + + '}'; + } + + public Integer getLastMaintainFlightSorties() { + return lastMaintainFlightSorties; + } + + public DroneMaintainStatus setLastMaintainFlightSorties(Integer lastMaintainFlightSorties) { + this.lastMaintainFlightSorties = lastMaintainFlightSorties; + return this; + } + + public Integer getLastMaintainFlightTime() { + return lastMaintainFlightTime; + } + + public DroneMaintainStatus setLastMaintainFlightTime(Integer lastMaintainFlightTime) { + this.lastMaintainFlightTime = lastMaintainFlightTime; + return this; + } + + public Long getLastMaintainTime() { + return lastMaintainTime; + } + + public DroneMaintainStatus setLastMaintainTime(Long lastMaintainTime) { + this.lastMaintainTime = lastMaintainTime; + return this; + } + + public MaintainTypeEnum getLastMaintainType() { + return lastMaintainType; + } + + public DroneMaintainStatus setLastMaintainType(MaintainTypeEnum lastMaintainType) { + this.lastMaintainType = lastMaintainType; + return this; + } + + public Boolean getState() { + return state; + } + + public DroneMaintainStatus setState(Boolean state) { + this.state = state; + return this; + } +} diff --git a/src/main/java/com/dji/sample/manage/model/enums/DeviceModeCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/DroneModeCodeEnum.java similarity index 61% rename from src/main/java/com/dji/sample/manage/model/enums/DeviceModeCodeEnum.java rename to src/main/java/com/dji/sdk/cloudapi/device/DroneModeCodeEnum.java index 9957032..526460b 100644 --- a/src/main/java/com/dji/sample/manage/model/enums/DeviceModeCodeEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/device/DroneModeCodeEnum.java @@ -1,5 +1,6 @@ -package com.dji.sample.manage.model.enums; +package com.dji.sdk.cloudapi.device; +import com.dji.sdk.exception.CloudSDKException; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; @@ -10,7 +11,7 @@ import java.util.Arrays; * @version 1.4 * @date 2023/3/9 */ -public enum DeviceModeCodeEnum { +public enum DroneModeCodeEnum { IDLE(0), @@ -46,19 +47,20 @@ public enum DeviceModeCodeEnum { VIRTUAL_JOYSTICK(16); - int val; + private final int code; - DeviceModeCodeEnum(int val) { - this.val = val; + DroneModeCodeEnum(int code) { + this.code = code; } @JsonValue - public int getVal() { - return val; + public int getCode() { + return code; } @JsonCreator - public static DeviceModeCodeEnum find(int value) { - return Arrays.stream(values()).filter(modeCodeEnum -> modeCodeEnum.ordinal() == value).findAny().orElse(DISCONNECTED); + public static DroneModeCodeEnum find(int code) { + return Arrays.stream(values()).filter(modeCodeEnum -> modeCodeEnum.ordinal() == code).findAny() + .orElseThrow(() -> new CloudSDKException(DroneModeCodeEnum.class, code)); } } diff --git a/src/main/java/com/dji/sdk/cloudapi/device/DronePositionState.java b/src/main/java/com/dji/sdk/cloudapi/device/DronePositionState.java new file mode 100644 index 0000000..b0d6aef --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/DronePositionState.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 0.3 + * @date 2022/1/27 + */ +public class DronePositionState { + + private Integer gpsNumber; + + private PositionFixedEnum isFixed; + + private Integer quality; + + private Integer rtkNumber; + + public DronePositionState() { + } + + @Override + public String toString() { + return "DronePositionState{" + + "gpsNumber=" + gpsNumber + + ", isFixed=" + isFixed + + ", quality=" + quality + + ", rtkNumber=" + rtkNumber + + '}'; + } + + public Integer getGpsNumber() { + return gpsNumber; + } + + public DronePositionState setGpsNumber(Integer gpsNumber) { + this.gpsNumber = gpsNumber; + return this; + } + + public PositionFixedEnum getIsFixed() { + return isFixed; + } + + public DronePositionState setIsFixed(PositionFixedEnum isFixed) { + this.isFixed = isFixed; + return this; + } + + public Integer getQuality() { + return quality; + } + + public DronePositionState setQuality(Integer quality) { + this.quality = quality; + return this; + } + + public Integer getRtkNumber() { + return rtkNumber; + } + + public DronePositionState setRtkNumber(Integer rtkNumber) { + this.rtkNumber = rtkNumber; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/ExitWaylineWhenRcLostEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/ExitWaylineWhenRcLostEnum.java new file mode 100755 index 0000000..e5f1483 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/ExitWaylineWhenRcLostEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public enum ExitWaylineWhenRcLostEnum { + + CONTINUE(0), + + EXECUTE_RC_LOST_ACTION(1); + + private final int action; + + ExitWaylineWhenRcLostEnum(int action) { + this.action = action; + } + + @JsonValue + public int getAction() { + return action; + } + + @JsonCreator + public static ExitWaylineWhenRcLostEnum find(int action) { + return Arrays.stream(values()).filter(actionEnum -> actionEnum.action == action).findAny() + .orElseThrow(() -> new CloudSDKException(ExitWaylineWhenRcLostEnum.class, action)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/FlighttaskStepCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/FlighttaskStepCodeEnum.java new file mode 100644 index 0000000..00ed09f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/FlighttaskStepCodeEnum.java @@ -0,0 +1,49 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum FlighttaskStepCodeEnum { + + TASK_PREPARING(0), + + TASK_OPERATING(1), + + STATE_RECOVERING(2), + + WORKING(4), + + // TODO 确认状态 + UNKNOWN(5), + + UNKNOWN1(255), + + UNKNOWN2(256), + ; + + private final int code; + + FlighttaskStepCodeEnum(int code) { + this.code = code; + } + + @JsonValue + public int getCode() { + return code; + } + + @JsonCreator + public static FlighttaskStepCodeEnum find(int code) { + return Arrays.stream(values()).filter(codeEnum -> codeEnum.code == code).findAny() + .orElseThrow(() -> new CloudSDKException(FlighttaskStepCodeEnum.class, code)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/GearEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/GearEnum.java new file mode 100644 index 0000000..b22a957 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/GearEnum.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum GearEnum { + + A(0), + + P(1), + + NAV(2), + + FPV(3), + + FARM(4), + + S(5), + + F(6), + + M(7), + + G(8), + + T(9), + ; + + private final int gear; + + GearEnum(int gear) { + this.gear = gear; + } + + @JsonValue + public int getGear() { + return gear; + } + + @JsonCreator + public static GearEnum find(int gear) { + return Arrays.stream(values()).filter(gearEnum -> gearEnum.gear == gear).findAny() + .orElseThrow(() -> new CloudSDKException(GearEnum.class, gear)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/LinkWorkModeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/LinkWorkModeEnum.java new file mode 100644 index 0000000..8e836fe --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/LinkWorkModeEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/25 + */ +public enum LinkWorkModeEnum { + + SDR_ONLY(0), + + SDR_WITH_4G(1); + + private final int mode; + + LinkWorkModeEnum(int mode) { + this.mode = mode; + } + + @JsonValue + public int getMode() { + return mode; + } + + @JsonCreator + public static LinkWorkModeEnum find(int mode) { + return Arrays.stream(LinkWorkModeEnum.values()).filter(modeEnum -> modeEnum.mode == mode).findAny() + .orElseThrow(() -> new CloudSDKException(LinkWorkModeEnum.class, mode)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/LiveviewWorldRegion.java b/src/main/java/com/dji/sdk/cloudapi/device/LiveviewWorldRegion.java new file mode 100644 index 0000000..308060c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/LiveviewWorldRegion.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/8 + */ +public class LiveviewWorldRegion { + + private Float bottom; + + private Float left; + + private Float right; + + private Float top; + + public LiveviewWorldRegion() { + } + + @Override + public String toString() { + return "LiveviewWorldRegion{" + + "bottom=" + bottom + + ", left=" + left + + ", right=" + right + + ", top=" + top + + '}'; + } + + public Float getBottom() { + return bottom; + } + + public LiveviewWorldRegion setBottom(Float bottom) { + this.bottom = bottom; + return this; + } + + public Float getLeft() { + return left; + } + + public LiveviewWorldRegion setLeft(Float left) { + this.left = left; + return this; + } + + public Float getRight() { + return right; + } + + public LiveviewWorldRegion setRight(Float right) { + this.right = right; + return this; + } + + public Float getTop() { + return top; + } + + public LiveviewWorldRegion setTop(Float top) { + this.top = top; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/MaintainTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/MaintainTypeEnum.java new file mode 100644 index 0000000..a1c1f11 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/MaintainTypeEnum.java @@ -0,0 +1,46 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum MaintainTypeEnum { + + NO(0), + + DRONE_BASIC(1), + + DRONE_ROUTINE(2), + + DRONE_DEEP(3), + + DOCK_ROUTINE(17), + + DOCK_DEEP(18), + ; + + private final int type; + + MaintainTypeEnum(int type) { + this.type = type; + } + + @JsonValue + public int getType() { + return type; + } + + @JsonCreator + public static MaintainTypeEnum find(int type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(MaintainTypeEnum.class, type)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/MeasureTargetStateEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/MeasureTargetStateEnum.java new file mode 100644 index 0000000..48566e8 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/MeasureTargetStateEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum MeasureTargetStateEnum { + + NORMAL(0), + + TOO_CLOSE(1), + + TOO_FAR(2), + + NO_SIGNAL(3), + ; + + private final int state; + + MeasureTargetStateEnum(int state) { + this.state = state; + } + + @JsonValue + public int getState() { + return state; + } + + @JsonCreator + public static MeasureTargetStateEnum find(int state) { + return Arrays.stream(values()).filter(stateEnum -> stateEnum.state == state).findAny() + .orElseThrow(() -> new CloudSDKException(MeasureTargetStateEnum.class, state)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/MediaFileDetail.java b/src/main/java/com/dji/sdk/cloudapi/device/MediaFileDetail.java new file mode 100644 index 0000000..d1e3e90 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/MediaFileDetail.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/17 + */ +public class MediaFileDetail { + + private Integer remainUpload; + + public MediaFileDetail() { + } + + @Override + public String toString() { + return "MediaFileDetail{" + + "remainUpload=" + remainUpload + + '}'; + } + + public Integer getRemainUpload() { + return remainUpload; + } + + public MediaFileDetail setRemainUpload(Integer remainUpload) { + this.remainUpload = remainUpload; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/ModeCodeReasonEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/ModeCodeReasonEnum.java new file mode 100644 index 0000000..dd8f67a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/ModeCodeReasonEnum.java @@ -0,0 +1,79 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/2/28 + */ +public enum ModeCodeReasonEnum { + + NO_MEANING(0), + + LOW_POWER(1), + + LOW_VOLTAGE(2), + + SERIOUS_LOW_VOLTAGE(3), + + RC_CONTROL(4), + + APP_CONTROL(5), + + RC_SIGNAL_LOST(6), + + EXTERNAL_DEVICE_TRIGGERED(7), + + GEO_ZONE(8), + + HOME_POINT_TOO_CLOSED(9), + + HOME_POINT_TOO_FAR(10), + + EXECUTING_WAYPOINT_MISSION(11), + + ARRIVE_HOME_POINT(12), + + SECOND_LIMIT_LANDING(13), + + APP_FORCIBLY_BREAK_PROTECTION(14), + + PLANES_PASSING_NEARBY(15), + + HEIGHT_CONTROL_FAILED(16), + + LOW_POWER_RTH(17), + + AP_CONTROL(18), + + HARDWARE_ABNORMAL(19), + + TOUCHDOWN_AVOIDANCE_PROTECTION(20), + + CANCEL_RTH(21), + + RTH_OBSTACLE_AVOIDANCE(22), + ; + + private final int reason; + + ModeCodeReasonEnum(int reason) { + this.reason = reason; + } + + @JsonValue + public int getReason() { + return reason; + } + + @JsonCreator + public static ModeCodeReasonEnum find(int reason) { + return Arrays.stream(values()).filter(reasonEnum -> reasonEnum.reason == reason).findAny() + .orElseThrow(() -> new CloudSDKException(ModeCodeReasonEnum.class, reason)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/NetworkState.java b/src/main/java/com/dji/sdk/cloudapi/device/NetworkState.java new file mode 100644 index 0000000..dfe33b3 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/NetworkState.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +public class NetworkState { + + private NetworkStateTypeEnum type; + + private NetworkStateQualityEnum quality; + + private Float rate; + + public NetworkState() { + } + + @Override + public String toString() { + return "NetworkState{" + + "type=" + type + + ", quality=" + quality + + ", rate=" + rate + + '}'; + } + + public NetworkStateTypeEnum getType() { + return type; + } + + public NetworkState setType(NetworkStateTypeEnum type) { + this.type = type; + return this; + } + + public NetworkStateQualityEnum getQuality() { + return quality; + } + + public NetworkState setQuality(NetworkStateQualityEnum quality) { + this.quality = quality; + return this; + } + + public Float getRate() { + return rate; + } + + public NetworkState setRate(Float rate) { + this.rate = rate; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/NetworkStateQualityEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/NetworkStateQualityEnum.java new file mode 100644 index 0000000..6b0b447 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/NetworkStateQualityEnum.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum NetworkStateQualityEnum { + + BAD(0), + + MODERATE(1), + + GOOD(2), + ; + + private final int quality; + + NetworkStateQualityEnum(int quality) { + this.quality = quality; + } + + @JsonValue + public int getQuality() { + return quality; + } + + @JsonCreator + public static NetworkStateQualityEnum find(int quality) { + return Arrays.stream(values()).filter(qualityEnum -> qualityEnum.quality == quality).findAny() + .orElseThrow(() -> new CloudSDKException(NetworkStateQualityEnum.class, quality)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/NetworkStateTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/NetworkStateTypeEnum.java new file mode 100644 index 0000000..9f63e06 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/NetworkStateTypeEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum NetworkStateTypeEnum { + + FOURTH_GENERATION(1), + + ETHERNET(2), + ; + + private final int type; + + NetworkStateTypeEnum(int type) { + this.type = type; + } + + @JsonValue + public int getType() { + return type; + } + + @JsonCreator + public static NetworkStateTypeEnum find(int type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(NetworkStateTypeEnum.class, type)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/ObstacleAvoidance.java b/src/main/java/com/dji/sdk/cloudapi/device/ObstacleAvoidance.java new file mode 100644 index 0000000..fc49dfe --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/ObstacleAvoidance.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.3 + * @date 2022/10/27 + */ +public class ObstacleAvoidance { + + private SwitchActionEnum horizon; + + private SwitchActionEnum upside; + + private SwitchActionEnum downside; + + public ObstacleAvoidance() { + } + + @Override + public String toString() { + return "ObstacleAvoidanceSet{" + + "horizon=" + horizon + + ", upside=" + upside + + ", downside=" + downside + + '}'; + } + + public SwitchActionEnum getHorizon() { + return horizon; + } + + public ObstacleAvoidance setHorizon(SwitchActionEnum horizon) { + this.horizon = horizon; + return this; + } + + public SwitchActionEnum getUpside() { + return upside; + } + + public ObstacleAvoidance setUpside(SwitchActionEnum upside) { + this.upside = upside; + return this; + } + + public SwitchActionEnum getDownside() { + return downside; + } + + public ObstacleAvoidance setDownside(SwitchActionEnum downside) { + this.downside = downside; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/OsdCamera.java b/src/main/java/com/dji/sdk/cloudapi/device/OsdCamera.java new file mode 100644 index 0000000..0d829ba --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/OsdCamera.java @@ -0,0 +1,138 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/8 + */ +public class OsdCamera { + + private CameraModeEnum cameraMode; + + private LiveviewWorldRegion liveviewWorldRegion; + + private PayloadIndex payloadIndex; + + private CameraStateEnum photoState; + + private Integer recordTime; + + private CameraStateEnum recordingState; + + private Long remainPhotoNum; + + private Integer remainRecordDuration; + + private Float zoomFactor; + + private Float irZoomFactor; + + public OsdCamera() { + } + + @Override + public String toString() { + return "OsdCamera{" + + "cameraMode=" + cameraMode + + ", liveviewWorldRegion=" + liveviewWorldRegion + + ", payloadIndex=" + payloadIndex + + ", photoState=" + photoState + + ", recordTime=" + recordTime + + ", recordingState=" + recordingState + + ", remainPhotoNum=" + remainPhotoNum + + ", remainRecordDuration=" + remainRecordDuration + + ", zoomFactor=" + zoomFactor + + ", irZoomFactor=" + irZoomFactor + + '}'; + } + + public CameraModeEnum getCameraMode() { + return cameraMode; + } + + public OsdCamera setCameraMode(CameraModeEnum cameraMode) { + this.cameraMode = cameraMode; + return this; + } + + public LiveviewWorldRegion getLiveviewWorldRegion() { + return liveviewWorldRegion; + } + + public OsdCamera setLiveviewWorldRegion(LiveviewWorldRegion liveviewWorldRegion) { + this.liveviewWorldRegion = liveviewWorldRegion; + return this; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public OsdCamera setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public CameraStateEnum getPhotoState() { + return photoState; + } + + public OsdCamera setPhotoState(CameraStateEnum photoState) { + this.photoState = photoState; + return this; + } + + public Integer getRecordTime() { + return recordTime; + } + + public OsdCamera setRecordTime(Integer recordTime) { + this.recordTime = recordTime; + return this; + } + + public CameraStateEnum getRecordingState() { + return recordingState; + } + + public OsdCamera setRecordingState(CameraStateEnum recordingState) { + this.recordingState = recordingState; + return this; + } + + public Long getRemainPhotoNum() { + return remainPhotoNum; + } + + public OsdCamera setRemainPhotoNum(Long remainPhotoNum) { + this.remainPhotoNum = remainPhotoNum; + return this; + } + + public Integer getRemainRecordDuration() { + return remainRecordDuration; + } + + public OsdCamera setRemainRecordDuration(Integer remainRecordDuration) { + this.remainRecordDuration = remainRecordDuration; + return this; + } + + public Float getZoomFactor() { + return zoomFactor; + } + + public OsdCamera setZoomFactor(Float zoomFactor) { + this.zoomFactor = zoomFactor; + return this; + } + + public Float getIrZoomFactor() { + return irZoomFactor; + } + + public OsdCamera setIrZoomFactor(Float irZoomFactor) { + this.irZoomFactor = irZoomFactor; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/OsdDock.java b/src/main/java/com/dji/sdk/cloudapi/device/OsdDock.java new file mode 100644 index 0000000..1ea119b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/OsdDock.java @@ -0,0 +1,474 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/3 + */ +public class OsdDock { + + private NetworkState networkState; + + private Boolean droneInDock; + + private DroneChargeState droneChargeState; + + private RainfallEnum rainfall; + + private Float windSpeed; + + private Float environmentTemperature; + + private Float temperature; + + private Integer humidity; + + private Float latitude; + + private Float longitude; + + private Float height; + + private AlternateLandPoint alternateLandPoint; + + private Long firstPowerOn; + + private DockPositionState positionState; + + private Storage storage; + + private DockModeCodeEnum modeCode; + + private CoverStateEnum coverState; + + private Boolean supplementLightState; + + private Boolean emergencyStopState; + + private AirConditioner airConditioner; + + private BatteryStoreModeEnum batteryStoreMode; + + private Boolean alarmState; + + private PutterStateEnum putterState; + + private DockSubDevice subDevice; + + private Integer jobNumber; + + private Long accTime; + + private Long activationTime; + + private OsdDockMaintainStatus maintainStatus; + + private Integer electricSupplyVoltage; + + private Integer workingVoltage; + + private Integer workingCurrent; + + private BackupBattery backupBattery; + + private DroneBatteryMaintenanceInfo droneBatteryMaintenanceInfo; + + private FlighttaskStepCodeEnum flighttaskStepCode; + + private Integer flighttaskPrepareCapacity; + + private MediaFileDetail mediaFileDetail; + + private WirelessLink wirelessLink; + + private DrcStateEnum drcState; + + public OsdDock() { + } + + @Override + public String toString() { + return "OsdDock{" + + "networkState=" + networkState + + ", droneInDock=" + droneInDock + + ", droneChargeState=" + droneChargeState + + ", rainfall=" + rainfall + + ", windSpeed=" + windSpeed + + ", environmentTemperature=" + environmentTemperature + + ", temperature=" + temperature + + ", humidity=" + humidity + + ", latitude=" + latitude + + ", longitude=" + longitude + + ", height=" + height + + ", alternateLandPoint=" + alternateLandPoint + + ", firstPowerOn=" + firstPowerOn + + ", positionState=" + positionState + + ", storage=" + storage + + ", modeCode=" + modeCode + + ", coverState=" + coverState + + ", supplementLightState=" + supplementLightState + + ", emergencyStopState=" + emergencyStopState + + ", airConditioner=" + airConditioner + + ", batteryStoreMode=" + batteryStoreMode + + ", alarmState=" + alarmState + + ", putterState=" + putterState + + ", subDevice=" + subDevice + + ", jobNumber=" + jobNumber + + ", accTime=" + accTime + + ", activationTime=" + activationTime + + ", maintainStatus=" + maintainStatus + + ", electricSupplyVoltage=" + electricSupplyVoltage + + ", workingVoltage=" + workingVoltage + + ", workingCurrent=" + workingCurrent + + ", backupBattery=" + backupBattery + + ", droneBatteryMaintenanceInfo=" + droneBatteryMaintenanceInfo + + ", flighttaskStepCode=" + flighttaskStepCode + + ", flighttaskPrepareCapacity=" + flighttaskPrepareCapacity + + ", mediaFileDetail=" + mediaFileDetail + + ", wirelessLink=" + wirelessLink + + ", drcState=" + drcState + + '}'; + } + + public NetworkState getNetworkState() { + return networkState; + } + + public OsdDock setNetworkState(NetworkState networkState) { + this.networkState = networkState; + return this; + } + + public Boolean getDroneInDock() { + return droneInDock; + } + + public OsdDock setDroneInDock(Boolean droneInDock) { + this.droneInDock = droneInDock; + return this; + } + + public DroneChargeState getDroneChargeState() { + return droneChargeState; + } + + public OsdDock setDroneChargeState(DroneChargeState droneChargeState) { + this.droneChargeState = droneChargeState; + return this; + } + + public RainfallEnum getRainfall() { + return rainfall; + } + + public OsdDock setRainfall(RainfallEnum rainfall) { + this.rainfall = rainfall; + return this; + } + + public Float getWindSpeed() { + return windSpeed; + } + + public OsdDock setWindSpeed(Float windSpeed) { + this.windSpeed = windSpeed; + return this; + } + + public Float getEnvironmentTemperature() { + return environmentTemperature; + } + + public OsdDock setEnvironmentTemperature(Float environmentTemperature) { + this.environmentTemperature = environmentTemperature; + return this; + } + + public Float getTemperature() { + return temperature; + } + + public OsdDock setTemperature(Float temperature) { + this.temperature = temperature; + return this; + } + + public Integer getHumidity() { + return humidity; + } + + public OsdDock setHumidity(Integer humidity) { + this.humidity = humidity; + return this; + } + + public Float getLatitude() { + return latitude; + } + + public OsdDock setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public OsdDock setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public Float getHeight() { + return height; + } + + public OsdDock setHeight(Float height) { + this.height = height; + return this; + } + + public AlternateLandPoint getAlternateLandPoint() { + return alternateLandPoint; + } + + public OsdDock setAlternateLandPoint(AlternateLandPoint alternateLandPoint) { + this.alternateLandPoint = alternateLandPoint; + return this; + } + + public Long getFirstPowerOn() { + return firstPowerOn; + } + + public OsdDock setFirstPowerOn(Long firstPowerOn) { + this.firstPowerOn = firstPowerOn; + return this; + } + + public DockPositionState getPositionState() { + return positionState; + } + + public OsdDock setPositionState(DockPositionState positionState) { + this.positionState = positionState; + return this; + } + + public Storage getStorage() { + return storage; + } + + public OsdDock setStorage(Storage storage) { + this.storage = storage; + return this; + } + + public DockModeCodeEnum getModeCode() { + return modeCode; + } + + public OsdDock setModeCode(DockModeCodeEnum modeCode) { + this.modeCode = modeCode; + return this; + } + + public CoverStateEnum getCoverState() { + return coverState; + } + + public OsdDock setCoverState(CoverStateEnum coverState) { + this.coverState = coverState; + return this; + } + + public Boolean getSupplementLightState() { + return supplementLightState; + } + + public OsdDock setSupplementLightState(Boolean supplementLightState) { + this.supplementLightState = supplementLightState; + return this; + } + + public Boolean getEmergencyStopState() { + return emergencyStopState; + } + + public OsdDock setEmergencyStopState(Boolean emergencyStopState) { + this.emergencyStopState = emergencyStopState; + return this; + } + + public AirConditioner getAirConditioner() { + return airConditioner; + } + + public OsdDock setAirConditioner(AirConditioner airConditioner) { + this.airConditioner = airConditioner; + return this; + } + + public BatteryStoreModeEnum getBatteryStoreMode() { + return batteryStoreMode; + } + + public OsdDock setBatteryStoreMode(BatteryStoreModeEnum batteryStoreMode) { + this.batteryStoreMode = batteryStoreMode; + return this; + } + + public Boolean getAlarmState() { + return alarmState; + } + + public OsdDock setAlarmState(Boolean alarmState) { + this.alarmState = alarmState; + return this; + } + + public PutterStateEnum getPutterState() { + return putterState; + } + + public OsdDock setPutterState(PutterStateEnum putterState) { + this.putterState = putterState; + return this; + } + + public DockSubDevice getSubDevice() { + return subDevice; + } + + public OsdDock setSubDevice(DockSubDevice subDevice) { + this.subDevice = subDevice; + return this; + } + + public Integer getJobNumber() { + return jobNumber; + } + + public OsdDock setJobNumber(Integer jobNumber) { + this.jobNumber = jobNumber; + return this; + } + + public Long getAccTime() { + return accTime; + } + + public OsdDock setAccTime(Long accTime) { + this.accTime = accTime; + return this; + } + + public Long getActivationTime() { + return activationTime; + } + + public OsdDock setActivationTime(Long activationTime) { + this.activationTime = activationTime; + return this; + } + + public OsdDockMaintainStatus getMaintainStatus() { + return maintainStatus; + } + + public OsdDock setMaintainStatus(OsdDockMaintainStatus maintainStatus) { + this.maintainStatus = maintainStatus; + return this; + } + + public Integer getElectricSupplyVoltage() { + return electricSupplyVoltage; + } + + public OsdDock setElectricSupplyVoltage(Integer electricSupplyVoltage) { + this.electricSupplyVoltage = electricSupplyVoltage; + return this; + } + + public Integer getWorkingVoltage() { + return workingVoltage; + } + + public OsdDock setWorkingVoltage(Integer workingVoltage) { + this.workingVoltage = workingVoltage; + return this; + } + + public Integer getWorkingCurrent() { + return workingCurrent; + } + + public OsdDock setWorkingCurrent(Integer workingCurrent) { + this.workingCurrent = workingCurrent; + return this; + } + + public BackupBattery getBackupBattery() { + return backupBattery; + } + + public OsdDock setBackupBattery(BackupBattery backupBattery) { + this.backupBattery = backupBattery; + return this; + } + + public DroneBatteryMaintenanceInfo getDroneBatteryMaintenanceInfo() { + return droneBatteryMaintenanceInfo; + } + + public OsdDock setDroneBatteryMaintenanceInfo(DroneBatteryMaintenanceInfo droneBatteryMaintenanceInfo) { + this.droneBatteryMaintenanceInfo = droneBatteryMaintenanceInfo; + return this; + } + + public FlighttaskStepCodeEnum getFlighttaskStepCode() { + return flighttaskStepCode; + } + + public OsdDock setFlighttaskStepCode(FlighttaskStepCodeEnum flighttaskStepCode) { + this.flighttaskStepCode = flighttaskStepCode; + return this; + } + + public Integer getFlighttaskPrepareCapacity() { + return flighttaskPrepareCapacity; + } + + public OsdDock setFlighttaskPrepareCapacity(Integer flighttaskPrepareCapacity) { + this.flighttaskPrepareCapacity = flighttaskPrepareCapacity; + return this; + } + + public MediaFileDetail getMediaFileDetail() { + return mediaFileDetail; + } + + public OsdDock setMediaFileDetail(MediaFileDetail mediaFileDetail) { + this.mediaFileDetail = mediaFileDetail; + return this; + } + + public WirelessLink getWirelessLink() { + return wirelessLink; + } + + public OsdDock setWirelessLink(WirelessLink wirelessLink) { + this.wirelessLink = wirelessLink; + return this; + } + + public DrcStateEnum getDrcState() { + return drcState; + } + + public OsdDock setDrcState(DrcStateEnum drcState) { + this.drcState = drcState; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/OsdDockDrone.java b/src/main/java/com/dji/sdk/cloudapi/device/OsdDockDrone.java new file mode 100644 index 0000000..d7e2176 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/OsdDockDrone.java @@ -0,0 +1,469 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/23 + */ +public class OsdDockDrone { + + private Float attitudeHead; + + private Double attitudePitch; + + private Double attitudeRoll; + + private Float elevation; + + private DroneBattery battery; + + private String firmwareVersion; + + private GearEnum gear; + + private Float height; + + private Float homeDistance; + + private Float horizontalSpeed; + + private Float latitude; + + private Float longitude; + + private DroneModeCodeEnum modeCode; + + private Double totalFlightDistance; + + private Float totalFlightTime; + + private Float verticalSpeed; + + private WindDirectionEnum windDirection; + + private Float windSpeed; + + private DronePositionState positionState; + + @JsonProperty(PayloadModelEnum.PAYLOAD_KEY) + private List payloads; + + private Storage storage; + + private SwitchActionEnum nightLightsState; + + private Integer heightLimit; + + private DockDistanceLimitStatus distanceLimitStatus; + + private ObstacleAvoidance obstacleAvoidance; + + private Long activationTime; + + private List cameras; + + private RcLostActionEnum rcLostAction; + + private Integer rthAltitude; + + private Integer totalFlightSorties; + + private ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost; + + private String country; + + private Boolean ridState; + + @JsonProperty("is_near_area_limit") + private Boolean nearAreaLimit; + + @JsonProperty("is_near_height_limit") + private Boolean nearHeightLimit; + + private OsdDroneMaintainStatus maintainStatus; + + private String trackId; + + public OsdDockDrone() { + } + + @Override + public String toString() { + return "OsdDockDrone{" + + "attitudeHead=" + attitudeHead + + ", attitudePitch=" + attitudePitch + + ", attitudeRoll=" + attitudeRoll + + ", elevation=" + elevation + + ", battery=" + battery + + ", firmwareVersion='" + firmwareVersion + '\'' + + ", gear=" + gear + + ", height=" + height + + ", homeDistance=" + homeDistance + + ", horizontalSpeed=" + horizontalSpeed + + ", latitude=" + latitude + + ", longitude=" + longitude + + ", modeCode=" + modeCode + + ", totalFlightDistance=" + totalFlightDistance + + ", totalFlightTime=" + totalFlightTime + + ", verticalSpeed=" + verticalSpeed + + ", windDirection=" + windDirection + + ", windSpeed=" + windSpeed + + ", positionState=" + positionState + + ", payloads=" + payloads + + ", storage=" + storage + + ", nightLightsState=" + nightLightsState + + ", heightLimit=" + heightLimit + + ", distanceLimitStatus=" + distanceLimitStatus + + ", obstacleAvoidance=" + obstacleAvoidance + + ", activationTime=" + activationTime + + ", cameras=" + cameras + + ", rcLostAction=" + rcLostAction + + ", rthAltitude=" + rthAltitude + + ", totalFlightSorties=" + totalFlightSorties + + ", exitWaylineWhenRcLost=" + exitWaylineWhenRcLost + + ", country='" + country + '\'' + + ", ridState=" + ridState + + ", nearAreaLimit=" + nearAreaLimit + + ", nearHeightLimit=" + nearHeightLimit + + ", maintainStatus=" + maintainStatus + + ", trackId='" + trackId + '\'' + + '}'; + } + + public Float getAttitudeHead() { + return attitudeHead; + } + + public OsdDockDrone setAttitudeHead(Float attitudeHead) { + this.attitudeHead = attitudeHead; + return this; + } + + public Double getAttitudePitch() { + return attitudePitch; + } + + public OsdDockDrone setAttitudePitch(Double attitudePitch) { + this.attitudePitch = attitudePitch; + return this; + } + + public Double getAttitudeRoll() { + return attitudeRoll; + } + + public OsdDockDrone setAttitudeRoll(Double attitudeRoll) { + this.attitudeRoll = attitudeRoll; + return this; + } + + public Float getElevation() { + return elevation; + } + + public OsdDockDrone setElevation(Float elevation) { + this.elevation = elevation; + return this; + } + + public DroneBattery getBattery() { + return battery; + } + + public OsdDockDrone setBattery(DroneBattery battery) { + this.battery = battery; + return this; + } + + public String getFirmwareVersion() { + return firmwareVersion; + } + + public OsdDockDrone setFirmwareVersion(String firmwareVersion) { + this.firmwareVersion = firmwareVersion; + return this; + } + + public GearEnum getGear() { + return gear; + } + + public OsdDockDrone setGear(GearEnum gear) { + this.gear = gear; + return this; + } + + public Float getHeight() { + return height; + } + + public OsdDockDrone setHeight(Float height) { + this.height = height; + return this; + } + + public Float getHomeDistance() { + return homeDistance; + } + + public OsdDockDrone setHomeDistance(Float homeDistance) { + this.homeDistance = homeDistance; + return this; + } + + public Float getHorizontalSpeed() { + return horizontalSpeed; + } + + public OsdDockDrone setHorizontalSpeed(Float horizontalSpeed) { + this.horizontalSpeed = horizontalSpeed; + return this; + } + + public Float getLatitude() { + return latitude; + } + + public OsdDockDrone setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public OsdDockDrone setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public DroneModeCodeEnum getModeCode() { + return modeCode; + } + + public OsdDockDrone setModeCode(DroneModeCodeEnum modeCode) { + this.modeCode = modeCode; + return this; + } + + public Double getTotalFlightDistance() { + return totalFlightDistance; + } + + public OsdDockDrone setTotalFlightDistance(Double totalFlightDistance) { + this.totalFlightDistance = totalFlightDistance; + return this; + } + + public Float getTotalFlightTime() { + return totalFlightTime; + } + + public OsdDockDrone setTotalFlightTime(Float totalFlightTime) { + this.totalFlightTime = totalFlightTime; + return this; + } + + public Float getVerticalSpeed() { + return verticalSpeed; + } + + public OsdDockDrone setVerticalSpeed(Float verticalSpeed) { + this.verticalSpeed = verticalSpeed; + return this; + } + + public WindDirectionEnum getWindDirection() { + return windDirection; + } + + public OsdDockDrone setWindDirection(WindDirectionEnum windDirection) { + this.windDirection = windDirection; + return this; + } + + public Float getWindSpeed() { + return windSpeed; + } + + public OsdDockDrone setWindSpeed(Float windSpeed) { + this.windSpeed = windSpeed; + return this; + } + + public DronePositionState getPositionState() { + return positionState; + } + + public OsdDockDrone setPositionState(DronePositionState positionState) { + this.positionState = positionState; + return this; + } + + public List getPayloads() { + return payloads; + } + + public OsdDockDrone setPayloads(List payloads) { + this.payloads = payloads; + return this; + } + + public Storage getStorage() { + return storage; + } + + public OsdDockDrone setStorage(Storage storage) { + this.storage = storage; + return this; + } + + public SwitchActionEnum getNightLightsState() { + return nightLightsState; + } + + public OsdDockDrone setNightLightsState(SwitchActionEnum nightLightsState) { + this.nightLightsState = nightLightsState; + return this; + } + + public Integer getHeightLimit() { + return heightLimit; + } + + public OsdDockDrone setHeightLimit(Integer heightLimit) { + this.heightLimit = heightLimit; + return this; + } + + public DockDistanceLimitStatus getDistanceLimitStatus() { + return distanceLimitStatus; + } + + public OsdDockDrone setDistanceLimitStatus(DockDistanceLimitStatus distanceLimitStatus) { + this.distanceLimitStatus = distanceLimitStatus; + return this; + } + + public ObstacleAvoidance getObstacleAvoidance() { + return obstacleAvoidance; + } + + public OsdDockDrone setObstacleAvoidance(ObstacleAvoidance obstacleAvoidance) { + this.obstacleAvoidance = obstacleAvoidance; + return this; + } + + public Long getActivationTime() { + return activationTime; + } + + public OsdDockDrone setActivationTime(Long activationTime) { + this.activationTime = activationTime; + return this; + } + + public List getCameras() { + return cameras; + } + + public OsdDockDrone setCameras(List cameras) { + this.cameras = cameras; + return this; + } + + public RcLostActionEnum getRcLostAction() { + return rcLostAction; + } + + public OsdDockDrone setRcLostAction(RcLostActionEnum rcLostAction) { + this.rcLostAction = rcLostAction; + return this; + } + + public Integer getRthAltitude() { + return rthAltitude; + } + + public OsdDockDrone setRthAltitude(Integer rthAltitude) { + this.rthAltitude = rthAltitude; + return this; + } + + public Integer getTotalFlightSorties() { + return totalFlightSorties; + } + + public OsdDockDrone setTotalFlightSorties(Integer totalFlightSorties) { + this.totalFlightSorties = totalFlightSorties; + return this; + } + + public ExitWaylineWhenRcLostEnum getExitWaylineWhenRcLost() { + return exitWaylineWhenRcLost; + } + + public OsdDockDrone setExitWaylineWhenRcLost(ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost) { + this.exitWaylineWhenRcLost = exitWaylineWhenRcLost; + return this; + } + + public String getCountry() { + return country; + } + + public OsdDockDrone setCountry(String country) { + this.country = country; + return this; + } + + public Boolean getRidState() { + return ridState; + } + + public OsdDockDrone setRidState(Boolean ridState) { + this.ridState = ridState; + return this; + } + + public Boolean getNearAreaLimit() { + return nearAreaLimit; + } + + public OsdDockDrone setNearAreaLimit(Boolean nearAreaLimit) { + this.nearAreaLimit = nearAreaLimit; + return this; + } + + public Boolean getNearHeightLimit() { + return nearHeightLimit; + } + + public OsdDockDrone setNearHeightLimit(Boolean nearHeightLimit) { + this.nearHeightLimit = nearHeightLimit; + return this; + } + + public OsdDroneMaintainStatus getMaintainStatus() { + return maintainStatus; + } + + public OsdDockDrone setMaintainStatus(OsdDroneMaintainStatus maintainStatus) { + this.maintainStatus = maintainStatus; + return this; + } + + public String getTrackId() { + return trackId; + } + + public OsdDockDrone setTrackId(String trackId) { + this.trackId = trackId; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/OsdDockMaintainStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/OsdDockMaintainStatus.java new file mode 100644 index 0000000..65951b6 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/OsdDockMaintainStatus.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class OsdDockMaintainStatus { + + private List maintainStatusArray; + + public OsdDockMaintainStatus() { + } + + @Override + public String toString() { + return "OsdDroneMaintainStatus{" + + "maintainStatusArray=" + maintainStatusArray + + '}'; + } + + public List getMaintainStatusArray() { + return maintainStatusArray; + } + + public OsdDockMaintainStatus setMaintainStatusArray(List maintainStatusArray) { + this.maintainStatusArray = maintainStatusArray; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/OsdDroneMaintainStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/OsdDroneMaintainStatus.java new file mode 100644 index 0000000..91208d6 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/OsdDroneMaintainStatus.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class OsdDroneMaintainStatus { + + private List maintainStatusArray; + + public OsdDroneMaintainStatus() { + } + + @Override + public String toString() { + return "OsdDroneMaintainStatus{" + + "maintainStatusArray=" + maintainStatusArray + + '}'; + } + + public List getMaintainStatusArray() { + return maintainStatusArray; + } + + public OsdDroneMaintainStatus setMaintainStatusArray(List maintainStatusArray) { + this.maintainStatusArray = maintainStatusArray; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/OsdRcDrone.java b/src/main/java/com/dji/sdk/cloudapi/device/OsdRcDrone.java new file mode 100644 index 0000000..feb3127 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/OsdRcDrone.java @@ -0,0 +1,311 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/23 + */ +public class OsdRcDrone { + + private Float attitudeHead; + + private Double attitudePitch; + + private Double attitudeRoll; + + private Float elevation; + + private DroneBattery battery; + + private String firmwareVersion; + + private GearEnum gear; + + private Float height; + + private Float homeDistance; + + private Float horizontalSpeed; + + private Float latitude; + + private Float longitude; + + private DroneModeCodeEnum modeCode; + + private Double totalFlightDistance; + + private Float totalFlightTime; + + private Float verticalSpeed; + + private WindDirectionEnum windDirection; + + private Float windSpeed; + + private DronePositionState positionState; + + @JsonProperty(PayloadModelEnum.PAYLOAD_KEY) + private List payloads; + + private Storage storage; + + private Integer heightLimit; + + private RcDistanceLimitStatus distanceLimitStatus; + + private String trackId; + + public OsdRcDrone() { + } + + @Override + public String toString() { + return "OsdRcDrone{" + + "attitudeHead=" + attitudeHead + + ", attitudePitch=" + attitudePitch + + ", attitudeRoll=" + attitudeRoll + + ", elevation=" + elevation + + ", battery=" + battery + + ", firmwareVersion='" + firmwareVersion + '\'' + + ", gear=" + gear + + ", height=" + height + + ", homeDistance=" + homeDistance + + ", horizontalSpeed=" + horizontalSpeed + + ", latitude=" + latitude + + ", longitude=" + longitude + + ", modeCode=" + modeCode + + ", totalFlightDistance=" + totalFlightDistance + + ", totalFlightTime=" + totalFlightTime + + ", verticalSpeed=" + verticalSpeed + + ", windDirection=" + windDirection + + ", windSpeed=" + windSpeed + + ", positionState=" + positionState + + ", payloads=" + payloads + + ", storage=" + storage + + ", heightLimit=" + heightLimit + + ", distanceLimitStatus=" + distanceLimitStatus + + ", trackId='" + trackId + '\'' + + '}'; + } + + public Float getAttitudeHead() { + return attitudeHead; + } + + public OsdRcDrone setAttitudeHead(Float attitudeHead) { + this.attitudeHead = attitudeHead; + return this; + } + + public Double getAttitudePitch() { + return attitudePitch; + } + + public OsdRcDrone setAttitudePitch(Double attitudePitch) { + this.attitudePitch = attitudePitch; + return this; + } + + public Double getAttitudeRoll() { + return attitudeRoll; + } + + public OsdRcDrone setAttitudeRoll(Double attitudeRoll) { + this.attitudeRoll = attitudeRoll; + return this; + } + + public Float getElevation() { + return elevation; + } + + public OsdRcDrone setElevation(Float elevation) { + this.elevation = elevation; + return this; + } + + public DroneBattery getBattery() { + return battery; + } + + public OsdRcDrone setBattery(DroneBattery battery) { + this.battery = battery; + return this; + } + + public String getFirmwareVersion() { + return firmwareVersion; + } + + public OsdRcDrone setFirmwareVersion(String firmwareVersion) { + this.firmwareVersion = firmwareVersion; + return this; + } + + public GearEnum getGear() { + return gear; + } + + public OsdRcDrone setGear(GearEnum gear) { + this.gear = gear; + return this; + } + + public Float getHeight() { + return height; + } + + public OsdRcDrone setHeight(Float height) { + this.height = height; + return this; + } + + public Float getHomeDistance() { + return homeDistance; + } + + public OsdRcDrone setHomeDistance(Float homeDistance) { + this.homeDistance = homeDistance; + return this; + } + + public Float getHorizontalSpeed() { + return horizontalSpeed; + } + + public OsdRcDrone setHorizontalSpeed(Float horizontalSpeed) { + this.horizontalSpeed = horizontalSpeed; + return this; + } + + public Float getLatitude() { + return latitude; + } + + public OsdRcDrone setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public OsdRcDrone setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public DroneModeCodeEnum getModeCode() { + return modeCode; + } + + public OsdRcDrone setModeCode(DroneModeCodeEnum modeCode) { + this.modeCode = modeCode; + return this; + } + + public Double getTotalFlightDistance() { + return totalFlightDistance; + } + + public OsdRcDrone setTotalFlightDistance(Double totalFlightDistance) { + this.totalFlightDistance = totalFlightDistance; + return this; + } + + public Float getTotalFlightTime() { + return totalFlightTime; + } + + public OsdRcDrone setTotalFlightTime(Float totalFlightTime) { + this.totalFlightTime = totalFlightTime; + return this; + } + + public Float getVerticalSpeed() { + return verticalSpeed; + } + + public OsdRcDrone setVerticalSpeed(Float verticalSpeed) { + this.verticalSpeed = verticalSpeed; + return this; + } + + public WindDirectionEnum getWindDirection() { + return windDirection; + } + + public OsdRcDrone setWindDirection(WindDirectionEnum windDirection) { + this.windDirection = windDirection; + return this; + } + + public Float getWindSpeed() { + return windSpeed; + } + + public OsdRcDrone setWindSpeed(Float windSpeed) { + this.windSpeed = windSpeed; + return this; + } + + public DronePositionState getPositionState() { + return positionState; + } + + public OsdRcDrone setPositionState(DronePositionState positionState) { + this.positionState = positionState; + return this; + } + + public List getPayloads() { + return payloads; + } + + public OsdRcDrone setPayloads(List payloads) { + this.payloads = payloads; + return this; + } + + public Storage getStorage() { + return storage; + } + + public OsdRcDrone setStorage(Storage storage) { + this.storage = storage; + return this; + } + + public Integer getHeightLimit() { + return heightLimit; + } + + public OsdRcDrone setHeightLimit(Integer heightLimit) { + this.heightLimit = heightLimit; + return this; + } + + public RcDistanceLimitStatus getDistanceLimitStatus() { + return distanceLimitStatus; + } + + public OsdRcDrone setDistanceLimitStatus(RcDistanceLimitStatus distanceLimitStatus) { + this.distanceLimitStatus = distanceLimitStatus; + return this; + } + + public String getTrackId() { + return trackId; + } + + public OsdRcDrone setTrackId(String trackId) { + this.trackId = trackId; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/OsdRemoteControl.java b/src/main/java/com/dji/sdk/cloudapi/device/OsdRemoteControl.java new file mode 100644 index 0000000..5493751 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/OsdRemoteControl.java @@ -0,0 +1,78 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/23 + */ +public class OsdRemoteControl { + + private Float latitude; + + private Float longitude; + + private Float height; + + private Integer capacityPercent; + + private WirelessLink wirelessLink; + + public OsdRemoteControl() { + } + + @Override + public String toString() { + return "OsdRemoteControl{" + + "latitude=" + latitude + + ", longitude=" + longitude + + ", height=" + height + + ", capacityPercent=" + capacityPercent + + ", wirelessLink=" + wirelessLink + + '}'; + } + + public Float getLatitude() { + return latitude; + } + + public OsdRemoteControl setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public OsdRemoteControl setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public Float getHeight() { + return height; + } + + public OsdRemoteControl setHeight(Float height) { + this.height = height; + return this; + } + + public Integer getCapacityPercent() { + return capacityPercent; + } + + public OsdRemoteControl setCapacityPercent(Integer capacityPercent) { + this.capacityPercent = capacityPercent; + return this; + } + + public WirelessLink getWirelessLink() { + return wirelessLink; + } + + public OsdRemoteControl setWirelessLink(WirelessLink wirelessLink) { + this.wirelessLink = wirelessLink; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/PayloadFirmwareVersion.java b/src/main/java/com/dji/sdk/cloudapi/device/PayloadFirmwareVersion.java new file mode 100644 index 0000000..9fac3a1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/PayloadFirmwareVersion.java @@ -0,0 +1,53 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonCreator; + +import java.util.Map; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/28 + */ +public class PayloadFirmwareVersion { + + private PayloadPositionEnum position; + + private String firmwareVersion; + + public PayloadFirmwareVersion() { + } + + @JsonCreator + public PayloadFirmwareVersion(Map map) { + Map.Entry entry = (Map.Entry) map.entrySet().toArray()[0]; + this.position = PayloadPositionEnum.find(Integer.parseInt(entry.getKey().split("-")[1])); + this.firmwareVersion = ((Map) entry.getValue()).values().toArray(String[]::new)[0]; + } + + @Override + public String toString() { + return "PayloadFirmwareVersion{" + + "position=" + position + + ", firmwareVersion='" + firmwareVersion + '\'' + + '}'; + } + + public PayloadPositionEnum getPosition() { + return position; + } + + public PayloadFirmwareVersion setPosition(PayloadPositionEnum position) { + this.position = position; + return this; + } + + public String getFirmwareVersion() { + return firmwareVersion; + } + + public PayloadFirmwareVersion setFirmwareVersion(String firmwareVersion) { + this.firmwareVersion = firmwareVersion; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/PayloadIndex.java b/src/main/java/com/dji/sdk/cloudapi/device/PayloadIndex.java new file mode 100644 index 0000000..c3c842f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/PayloadIndex.java @@ -0,0 +1,75 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.Objects; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public class PayloadIndex { + + @NotNull + private DeviceTypeEnum type; + + @NotNull + private DeviceSubTypeEnum subType; + + @NotNull + private PayloadPositionEnum position; + + public PayloadIndex() { + } + + @JsonCreator + public PayloadIndex(String payloadIndex) { + Objects.requireNonNull(payloadIndex); + int[] payloadIndexArr = Arrays.stream(payloadIndex.split("-")).mapToInt(Integer::parseInt).toArray(); + if (payloadIndexArr.length != 3) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + this.type = DeviceTypeEnum.find(payloadIndexArr[0]); + this.subType = DeviceSubTypeEnum.find(payloadIndexArr[1]); + this.position = PayloadPositionEnum.find(payloadIndexArr[2]); + } + + @Override + @JsonValue + public String toString() { + return String.format("%s-%s-%s", type.getType(), subType.getSubType(), position.getPosition()); + } + + public DeviceTypeEnum getType() { + return type; + } + + public PayloadIndex setType(DeviceTypeEnum type) { + this.type = type; + return this; + } + + public DeviceSubTypeEnum getSubType() { + return subType; + } + + public PayloadIndex setSubType(DeviceSubTypeEnum subType) { + this.subType = subType; + return this; + } + + public PayloadPositionEnum getPosition() { + return position; + } + + public PayloadIndex setPosition(PayloadPositionEnum position) { + this.position = position; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/PayloadModelEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/PayloadModelEnum.java new file mode 100644 index 0000000..e1c86c1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/PayloadModelEnum.java @@ -0,0 +1,77 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Arrays; +import java.util.Set; +import java.util.function.Function; +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"); + + public static final String PAYLOAD_KEY = "payload"; + + private final String model; + + private final 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 Set getAllModelWithPosition() { + Set position = Arrays.stream(PayloadPositionEnum.values()).map(PayloadPositionEnum::getPosition) + .map(String::valueOf).collect(Collectors.toSet()); + return Arrays.stream(values()).map(PayloadModelEnum::getModel).map(m -> position.stream().map(p -> m.concat("-").concat(p))) + .flatMap(Function.identity()).collect(Collectors.toSet()); + } + + public static Set getAllIndexWithPosition() { + Set position = Arrays.stream(PayloadPositionEnum.values()).map(PayloadPositionEnum::getPosition) + .map(String::valueOf).collect(Collectors.toSet()); + return Arrays.stream(values()).map(PayloadModelEnum::getIndex).map(m -> position.stream().map(p -> m.concat("-").concat(p))) + .flatMap(Function.identity()).collect(Collectors.toSet()); + } + + public static PayloadModelEnum find(String model) { + return Arrays.stream(values()).filter(modelEnum -> modelEnum.model.equals(model)).findAny() + .orElseThrow(() -> new CloudSDKException(PayloadModelEnum.class, model)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/PayloadPositionEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/PayloadPositionEnum.java new file mode 100644 index 0000000..a4bbab9 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/PayloadPositionEnum.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/25 + */ +public enum PayloadPositionEnum { + + FRONT_LEFT(0), + + FRONT_RIGHT(1), + + TOP(2), + + FPV(7); + + private final int position; + + PayloadPositionEnum(int position) { + this.position = position; + } + + @JsonValue + public int getPosition() { + return position; + } + + @JsonCreator + public static PayloadPositionEnum find(int position) { + return Arrays.stream(values()).filter(positionEnum -> positionEnum.position == position).findAny() + .orElseThrow(() -> new CloudSDKException(PayloadPositionEnum.class, position)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/PositionFixedEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/PositionFixedEnum.java new file mode 100644 index 0000000..7761bdb --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/PositionFixedEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum PositionFixedEnum { + + NOT_START(0), + + FIXING(1), + + SUCCESSFUL(2), + + FAILED(3), + ; + + private final int fixed; + + PositionFixedEnum(int fixed) { + this.fixed = fixed; + } + + @JsonValue + public int getFixed() { + return fixed; + } + + @JsonCreator + public static PositionFixedEnum find(int fixed) { + return Arrays.stream(values()).filter(fixedEnum -> fixedEnum.fixed == fixed).findAny() + .orElseThrow(() -> new CloudSDKException(PositionFixedEnum.class, fixed)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/PutterStateEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/PutterStateEnum.java new file mode 100644 index 0000000..6ce47dc --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/PutterStateEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum PutterStateEnum { + + CLOSED(0), + + OPENED(1), + + HALF_OPEN(2), + + ABNORMAL(3), + ; + + private final int state; + + PutterStateEnum(int state) { + this.state = state; + } + + @JsonValue + public int getState() { + return state; + } + + @JsonCreator + public static PutterStateEnum find(int state) { + return Arrays.stream(values()).filter(stateEnum -> stateEnum.state == state).findAny() + .orElseThrow(() -> new CloudSDKException(PutterStateEnum.class, state)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RainfallEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/RainfallEnum.java new file mode 100644 index 0000000..7d46b8e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RainfallEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum RainfallEnum { + + NO(0), + + LIGHT(1), + + MODERATE(2), + + HEAVY(3), + ; + + private final int rain; + + RainfallEnum(int rain) { + this.rain = rain; + } + + @JsonValue + public int getRain() { + return rain; + } + + @JsonCreator + public static RainfallEnum find(int rain) { + return Arrays.stream(values()).filter(rainEnum -> rainEnum.rain == rain).findAny() + .orElseThrow(() -> new CloudSDKException(RainfallEnum.class, rain)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RcDistanceLimitStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/RcDistanceLimitStatus.java new file mode 100644 index 0000000..e84edbb --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RcDistanceLimitStatus.java @@ -0,0 +1,43 @@ +package com.dji.sdk.cloudapi.device; + +/** + * The state of the drone's limited distance + * @author sean + * @version 1.3 + * @date 2022/10/27 + */ +public class RcDistanceLimitStatus { + + private Integer state; + + private Integer distanceLimit; + + public RcDistanceLimitStatus() { + } + + @Override + public String toString() { + return "RcDistanceLimitStatusSet{" + + "state=" + state + + ", distanceLimit=" + distanceLimit + + '}'; + } + + public Integer getState() { + return state; + } + + public RcDistanceLimitStatus setState(Integer state) { + this.state = state; + return this; + } + + public Integer getDistanceLimit() { + return distanceLimit; + } + + public RcDistanceLimitStatus setDistanceLimit(Integer distanceLimit) { + this.distanceLimit = distanceLimit; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RcDroneControlSource.java b/src/main/java/com/dji/sdk/cloudapi/device/RcDroneControlSource.java new file mode 100644 index 0000000..6939ac3 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RcDroneControlSource.java @@ -0,0 +1,92 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class RcDroneControlSource { + + private ControlSourceEnum controlSource; + + private Float homeLatitude; + + private Float homeLongitude; + + private Integer lowBatteryWarningThreshold; + + private Integer seriousLowBatteryWarningThreshold; + + private List payloads; + + public RcDroneControlSource() { + } + + @Override + public String toString() { + return "RcDroneControlSource{" + + "controlSource=" + controlSource + + ", homeLatitude=" + homeLatitude + + ", homeLongitude=" + homeLongitude + + ", lowBatteryWarningThreshold=" + lowBatteryWarningThreshold + + ", seriousLowBatteryWarningThreshold=" + seriousLowBatteryWarningThreshold + + ", payloads=" + payloads + + '}'; + } + + public ControlSourceEnum getControlSource() { + return controlSource; + } + + public RcDroneControlSource setControlSource(ControlSourceEnum controlSource) { + this.controlSource = controlSource; + return this; + } + + public Float getHomeLatitude() { + return homeLatitude; + } + + public RcDroneControlSource setHomeLatitude(Float homeLatitude) { + this.homeLatitude = homeLatitude; + return this; + } + + public Float getHomeLongitude() { + return homeLongitude; + } + + public RcDroneControlSource setHomeLongitude(Float homeLongitude) { + this.homeLongitude = homeLongitude; + return this; + } + + public Integer getLowBatteryWarningThreshold() { + return lowBatteryWarningThreshold; + } + + public RcDroneControlSource setLowBatteryWarningThreshold(Integer lowBatteryWarningThreshold) { + this.lowBatteryWarningThreshold = lowBatteryWarningThreshold; + return this; + } + + public Integer getSeriousLowBatteryWarningThreshold() { + return seriousLowBatteryWarningThreshold; + } + + public RcDroneControlSource setSeriousLowBatteryWarningThreshold(Integer seriousLowBatteryWarningThreshold) { + this.seriousLowBatteryWarningThreshold = seriousLowBatteryWarningThreshold; + return this; + } + + public List getPayloads() { + return payloads; + } + + public RcDroneControlSource setPayloads(List payloads) { + this.payloads = payloads; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RcDronePayload.java b/src/main/java/com/dji/sdk/cloudapi/device/RcDronePayload.java new file mode 100644 index 0000000..30610ff --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RcDronePayload.java @@ -0,0 +1,140 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/6 + */ +public class RcDronePayload { + + private PayloadIndex payloadIndex; + + private Float gimbalPitch; + + private Float gimbalRoll; + + private Float gimbalYaw; + + private Float measureTargetAltitude; + + private Float measureTargetDistance; + + private Float measureTargetLatitude; + + private Float measureTargetLongitude; + + private MeasureTargetStateEnum measureTargetErrorState; + + private List smartTrackPoint; + + public RcDronePayload() { + } + + @Override + public String toString() { + return "RcDronePayload{" + + "payloadIndex=" + payloadIndex + + ", gimbalPitch=" + gimbalPitch + + ", gimbalRoll=" + gimbalRoll + + ", gimbalYaw=" + gimbalYaw + + ", measureTargetAltitude=" + measureTargetAltitude + + ", measureTargetDistance=" + measureTargetDistance + + ", measureTargetLatitude=" + measureTargetLatitude + + ", measureTargetLongitude=" + measureTargetLongitude + + ", measureTargetErrorState=" + measureTargetErrorState + + ", smartTrackPoint=" + smartTrackPoint + + '}'; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public RcDronePayload setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public Float getGimbalPitch() { + return gimbalPitch; + } + + public RcDronePayload setGimbalPitch(Float gimbalPitch) { + this.gimbalPitch = gimbalPitch; + return this; + } + + public Float getGimbalRoll() { + return gimbalRoll; + } + + public RcDronePayload setGimbalRoll(Float gimbalRoll) { + this.gimbalRoll = gimbalRoll; + return this; + } + + public Float getGimbalYaw() { + return gimbalYaw; + } + + public RcDronePayload setGimbalYaw(Float gimbalYaw) { + this.gimbalYaw = gimbalYaw; + return this; + } + + public Float getMeasureTargetAltitude() { + return measureTargetAltitude; + } + + public RcDronePayload setMeasureTargetAltitude(Float measureTargetAltitude) { + this.measureTargetAltitude = measureTargetAltitude; + return this; + } + + public Float getMeasureTargetDistance() { + return measureTargetDistance; + } + + public RcDronePayload setMeasureTargetDistance(Float measureTargetDistance) { + this.measureTargetDistance = measureTargetDistance; + return this; + } + + public Float getMeasureTargetLatitude() { + return measureTargetLatitude; + } + + public RcDronePayload setMeasureTargetLatitude(Float measureTargetLatitude) { + this.measureTargetLatitude = measureTargetLatitude; + return this; + } + + public Float getMeasureTargetLongitude() { + return measureTargetLongitude; + } + + public RcDronePayload setMeasureTargetLongitude(Float measureTargetLongitude) { + this.measureTargetLongitude = measureTargetLongitude; + return this; + } + + public MeasureTargetStateEnum getMeasureTargetErrorState() { + return measureTargetErrorState; + } + + public RcDronePayload setMeasureTargetErrorState(MeasureTargetStateEnum measureTargetErrorState) { + this.measureTargetErrorState = measureTargetErrorState; + return this; + } + + public List getSmartTrackPoint() { + return smartTrackPoint; + } + + public RcDronePayload setSmartTrackPoint(List smartTrackPoint) { + this.smartTrackPoint = smartTrackPoint; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RcFirmwareVersion.java b/src/main/java/com/dji/sdk/cloudapi/device/RcFirmwareVersion.java new file mode 100644 index 0000000..f9f3a19 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RcFirmwareVersion.java @@ -0,0 +1,31 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.0 + * @date 2022/4/28 + */ +public class RcFirmwareVersion { + + private String firmwareVersion; + + public RcFirmwareVersion() { + } + + @Override + public String toString() { + return "RcFirmwareVersion{" + + "firmwareVersion='" + firmwareVersion + '\'' + + '}'; + } + + public String getFirmwareVersion() { + return firmwareVersion; + } + + public RcFirmwareVersion setFirmwareVersion(String firmwareVersion) { + this.firmwareVersion = firmwareVersion; + return this; + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RcLiveStatus.java b/src/main/java/com/dji/sdk/cloudapi/device/RcLiveStatus.java new file mode 100644 index 0000000..19a4262 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RcLiveStatus.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.device; + +import java.util.List; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/23 + */ +public class RcLiveStatus { + + private List liveStatus; + + public RcLiveStatus() { + } + + @Override + public String toString() { + return "RcLiveStatus{" + + "liveStatus=" + liveStatus + + '}'; + } + + public List getLiveStatus() { + return liveStatus; + } + + public RcLiveStatus setLiveStatus(List liveStatus) { + this.liveStatus = liveStatus; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RcLiveStatusData.java b/src/main/java/com/dji/sdk/cloudapi/device/RcLiveStatusData.java new file mode 100644 index 0000000..05c779a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RcLiveStatusData.java @@ -0,0 +1,56 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.cloudapi.livestream.VideoQualityEnum; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/23 + */ +public class RcLiveStatusData { + + private Boolean status; + + private VideoId videoId; + + private VideoQualityEnum videoQuality; + + public RcLiveStatusData() { + } + + @Override + public String toString() { + return "RcLiveStatusData{" + + "status=" + status + + ", videoId=" + videoId + + ", videoQuality=" + videoQuality + + '}'; + } + + public Boolean getStatus() { + return status; + } + + public RcLiveStatusData setStatus(Boolean status) { + this.status = status; + return this; + } + + public VideoId getVideoId() { + return videoId; + } + + public RcLiveStatusData setVideoId(VideoId videoId) { + this.videoId = videoId; + return this; + } + + public VideoQualityEnum getVideoQuality() { + return videoQuality; + } + + public RcLiveStatusData setVideoQuality(VideoQualityEnum videoQuality) { + this.videoQuality = videoQuality; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RcLostActionEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/RcLostActionEnum.java new file mode 100644 index 0000000..04f1c08 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RcLostActionEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/1 + */ +public enum RcLostActionEnum { + + HOVER(0), + + LAND(1), + + RETURN_HOME(2); + + private final int action; + + RcLostActionEnum(int action) { + this.action = action; + } + + @JsonValue + public int getAction() { + return action; + } + + @JsonCreator + public static RcLostActionEnum find(int action) { + return Arrays.stream(values()).filter(actionEnum -> actionEnum.ordinal() == action).findAny() + .orElseThrow(() -> new CloudSDKException(RcLostActionEnum.class, action)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/RcPayloadControlSource.java b/src/main/java/com/dji/sdk/cloudapi/device/RcPayloadControlSource.java new file mode 100644 index 0000000..7eed484 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/RcPayloadControlSource.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class RcPayloadControlSource { + + private ControlSourceEnum controlSource; + + private PayloadIndex payloadIndex; + + private String sn; + + private String firmwareVersion; + + public RcPayloadControlSource() { + } + + @Override + public String toString() { + return "RcPayloadControlSource{" + + "controlSource=" + controlSource + + ", payloadIndex=" + payloadIndex + + ", sn='" + sn + '\'' + + ", firmwareVersion='" + firmwareVersion + '\'' + + '}'; + } + + public ControlSourceEnum getControlSource() { + return controlSource; + } + + public RcPayloadControlSource setControlSource(ControlSourceEnum controlSource) { + this.controlSource = controlSource; + return this; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public RcPayloadControlSource setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public String getSn() { + return sn; + } + + public RcPayloadControlSource setSn(String sn) { + this.sn = sn; + return this; + } + + public String getFirmwareVersion() { + return firmwareVersion; + } + + public RcPayloadControlSource setFirmwareVersion(String firmwareVersion) { + this.firmwareVersion = firmwareVersion; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/device/SmartTrackPoint.java b/src/main/java/com/dji/sdk/cloudapi/device/SmartTrackPoint.java new file mode 100644 index 0000000..acb7517 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/SmartTrackPoint.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class SmartTrackPoint { + + private TrackTargetModeEnum trackTargetMode; + + private Float trackLatitude; + + private Float trackLongitude; + + private Float trackAltitude; + + public SmartTrackPoint() { + } + + @Override + public String toString() { + return "SmartTrackPoint{" + + "trackTargetMode=" + trackTargetMode + + ", trackLatitude=" + trackLatitude + + ", trackLongitude=" + trackLongitude + + ", trackAltitude=" + trackAltitude + + '}'; + } + + public TrackTargetModeEnum getTrackTargetMode() { + return trackTargetMode; + } + + public SmartTrackPoint setTrackTargetMode(TrackTargetModeEnum trackTargetMode) { + this.trackTargetMode = trackTargetMode; + return this; + } + + public Float getTrackLatitude() { + return trackLatitude; + } + + public SmartTrackPoint setTrackLatitude(Float trackLatitude) { + this.trackLatitude = trackLatitude; + return this; + } + + public Float getTrackLongitude() { + return trackLongitude; + } + + public SmartTrackPoint setTrackLongitude(Float trackLongitude) { + this.trackLongitude = trackLongitude; + return this; + } + + public Float getTrackAltitude() { + return trackAltitude; + } + + public SmartTrackPoint setTrackAltitude(Float trackAltitude) { + this.trackAltitude = trackAltitude; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/Storage.java b/src/main/java/com/dji/sdk/cloudapi/device/Storage.java new file mode 100644 index 0000000..a6575db --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/Storage.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/11 + */ +public class Storage { + + private Long total; + + private Long used; + + public Storage() { + } + + @Override + public String toString() { + return "Storage{" + + "total=" + total + + ", used=" + used + + '}'; + } + + public Long getTotal() { + return total; + } + + public Storage setTotal(Long total) { + this.total = total; + return this; + } + + public Long getUsed() { + return used; + } + + public Storage setUsed(Long used) { + this.used = used; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/SwitchActionEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/SwitchActionEnum.java new file mode 100644 index 0000000..ee36d80 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/SwitchActionEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/10/28 + */ +public enum SwitchActionEnum { + + DISABLE(0), + + ENABLE(1); + + private final int action; + + SwitchActionEnum(int action) { + this.action = action; + } + + @JsonValue + public int getAction() { + return action; + } + + @JsonCreator + public static SwitchActionEnum find(int action) { + return Arrays.stream(values()).filter(actionEnum -> actionEnum.action == action).findAny() + .orElseThrow(() -> new CloudSDKException(SwitchActionEnum.class, action)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/ThermalGainModeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/ThermalGainModeEnum.java new file mode 100644 index 0000000..0bd3a8e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/ThermalGainModeEnum.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum ThermalGainModeEnum { + + AUTOMATIC(0), + + LOW(1), + + HIGH(2), + ; + + private final int mode; + + ThermalGainModeEnum(int mode) { + this.mode = mode; + } + + @JsonValue + public int getMode() { + return mode; + } + + @JsonCreator + public static ThermalGainModeEnum find(int mode) { + return Arrays.stream(values()).filter(modeEnum -> modeEnum.mode == mode).findAny() + .orElseThrow(() -> new CloudSDKException(ThermalGainModeEnum.class, mode)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/ThermalPaletteStyleEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/ThermalPaletteStyleEnum.java new file mode 100644 index 0000000..b63c6eb --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/ThermalPaletteStyleEnum.java @@ -0,0 +1,72 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum ThermalPaletteStyleEnum { + + WHITE_HOT(0), + + BLACK_HOT(1), + + RED_HOT(2), + + GREEN_HOT(3), + + FUSION(4), + + RAINBOW(5), + + IRONBOW1(6), + + IRONBOW2(7), + + ICE_FIRE(8), + + SEPIA(9), + + GLOWBOW(10), + + COLOR1(11), + + COLOR2(12), + + RAIN(13), + + HOT_SPOT(14), + + RAINBOW2(15), + + GRAY(16), + + METAL(17), + + COLD_SPOT(18), + ; + + private final int style; + + ThermalPaletteStyleEnum(int style) { + this.style = style; + } + + @JsonValue + public int getStyle() { + return style; + } + + @JsonCreator + public static ThermalPaletteStyleEnum find(int style) { + return Arrays.stream(values()).filter(styleEnum -> styleEnum.style == style).findAny() + .orElseThrow(() -> new CloudSDKException(ThermalPaletteStyleEnum.class, style)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/TrackTargetModeEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/TrackTargetModeEnum.java new file mode 100644 index 0000000..9dbeb0c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/TrackTargetModeEnum.java @@ -0,0 +1,39 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum TrackTargetModeEnum { + + NORMAL(1), + + LOW_CREDIBILITY(2), + + PREDICTED(3), + ; + private final int mode; + + TrackTargetModeEnum(int mode) { + this.mode = mode; + } + + @JsonValue + public int getMode() { + return mode; + } + + @JsonCreator + public static TrackTargetModeEnum find(int mode) { + return Arrays.stream(values()).filter(modeEnum -> modeEnum.mode == mode).findAny() + .orElseThrow(() -> new CloudSDKException(TrackTargetModeEnum.class, mode)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/UpdateTopo.java b/src/main/java/com/dji/sdk/cloudapi/device/UpdateTopo.java new file mode 100644 index 0000000..eb9feaf --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/UpdateTopo.java @@ -0,0 +1,105 @@ +package com.dji.sdk.cloudapi.device; + + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/26 + */ +public class UpdateTopo { + + private DeviceDomainEnum domain; + + private DeviceTypeEnum type; + + private DeviceSubTypeEnum subType; + + private String deviceSecret; + + private String nonce; + + private String thingVersion; + + private List subDevices; + + public UpdateTopo() { + } + + @Override + public String toString() { + return "UpdateTopo{" + + "domain=" + domain + + ", type=" + type + + ", subType=" + subType + + ", deviceSecret='" + deviceSecret + '\'' + + ", nonce='" + nonce + '\'' + + ", thingVersion=" + thingVersion + + ", subDevices=" + subDevices + + '}'; + } + + public DeviceDomainEnum getDomain() { + return domain; + } + + public UpdateTopo setDomain(DeviceDomainEnum domain) { + this.domain = domain; + return this; + } + + public DeviceTypeEnum getType() { + return type; + } + + public UpdateTopo setType(DeviceTypeEnum type) { + this.type = type; + return this; + } + + public DeviceSubTypeEnum getSubType() { + return subType; + } + + public UpdateTopo setSubType(DeviceSubTypeEnum subType) { + this.subType = subType; + return this; + } + + public String getDeviceSecret() { + return deviceSecret; + } + + public UpdateTopo setDeviceSecret(String deviceSecret) { + this.deviceSecret = deviceSecret; + return this; + } + + public String getNonce() { + return nonce; + } + + public UpdateTopo setNonce(String nonce) { + this.nonce = nonce; + return this; + } + + public String getThingVersion() { + return thingVersion; + } + + public UpdateTopo setThingVersion(String thingVersion) { + this.thingVersion = thingVersion; + return this; + } + + public List getSubDevices() { + return subDevices; + } + + public UpdateTopo setSubDevices(List subDevices) { + this.subDevices = subDevices; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/UpdateTopoSubDevice.java b/src/main/java/com/dji/sdk/cloudapi/device/UpdateTopoSubDevice.java new file mode 100644 index 0000000..4ba2734 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/UpdateTopoSubDevice.java @@ -0,0 +1,114 @@ +package com.dji.sdk.cloudapi.device; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/26 + */ +public class UpdateTopoSubDevice { + + private String sn; + + private DeviceDomainEnum domain; + + private DeviceTypeEnum type; + + private DeviceSubTypeEnum subType; + + private ControlSourceEnum index; + + private String deviceSecret; + + private String nonce; + + private String thingVersion; + + public UpdateTopoSubDevice() { + } + + @Override + public String toString() { + return "UpdateTopoSubDevice{" + + "sn='" + sn + '\'' + + ", domain=" + domain + + ", type=" + type + + ", subType=" + subType + + ", index=" + index + + ", deviceSecret='" + deviceSecret + '\'' + + ", nonce='" + nonce + '\'' + + ", thingVersion=" + thingVersion + + '}'; + } + + public String getSn() { + return sn; + } + + public UpdateTopoSubDevice setSn(String sn) { + this.sn = sn; + return this; + } + + public DeviceDomainEnum getDomain() { + return domain; + } + + public UpdateTopoSubDevice setDomain(DeviceDomainEnum domain) { + this.domain = domain; + return this; + } + + public DeviceTypeEnum getType() { + return type; + } + + public UpdateTopoSubDevice setType(DeviceTypeEnum type) { + this.type = type; + return this; + } + + public DeviceSubTypeEnum getSubType() { + return subType; + } + + public UpdateTopoSubDevice setSubType(DeviceSubTypeEnum subType) { + this.subType = subType; + return this; + } + + public ControlSourceEnum getIndex() { + return index; + } + + public UpdateTopoSubDevice setIndex(ControlSourceEnum index) { + this.index = index; + return this; + } + + public String getDeviceSecret() { + return deviceSecret; + } + + public UpdateTopoSubDevice setDeviceSecret(String deviceSecret) { + this.deviceSecret = deviceSecret; + return this; + } + + public String getNonce() { + return nonce; + } + + public UpdateTopoSubDevice setNonce(String nonce) { + this.nonce = nonce; + return this; + } + + public String getThingVersion() { + return thingVersion; + } + + public UpdateTopoSubDevice setThingVersion(String thingVersion) { + this.thingVersion = thingVersion; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/VideoId.java b/src/main/java/com/dji/sdk/cloudapi/device/VideoId.java new file mode 100644 index 0000000..a61b77b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/VideoId.java @@ -0,0 +1,82 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.cloudapi.livestream.VideoTypeEnum; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import org.springframework.util.StringUtils; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.Objects; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/25 + */ +public class VideoId { + + @NotNull + private String droneSn; + + @NotNull + private PayloadIndex payloadIndex; + + @NotNull + private VideoTypeEnum videoType = VideoTypeEnum.NORMAL; + + public VideoId() { + } + + @JsonCreator + public VideoId(String videoId) { + if (!StringUtils.hasText(videoId)) { + return; + } + String[] videoIdArr = Arrays.stream(videoId.split("/")).toArray(String[]::new); + if (videoIdArr.length != 3) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + this.droneSn = videoIdArr[0]; + this.payloadIndex = new PayloadIndex(videoIdArr[1]); + this.videoType = VideoTypeEnum.find(videoIdArr[2].split("-")[0]); + } + + @Override + @JsonValue + public String toString() { + if (Objects.isNull(payloadIndex)) { + return ""; + } + return String.format("%s/%s/%s-0", droneSn, payloadIndex.toString(), videoType.getType()); + } + + public String getDroneSn() { + return droneSn; + } + + public VideoId setDroneSn(String droneSn) { + this.droneSn = droneSn; + return this; + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public VideoId setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public VideoTypeEnum getVideoType() { + return videoType; + } + + public VideoId setVideoType(VideoTypeEnum videoType) { + this.videoType = videoType; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/WindDirectionEnum.java b/src/main/java/com/dji/sdk/cloudapi/device/WindDirectionEnum.java new file mode 100644 index 0000000..18ddf94 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/WindDirectionEnum.java @@ -0,0 +1,52 @@ +package com.dji.sdk.cloudapi.device; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public enum WindDirectionEnum { + + NO(0), + + NORTH(1), + + NORTHEAST(2), + + EAST(3), + + SOUTHEAST(4), + + SOUTH(5), + + SOUTHWEST(6), + + WEST(7), + + NORTHWEST(8), + ; + + private final int direction; + + WindDirectionEnum(int direction) { + this.direction = direction; + } + + @JsonValue + public int getDirection() { + return direction; + } + + @JsonCreator + public static WindDirectionEnum find(int direction) { + return Arrays.stream(values()).filter(directionEnum -> directionEnum.direction == direction).findAny() + .orElseThrow(() -> new CloudSDKException(WindDirectionEnum.class, direction)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/WirelessLink.java b/src/main/java/com/dji/sdk/cloudapi/device/WirelessLink.java new file mode 100644 index 0000000..8938821 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/WirelessLink.java @@ -0,0 +1,145 @@ +package com.dji.sdk.cloudapi.device; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/3 + */ +public class WirelessLink { + + @JsonProperty("4g_freq_band") + private Float fourthGenerationFreqBand; + + @JsonProperty("4g_gnd_quality") + private Integer fourthGenerationGndQuality; + + @JsonProperty("4g_link_state") + private Boolean fourthGenerationLinkState; + + @JsonProperty("4g_quality") + private Integer fourthGenerationQuality; + + @JsonProperty("4g_uav_quality") + private Integer fourthGenerationUavQuality; + + private Integer dongleNumber; + + private LinkWorkModeEnum linkWorkmode; + + private Float sdrFreqBand; + + private Boolean sdrLinkState; + + private Integer sdrQuality; + + public WirelessLink() { + } + + @Override + public String toString() { + return "WirelessLink{" + + "fourthGenerationFreqBand=" + fourthGenerationFreqBand + + ", fourthGenerationGndQuality=" + fourthGenerationGndQuality + + ", fourthGenerationLinkState=" + fourthGenerationLinkState + + ", fourthGenerationQuality=" + fourthGenerationQuality + + ", fourthGenerationUavQuality=" + fourthGenerationUavQuality + + ", dongleNumber=" + dongleNumber + + ", linkWorkmode=" + linkWorkmode + + ", sdrFreqBand=" + sdrFreqBand + + ", sdrLinkState=" + sdrLinkState + + ", sdrQuality=" + sdrQuality + + '}'; + } + + public Float getFourthGenerationFreqBand() { + return fourthGenerationFreqBand; + } + + public WirelessLink setFourthGenerationFreqBand(Float fourthGenerationFreqBand) { + this.fourthGenerationFreqBand = fourthGenerationFreqBand; + return this; + } + + public Integer getFourthGenerationGndQuality() { + return fourthGenerationGndQuality; + } + + public WirelessLink setFourthGenerationGndQuality(Integer fourthGenerationGndQuality) { + this.fourthGenerationGndQuality = fourthGenerationGndQuality; + return this; + } + + public Boolean getFourthGenerationLinkState() { + return fourthGenerationLinkState; + } + + public WirelessLink setFourthGenerationLinkState(Boolean fourthGenerationLinkState) { + this.fourthGenerationLinkState = fourthGenerationLinkState; + return this; + } + + public Integer getFourthGenerationQuality() { + return fourthGenerationQuality; + } + + public WirelessLink setFourthGenerationQuality(Integer fourthGenerationQuality) { + this.fourthGenerationQuality = fourthGenerationQuality; + return this; + } + + public Integer getFourthGenerationUavQuality() { + return fourthGenerationUavQuality; + } + + public WirelessLink setFourthGenerationUavQuality(Integer fourthGenerationUavQuality) { + this.fourthGenerationUavQuality = fourthGenerationUavQuality; + return this; + } + + public Integer getDongleNumber() { + return dongleNumber; + } + + public WirelessLink setDongleNumber(Integer dongleNumber) { + this.dongleNumber = dongleNumber; + return this; + } + + public LinkWorkModeEnum getLinkWorkmode() { + return linkWorkmode; + } + + public WirelessLink setLinkWorkmode(LinkWorkModeEnum linkWorkmode) { + this.linkWorkmode = linkWorkmode; + return this; + } + + public Float getSdrFreqBand() { + return sdrFreqBand; + } + + public WirelessLink setSdrFreqBand(Float sdrFreqBand) { + this.sdrFreqBand = sdrFreqBand; + return this; + } + + public Boolean getSdrLinkState() { + return sdrLinkState; + } + + public WirelessLink setSdrLinkState(Boolean sdrLinkState) { + this.sdrLinkState = sdrLinkState; + return this; + } + + public Integer getSdrQuality() { + return sdrQuality; + } + + public WirelessLink setSdrQuality(Integer sdrQuality) { + this.sdrQuality = sdrQuality; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/device/api/AbstractDeviceService.java b/src/main/java/com/dji/sdk/cloudapi/device/api/AbstractDeviceService.java new file mode 100644 index 0000000..b8b5352 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/device/api/AbstractDeviceService.java @@ -0,0 +1,177 @@ +package com.dji.sdk.cloudapi.device.api; + +import com.dji.sdk.cloudapi.device.*; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.osd.TopicOsdRequest; +import com.dji.sdk.mqtt.state.TopicStateRequest; +import com.dji.sdk.mqtt.status.TopicStatusRequest; +import com.dji.sdk.mqtt.status.TopicStatusResponse; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class AbstractDeviceService { + + /** + * osd dock + * @param request data + * @param headers The headers for a {@link Message}. + * @return status_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD_DOCK) + public void osdDock(TopicOsdRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("osdDock not implemented"); + } + + /** + * osd dock drone + * @param request data + * @param headers The headers for a {@link Message}. + * @return status_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD_DOCK_DRONE) + public void osdDockDrone(TopicOsdRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("osdDockDrone not implemented"); + } + + /** + * osd remote control + * @param request data + * @param headers The headers for a {@link Message}. + * @return status_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD_RC) + public void osdRemoteControl(TopicOsdRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("osdRemoteControl not implemented"); + } + + /** + * osd remote control drone + * @param request data + * @param headers The headers for a {@link Message}. + * @return status_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD_RC_DRONE) + public void osdRcDrone(TopicOsdRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("osdRcDrone not implemented"); + } + + /** + * Gateway device + sub device online + * @param request data + * @param headers The headers for a {@link Message}. + * @return status_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_ONLINE, outputChannel = ChannelName.OUTBOUND_STATUS) + public TopicStatusResponse updateTopoOnline(TopicStatusRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("updateTopoOnline not implemented"); + } + + /** + * Sub device offline + * @param request data + * @param headers The headers for a {@link Message}. + * @return status_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_OFFLINE, outputChannel = ChannelName.OUTBOUND_STATUS) + public TopicStatusResponse updateTopoOffline(TopicStatusRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("updateTopoOffline not implemented"); + } + + /** + * Firmware version update for dock and drone + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_FIRMWARE_VERSION) + public void dockFirmwareVersionUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("dockFirmwareVersionUpdate not implemented"); + } + + /** + * Firmware version update for remote control and drone + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_RC_FIRMWARE_VERSION) + public void rcFirmwareVersionUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("rcFirmwareVersionUpdate not implemented"); + } + + /** + * Drone control source update for dock and drone + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_CONTROL_SOURCE) + public void dockControlSourceUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("dockControlSourceUpdate not implemented"); + } + + /** + * Drone control source update for remote control and drone + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_RC_CONTROL_SOURCE) + public void rcControlSourceUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("rcControlSourceUpdate not implemented"); + } + + /** + * Live status update for dock and drone + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_LIVE_STATUS) + public void dockLiveStatusUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("dockLiveStatusUpdate not implemented"); + } + + /** + * Live status source update for remote control and drone + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_RC_LIVE_STATUS) + public void rcLiveStatusUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("rcLiveStatusUpdate not implemented"); + } + + /** + * Payload firmware version update for remote control and drone + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_RC_PAYLOAD_FIRMWARE) + public void rcPayloadFirmwareVersionUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("rcPayloadFirmwareVersionUpdate not implemented"); + } + + /** + * Wpmz firmware version update for drone + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_WPMZ_VERSION) + public void dockWpmzVersionUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("dockWpmzVersionUpdate not implemented"); + } + + /** + * Styles supported by the IR palette + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_WPMZ_VERSION) + public void dockPayload(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("dockPayload not implemented"); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareErrorCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareErrorCodeEnum.java new file mode 100644 index 0000000..957f4d5 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareErrorCodeEnum.java @@ -0,0 +1,97 @@ +package com.dji.sdk.cloudapi.firmware; + +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.mqtt.events.IEventsErrorCode; +import com.dji.sdk.mqtt.services.IServicesErrorCode; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public enum FirmwareErrorCodeEnum implements IServicesErrorCode, IEventsErrorCode, IErrorInfo { + + WRONG_TYPE(312001, "Consistency Upgrade was trrigered, but device didn't request."), + + READY_1_FAILED(312002, "Failed to upgrade. Please try again."), + + VALIDATION_FAILED(312003, "Failed to upgrade. Please try again."), + + READY_2_FAILED(312004, "Failed to upgrade. Please try again."), + + WRONG_PROTOCOL(312010, "The upgrade request is different from the API."), + + WRONG_PARAMETER(312012, "Please check the parameters and try again."), + + COMMAND_1_FAILED(312013, "Failed to upgrade. Please try again."), + + UPDATING(312014, "Updating device firmware. Wait until update completed."), + + WORKING(312015, "Device can not upgrade during the flight. Please wait and try again."), + + TRANSMISSION_ERROR(312016, "Update failed. Dock and aircraft transmission error. Restart dock and aircraft and try again."), + + VERSION_CHECK_FAILED(312017, "Failed to check the version."), + + COMMAND_2_FAILED(312018, "Failed to upgrade. Please try again."), + + COMMAND_3_FAILED(312019, "Failed to upgrade. Please try again."), + + COMMAND_4_FAILED(312020, "Failed to upgrade. Please try again."), + + COMMAND_5_FAILED(312021, "Failed to upgrade. Please try again."), + + AIRCRAFT_NOT_FOUND(312022, "Failed to power on aircraft, or aircraft not connected. Check if aircraft is inside dock, battery installed, and dock and aircraft linked."), + + AIRCRAFT_OUTSIDE(312023, "Failed to push driving rods back into place. Unable to update aircraft firmware. Check if emergency stop button is pressed down or driving rods are stuck."), + + COMMAND_6_FAILED(312024, "Failed to upgrade. Please try again."), + + DELETE_FAILED(312025, "Failed to delete old firmware package."), + + DECOMPRESSION_FAILED(312026, "Failed to decompress the offline upgrade package."), + + NO_AIRCRAFT_DETECTED(312027, "Failed to update firmware. Aircraft not detected inside dock."), + + DEVICE_RESTART_1(312028, "Failed to update firmware. Device restarted during update."), + + DEVICE_RESTART_2(312029, "Restarting device. Unable to update firmware."), + + FOURTH_GENERATION_IS_ENABLE(312030, "Aircraft enhanced transmission enabled. Failed to update firmware. Disable 4G transmission and try again."), + + LOW_POWER(312704, "Aircraft battery level too low. Wait until aircraft is charged to above 20% and try again."), + + UNKNOWN(-1, "UNKNOWN"), + ; + + + private final String msg; + + private final int code; + + FirmwareErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String getMessage() { + return this.msg; + } + + @Override + public Integer getCode() { + return this.code; + } + + /** + * @param code error code + * @return enumeration object + */ + public static FirmwareErrorCodeEnum find(int code) { + return Arrays.stream(values()).filter(codeEnum -> codeEnum.code == code).findAny().orElse(UNKNOWN); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareMethodEnum.java b/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareMethodEnum.java new file mode 100644 index 0000000..5c0370c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareMethodEnum.java @@ -0,0 +1,24 @@ +package com.dji.sdk.cloudapi.firmware; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +public enum FirmwareMethodEnum { + + OTA_CREATE("ota_create"); + + private final String method; + + FirmwareMethodEnum(String method) { + this.method = method; + } + + @JsonValue + public String getMethod() { + return method; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareUpgradeTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareUpgradeTypeEnum.java new file mode 100644 index 0000000..a5dc5e7 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/FirmwareUpgradeTypeEnum.java @@ -0,0 +1,44 @@ +package com.dji.sdk.cloudapi.firmware; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.2 + * @date 2022/8/15 + */ +public enum FirmwareUpgradeTypeEnum { + + /** + * to upgraded + */ + NORMAL_UPGRADE(2), + + /** + * A consistency upgrade is required. + */ + CONSISTENT_UPGRADE(3); + + private final int type; + + FirmwareUpgradeTypeEnum(int type) { + this.type = type; + } + + @JsonValue + public int getType() { + return type; + } + + @JsonCreator + public static FirmwareUpgradeTypeEnum find(int type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(FirmwareUpgradeTypeEnum.class, type)); + + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateDevice.java b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateDevice.java new file mode 100644 index 0000000..bef65e7 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateDevice.java @@ -0,0 +1,113 @@ +package com.dji.sdk.cloudapi.firmware; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.2 + * @date 2022/8/16 + */ +public class OtaCreateDevice { + + @NotNull + private String sn; + + @NotNull + @Pattern(regexp = "^\\d{2}\\.\\d{2}\\.\\d{4}$") + private String productVersion; + + @NotNull + private String fileUrl; + + @NotNull + private String md5; + + @NotNull + private Long fileSize; + + @NotNull + private FirmwareUpgradeTypeEnum firmwareUpgradeType; + + @NotNull + private String fileName; + + public OtaCreateDevice() { + } + + @Override + public String toString() { + return "OtaCreateDevice{" + + "sn='" + sn + '\'' + + ", productVersion='" + productVersion + '\'' + + ", fileUrl='" + fileUrl + '\'' + + ", md5='" + md5 + '\'' + + ", fileSize=" + fileSize + + ", firmwareUpgradeType=" + firmwareUpgradeType + + ", fileName='" + fileName + '\'' + + '}'; + } + + public String getSn() { + return sn; + } + + public OtaCreateDevice setSn(String sn) { + this.sn = sn; + return this; + } + + public String getProductVersion() { + return productVersion; + } + + public OtaCreateDevice setProductVersion(String productVersion) { + this.productVersion = productVersion; + return this; + } + + public String getFileUrl() { + return fileUrl; + } + + public OtaCreateDevice setFileUrl(String fileUrl) { + this.fileUrl = fileUrl; + return this; + } + + public String getMd5() { + return md5; + } + + public OtaCreateDevice setMd5(String md5) { + this.md5 = md5; + return this; + } + + public Long getFileSize() { + return fileSize; + } + + public OtaCreateDevice setFileSize(Long fileSize) { + this.fileSize = fileSize; + return this; + } + + public FirmwareUpgradeTypeEnum getFirmwareUpgradeType() { + return firmwareUpgradeType; + } + + public OtaCreateDevice setFirmwareUpgradeType(FirmwareUpgradeTypeEnum firmwareUpgradeType) { + this.firmwareUpgradeType = firmwareUpgradeType; + return this; + } + + public String getFileName() { + return fileName; + } + + public OtaCreateDevice setFileName(String fileName) { + this.fileName = fileName; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateRequest.java b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateRequest.java new file mode 100644 index 0000000..1c43792 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateRequest.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.firmware; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/28 + */ +public class OtaCreateRequest extends BaseModel { + + @Size(min = 1, max = 2) + @NotNull + @Valid + private List devices; + + public OtaCreateRequest() { + } + + @Override + public String toString() { + return "OtaCreateRequest{" + + "devices=" + devices + + '}'; + } + + public List getDevices() { + return devices; + } + + public OtaCreateRequest setDevices(List devices) { + this.devices = devices; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateResponse.java b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateResponse.java new file mode 100644 index 0000000..66d28c3 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaCreateResponse.java @@ -0,0 +1,33 @@ +package com.dji.sdk.cloudapi.firmware; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class OtaCreateResponse { + + /** + * Mission status + **/ + private OtaProgressStatusEnum status; + + public OtaCreateResponse() { + } + + @Override + public String toString() { + return "OtaCreateResponse{" + + "status=" + status + + '}'; + } + + public OtaProgressStatusEnum getStatus() { + return status; + } + + public OtaCreateResponse setStatus(OtaProgressStatusEnum status) { + this.status = status; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgress.java b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgress.java new file mode 100644 index 0000000..e3fe097 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgress.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.firmware; + +/** + * @author sean + * @version 1.2 + * @date 2022/7/29 + */ +public class OtaProgress { + + private OtaProgressStatusEnum status; + + private OtaProgressData progress; + + private OtaProgressExt ext; + + public OtaProgress() { + } + + @Override + public String toString() { + return "OtaProgress{" + + "status=" + status + + ", progress=" + progress + + ", ext=" + ext + + '}'; + } + + public OtaProgressStatusEnum getStatus() { + return status; + } + + public OtaProgress setStatus(OtaProgressStatusEnum status) { + this.status = status; + return this; + } + + public OtaProgressData getProgress() { + return progress; + } + + public OtaProgress setProgress(OtaProgressData progress) { + this.progress = progress; + return this; + } + + public OtaProgressExt getExt() { + return ext; + } + + public OtaProgress setExt(OtaProgressExt ext) { + this.ext = ext; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressData.java b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressData.java new file mode 100644 index 0000000..997cce1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressData.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.firmware; + +/** + * @author sean + * @version 1.2 + * @date 2022/7/29 + */ +public class OtaProgressData { + + private Integer percent; + + private OtaProgressStepEnum currentStep; + + public OtaProgressData() { + } + + @Override + public String toString() { + return "OtaProgressData{" + + "percent=" + percent + + ", currentStep=" + currentStep + + '}'; + } + + public Integer getPercent() { + return percent; + } + + public OtaProgressData setPercent(Integer percent) { + this.percent = percent; + return this; + } + + public OtaProgressStepEnum getCurrentStep() { + return currentStep; + } + + public OtaProgressData setCurrentStep(OtaProgressStepEnum currentStep) { + this.currentStep = currentStep; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressExt.java b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressExt.java new file mode 100644 index 0000000..1eb2f30 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressExt.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.firmware; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/30 + */ +public class OtaProgressExt { + + private Long rate; + + public OtaProgressExt() { + } + + @Override + public String toString() { + return "OtaProgressExt{" + + "rate=" + rate + + '}'; + } + + public Long getRate() { + return rate; + } + + public OtaProgressExt setRate(Long rate) { + this.rate = rate; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressStatusEnum.java b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressStatusEnum.java new file mode 100644 index 0000000..1a304b4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressStatusEnum.java @@ -0,0 +1,57 @@ +package com.dji.sdk.cloudapi.firmware; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.2 + * @date 2022/8/17 + */ +public enum OtaProgressStatusEnum { + + SENT("sent", false), + + IN_PROGRESS("in_progress", false), + + OK("ok", true), + + PAUSED("paused", false), + + REJECTED("rejected", true), + + FAILED("failed", true), + + CANCELED("canceled", true), + + TIMEOUT("timeout", true); + + private final String status; + + private final boolean end; + + OtaProgressStatusEnum(String status, boolean end) { + this.status = status; + this.end = end; + } + + @JsonValue + public String getStatus() { + return status; + } + + public boolean isEnd() { + return end; + } + + @JsonCreator + public static OtaProgressStatusEnum find(String status) { + return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny() + .orElseThrow(() -> new CloudSDKException(OtaProgressStatusEnum.class, status)); + } +} + + diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressStepEnum.java b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressStepEnum.java new file mode 100644 index 0000000..6369879 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/OtaProgressStepEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.firmware; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/28 + */ +public enum OtaProgressStepEnum { + + DOWNLOADING(1), + + UPGRADING(2); + + private final int step; + + OtaProgressStepEnum(int step) { + this.step = step; + } + + @JsonValue + public int getStep() { + return step; + } + + @JsonCreator + public static OtaProgressStepEnum find(int step) { + return Arrays.stream(values()).filter(stepEnum -> stepEnum.step == step).findAny() + .orElseThrow(() -> new CloudSDKException(OtaProgressStepEnum.class, step)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/firmware/api/AbstractFirmwareService.java b/src/main/java/com/dji/sdk/cloudapi/firmware/api/AbstractFirmwareService.java new file mode 100644 index 0000000..44766de --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/firmware/api/AbstractFirmwareService.java @@ -0,0 +1,57 @@ +package com.dji.sdk.cloudapi.firmware.api; + +import com.dji.sdk.cloudapi.firmware.FirmwareMethodEnum; +import com.dji.sdk.cloudapi.firmware.OtaCreateRequest; +import com.dji.sdk.cloudapi.firmware.OtaCreateResponse; +import com.dji.sdk.cloudapi.firmware.OtaProgress; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.services.ServicesPublish; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/28 + */ +public abstract class AbstractFirmwareService { + + @Resource + private ServicesPublish servicesPublish; + + /** + * Firmware upgrade progress + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_OTA_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse otaProgress(TopicEventsRequest> request, MessageHeaders headers) { + throw new UnsupportedOperationException("otaProgress not implemented"); + } + + /** + * Firmware upgrade + * @param gateway + * @param request data + * @return services_reply + */ + public TopicServicesResponse> otaCreate(GatewayManager gateway, OtaCreateRequest request) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + FirmwareMethodEnum.OTA_CREATE.getMethod(), + request); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/DeviceHms.java b/src/main/java/com/dji/sdk/cloudapi/hms/DeviceHms.java new file mode 100644 index 0000000..a55586c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/DeviceHms.java @@ -0,0 +1,104 @@ +package com.dji.sdk.cloudapi.hms; + +import com.dji.sdk.cloudapi.device.DeviceEnum; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/6 + */ +public class DeviceHms { + + private String code; + + private DeviceEnum deviceType; + + private Boolean imminent; + + private Boolean inTheSky; + + private HmsLevelEnum level; + + private HmsModuleEnum module; + + private DeviceHmsArgs args; + + public DeviceHms() { + } + + @Override + public String toString() { + return "DeviceHms{" + + "code='" + code + '\'' + + ", deviceType=" + deviceType + + ", imminent=" + imminent + + ", inTheSky=" + inTheSky + + ", level=" + level + + ", module=" + module + + ", args=" + args + + '}'; + } + + public String getCode() { + return code; + } + + public DeviceHms setCode(String code) { + this.code = code; + return this; + } + + public DeviceEnum getDeviceType() { + return deviceType; + } + + public DeviceHms setDeviceType(DeviceEnum deviceType) { + this.deviceType = deviceType; + return this; + } + + public Boolean getImminent() { + return imminent; + } + + public DeviceHms setImminent(Boolean imminent) { + this.imminent = imminent; + return this; + } + + public Boolean getInTheSky() { + return inTheSky; + } + + public DeviceHms setInTheSky(Boolean inTheSky) { + this.inTheSky = inTheSky; + return this; + } + + public HmsLevelEnum getLevel() { + return level; + } + + public DeviceHms setLevel(HmsLevelEnum level) { + this.level = level; + return this; + } + + public HmsModuleEnum getModule() { + return module; + } + + public DeviceHms setModule(HmsModuleEnum module) { + this.module = module; + return this; + } + + public DeviceHmsArgs getArgs() { + return args; + } + + public DeviceHms setArgs(DeviceHmsArgs args) { + this.args = args; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/DeviceHmsArgs.java b/src/main/java/com/dji/sdk/cloudapi/hms/DeviceHmsArgs.java new file mode 100644 index 0000000..3c9493e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/DeviceHmsArgs.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.hms; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/6 + */ +public class DeviceHmsArgs { + + private Long componentIndex; + + private Integer sensorIndex; + + private Integer alarmId; + + public DeviceHmsArgs() { + } + + @Override + public String toString() { + return "DeviceHmsArgs{" + + "componentIndex=" + componentIndex + + ", sensorIndex=" + sensorIndex + + ", alarmId=" + alarmId + + '}'; + } + + public Long getComponentIndex() { + return componentIndex; + } + + public DeviceHmsArgs setComponentIndex(Long componentIndex) { + this.componentIndex = componentIndex; + return this; + } + + public Integer getSensorIndex() { + return sensorIndex; + } + + public DeviceHmsArgs setSensorIndex(Integer sensorIndex) { + this.sensorIndex = sensorIndex; + return this; + } + + public Integer getAlarmId() { + return alarmId; + } + + public DeviceHmsArgs setAlarmId(Integer alarmId) { + this.alarmId = alarmId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/Hms.java b/src/main/java/com/dji/sdk/cloudapi/hms/Hms.java new file mode 100644 index 0000000..8f6dd83 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/Hms.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.hms; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/27 + */ +public class Hms { + + private List list; + + public Hms() { + } + + @Override + public String toString() { + return "Hms{" + + "list=" + list + + '}'; + } + + public List getList() { + return list; + } + + public Hms setList(List list) { + this.list = list; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsBatteryIndexEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsBatteryIndexEnum.java new file mode 100644 index 0000000..9583f4e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsBatteryIndexEnum.java @@ -0,0 +1,48 @@ +package com.dji.sdk.cloudapi.hms; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +public enum HmsBatteryIndexEnum { + + LEFT(0, "left", "左"), + + RIGHT(1, "right", "右"); + + private final int val; + + private final String en; + + private final String zh; + + HmsBatteryIndexEnum(int val, String en, String zh) { + this.val = val; + this.en = en; + this.zh = zh; + } + + @JsonValue + public int getVal() { + return val; + } + + public String getEn() { + return en; + } + + public String getZh() { + return zh; + } + + public static HmsBatteryIndexEnum find(int val) { + return Arrays.stream(HmsBatteryIndexEnum.values()).filter(battery -> battery.val == val).findAny() + .orElseThrow(() -> new CloudSDKException(HmsBatteryIndexEnum.class, val)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsChargingRodIndexEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsChargingRodIndexEnum.java new file mode 100644 index 0000000..08751d2 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsChargingRodIndexEnum.java @@ -0,0 +1,50 @@ +package com.dji.sdk.cloudapi.hms; + +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +public enum HmsChargingRodIndexEnum { + + FRONT(0, "front", "前"), + + BACK(1, "back", "后"), + + LEFT(2, "left", "左"), + + RIGHT(3, "right", "右"); + + private final int val; + + private final String en; + + private final String zh; + + HmsChargingRodIndexEnum(int val, String en, String zh) { + this.val = val; + this.en = en; + this.zh = zh; + } + + public int getVal() { + return val; + } + + public String getEn() { + return en; + } + + public String getZh() { + return zh; + } + + public static HmsChargingRodIndexEnum find(int val) { + return Arrays.stream(HmsChargingRodIndexEnum.values()).filter(rod -> rod.val == val).findAny() + .orElseThrow(() -> new CloudSDKException(HmsChargingRodIndexEnum.class, val)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsDockCoverIndexEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsDockCoverIndexEnum.java new file mode 100644 index 0000000..fdd417e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsDockCoverIndexEnum.java @@ -0,0 +1,48 @@ +package com.dji.sdk.cloudapi.hms; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +public enum HmsDockCoverIndexEnum { + + LEFT(0, "left", "左"), + + RIGHT(1, "right", "右"); + + private final int val; + + private final String en; + + private final String zh; + + HmsDockCoverIndexEnum(int val, String en, String zh) { + this.val = val; + this.en = en; + this.zh = zh; + } + + @JsonValue + public int getVal() { + return val; + } + + public String getEn() { + return en; + } + + public String getZh() { + return zh; + } + + public static HmsDockCoverIndexEnum find(int val) { + return Arrays.stream(HmsDockCoverIndexEnum.values()).filter(dockCover -> dockCover.val == val).findAny() + .orElseThrow(() -> new CloudSDKException(HmsDockCoverIndexEnum.class, val)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsFaqIdEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsFaqIdEnum.java new file mode 100644 index 0000000..065920f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsFaqIdEnum.java @@ -0,0 +1,43 @@ +package com.dji.sdk.cloudapi.hms; + +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +public enum HmsFaqIdEnum { + + DOCK_TIP("dock_tip_", DeviceDomainEnum.DOCK), + + FPV_TIP("fpv_tip_", DeviceDomainEnum.DRONE); + + private final String text; + + private final DeviceDomainEnum domain; + + @JsonValue + public String getText() { + return text; + } + + public DeviceDomainEnum getDomain() { + return domain; + } + + HmsFaqIdEnum(String text, DeviceDomainEnum domain) { + this.text = text; + this.domain = domain; + } + + public static HmsFaqIdEnum find(DeviceDomainEnum domain) { + return Arrays.stream(values()).filter(faqIdEnum -> faqIdEnum.domain == domain).findAny() + .orElseThrow(() -> new CloudSDKException(HmsFaqIdEnum.class, domain)); + } +} + diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsFormatKeyEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsFormatKeyEnum.java new file mode 100644 index 0000000..58f5772 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsFormatKeyEnum.java @@ -0,0 +1,44 @@ +package com.dji.sdk.cloudapi.hms; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +public enum HmsFormatKeyEnum { + + ALARM_ID("%alarmid"), + + COMPONENT_INDEX("%component_index"), + + INDEX("%index"), + + BATTERY_INDEX("%battery_index"), + + DOCK_COVER_INDEX("%dock_cover_index"), + + CHARGING_ROD_INDEX("%charging_rod_index"); + +// public static final char KEY_START = '%'; + + private final String key; + + HmsFormatKeyEnum(String key) { + this.key = key; + } + + @JsonValue + public String getKey() { + return key; + } + + public static HmsFormatKeyEnum find(String key) { + return Arrays.stream(HmsFormatKeyEnum.values()).filter(format -> format.getKey().equals(key)).findAny() + .orElseThrow(() -> new CloudSDKException(HmsFormatKeyEnum.class, key)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsInTheSkyEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsInTheSkyEnum.java new file mode 100644 index 0000000..ba995d9 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsInTheSkyEnum.java @@ -0,0 +1,24 @@ +package com.dji.sdk.cloudapi.hms; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +public enum HmsInTheSkyEnum { + + IN_THE_SKY("_in_the_sky"); + + private final String text; + + HmsInTheSkyEnum(String text) { + this.text = text; + } + + @JsonValue + public String getText() { + return text; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsLevelEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsLevelEnum.java new file mode 100644 index 0000000..d1ce59a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsLevelEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.hms; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/27 + */ +public enum HmsLevelEnum { + + INFORM(0), + + NOTICE(1), + + ALARM(2); + + private final int level; + + HmsLevelEnum(int level) { + this.level = level; + } + + @JsonValue + public int getLevel() { + return level; + } + + @JsonCreator + public static HmsLevelEnum find(int level) { + return Arrays.stream(values()).filter(levelEnum -> levelEnum.level == level).findAny() + .orElseThrow(() -> new CloudSDKException(HmsLevelEnum.class, level)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsMessageLanguageEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsMessageLanguageEnum.java new file mode 100644 index 0000000..1ac863a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsMessageLanguageEnum.java @@ -0,0 +1,23 @@ +package com.dji.sdk.cloudapi.hms; + +/** + * @author sean + * @version 1.1 + * @date 2022/7/7 + */ +public enum HmsMessageLanguageEnum { + + EN("en"), + + ZH("zh"); + + private final String language; + + HmsMessageLanguageEnum(String language) { + this.language = language; + } + + public String getLanguage() { + return language; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/HmsModuleEnum.java b/src/main/java/com/dji/sdk/cloudapi/hms/HmsModuleEnum.java new file mode 100644 index 0000000..0697b0a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/HmsModuleEnum.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.hms; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/27 + */ +public enum HmsModuleEnum { + + FLIGHT_MISSION(0), + + DEVICE_MANAGEMENT(1), + + MEDIA(2), + + HMS(3); + + private final int module; + + HmsModuleEnum(int module) { + this.module = module; + } + + @JsonValue + public int getModule() { + return module; + } + + @JsonCreator + public static HmsModuleEnum find(int module) { + return Arrays.stream(values()).filter(moduleEnum -> moduleEnum.module == module).findAny() + .orElseThrow(() -> new CloudSDKException(HmsModuleEnum.class, module)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/hms/api/AbstractHmsService.java b/src/main/java/com/dji/sdk/cloudapi/hms/api/AbstractHmsService.java new file mode 100644 index 0000000..97b1c1b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/hms/api/AbstractHmsService.java @@ -0,0 +1,27 @@ +package com.dji.sdk.cloudapi.hms.api; + +import com.dji.sdk.cloudapi.hms.Hms; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.MessageHeaders; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/27 + */ +public abstract class AbstractHmsService { + + /** + * Reporting of hms + * @param response + * @param headers + * @return + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HMS) + public void hms(TopicEventsRequest response, MessageHeaders headers) { + throw new UnsupportedOperationException("hms not implemented"); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacity.java b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacity.java new file mode 100644 index 0000000..c36b39c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacity.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.livestream; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class DockLiveCapacity { + + /** + * Total number of video streams available for livestreaming. + * Indicates the total number of all available live video streams owned by the aircraft or device. + */ + private Integer availableVideoNumber; + + /** + * Maximum total number of video streams that can be lived stream simultaneously. + */ + private Integer coexistVideoNumberMax; + + /** + * Device live streaming capability list + */ + private List deviceList; + + public DockLiveCapacity() { + } + + @Override + public String toString() { + return "DockLiveCapacity{" + + "availableVideoNumber=" + availableVideoNumber + + ", coexistVideoNumberMax=" + coexistVideoNumberMax + + ", deviceList=" + deviceList + + '}'; + } + + public Integer getAvailableVideoNumber() { + return availableVideoNumber; + } + + public DockLiveCapacity setAvailableVideoNumber(Integer availableVideoNumber) { + this.availableVideoNumber = availableVideoNumber; + return this; + } + + public Integer getCoexistVideoNumberMax() { + return coexistVideoNumberMax; + } + + public DockLiveCapacity setCoexistVideoNumberMax(Integer coexistVideoNumberMax) { + this.coexistVideoNumberMax = coexistVideoNumberMax; + return this; + } + + public List getDeviceList() { + return deviceList; + } + + public DockLiveCapacity setDeviceList(List deviceList) { + this.deviceList = deviceList; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityCamera.java b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityCamera.java new file mode 100644 index 0000000..e182409 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityCamera.java @@ -0,0 +1,80 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.cloudapi.device.PayloadIndex; + +import java.util.List; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class DockLiveCapacityCamera { + + /** + * Total number of video streams that can be used for livestreaming + * Total number of video streams that the camera can live stream + */ + private Integer availableVideoNumber; + + /** + * Maximum number of video streams that the camera can live stream at the same time. + */ + private Integer coexistVideoNumberMax; + + /** + * Camera index, composed of product type enumeration and gimbal index. + */ + private PayloadIndex cameraIndex; + + private List videoList; + + public DockLiveCapacityCamera() { + } + + @Override + public String toString() { + return "DockLiveCapacityCamera{" + + "availableVideoNumber=" + availableVideoNumber + + ", coexistVideoNumberMax=" + coexistVideoNumberMax + + ", cameraIndex=" + cameraIndex + + ", videoList=" + videoList + + '}'; + } + + public Integer getAvailableVideoNumber() { + return availableVideoNumber; + } + + public DockLiveCapacityCamera setAvailableVideoNumber(Integer availableVideoNumber) { + this.availableVideoNumber = availableVideoNumber; + return this; + } + + public Integer getCoexistVideoNumberMax() { + return coexistVideoNumberMax; + } + + public DockLiveCapacityCamera setCoexistVideoNumberMax(Integer coexistVideoNumberMax) { + this.coexistVideoNumberMax = coexistVideoNumberMax; + return this; + } + + public PayloadIndex getCameraIndex() { + return cameraIndex; + } + + public DockLiveCapacityCamera setCameraIndex(PayloadIndex cameraIndex) { + this.cameraIndex = cameraIndex; + return this; + } + + public List getVideoList() { + return videoList; + } + + public DockLiveCapacityCamera setVideoList(List videoList) { + this.videoList = videoList; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityDevice.java b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityDevice.java new file mode 100644 index 0000000..f421132 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityDevice.java @@ -0,0 +1,81 @@ +package com.dji.sdk.cloudapi.livestream; + +import java.util.List; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class DockLiveCapacityDevice { + + /** + * Device serial number + */ + private String sn; + + /** + * Total number of video streams that can be used for livestreaming + * Total number of video streams used for livestreaming that belongs to devices. + */ + private Integer availableVideoNumber; + + /** + * Maximum number of video streams that can be used for livestreaming at the same time + */ + private Integer coexistVideoNumberMax; + + /** + * Camera list on the device + */ + private List cameraList; + + public DockLiveCapacityDevice() { + } + + @Override + public String toString() { + return "DockLiveCapacityDevice{" + + "sn='" + sn + '\'' + + ", availableVideoNumber=" + availableVideoNumber + + ", coexistVideoNumberMax=" + coexistVideoNumberMax + + ", cameraList=" + cameraList + + '}'; + } + + public String getSn() { + return sn; + } + + public DockLiveCapacityDevice setSn(String sn) { + this.sn = sn; + return this; + } + + public Integer getAvailableVideoNumber() { + return availableVideoNumber; + } + + public DockLiveCapacityDevice setAvailableVideoNumber(Integer availableVideoNumber) { + this.availableVideoNumber = availableVideoNumber; + return this; + } + + public Integer getCoexistVideoNumberMax() { + return coexistVideoNumberMax; + } + + public DockLiveCapacityDevice setCoexistVideoNumberMax(Integer coexistVideoNumberMax) { + this.coexistVideoNumberMax = coexistVideoNumberMax; + return this; + } + + public List getCameraList() { + return cameraList; + } + + public DockLiveCapacityDevice setCameraList(List cameraList) { + this.cameraList = cameraList; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityVideo.java b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityVideo.java new file mode 100644 index 0000000..46821e9 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLiveCapacityVideo.java @@ -0,0 +1,56 @@ +package com.dji.sdk.cloudapi.livestream; + +import java.util.List; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class DockLiveCapacityVideo { + + private String videoIndex; + + private VideoTypeEnum videoType; + + private List switchableVideoTypes; + + public DockLiveCapacityVideo() { + } + + @Override + public String toString() { + return "DockLiveCapacityVideo{" + + "videoIndex='" + videoIndex + '\'' + + ", videoType=" + videoType + + ", switchableVideoTypes=" + switchableVideoTypes + + '}'; + } + + public String getVideoIndex() { + return videoIndex; + } + + public DockLiveCapacityVideo setVideoIndex(String videoIndex) { + this.videoIndex = videoIndex; + return this; + } + + public VideoTypeEnum getVideoType() { + return videoType; + } + + public DockLiveCapacityVideo setVideoType(VideoTypeEnum videoType) { + this.videoType = videoType; + return this; + } + + public List getSwitchableVideoTypes() { + return switchableVideoTypes; + } + + public DockLiveCapacityVideo setSwitchableVideoTypes(List switchableVideoTypes) { + this.switchableVideoTypes = switchableVideoTypes; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/DockLivestreamAbilityUpdate.java b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLivestreamAbilityUpdate.java new file mode 100644 index 0000000..87d1e55 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/DockLivestreamAbilityUpdate.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.livestream; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class DockLivestreamAbilityUpdate { + + private DockLiveCapacity liveCapacity; + + public DockLivestreamAbilityUpdate() { + } + + @Override + public String toString() { + return "DockLivestreamAbilityUpdate{" + + "liveCapacity=" + liveCapacity + + '}'; + } + + public DockLiveCapacity getLiveCapacity() { + return liveCapacity; + } + + public DockLivestreamAbilityUpdate setLiveCapacity(DockLiveCapacity liveCapacity) { + this.liveCapacity = liveCapacity; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/LensChangeVideoTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/livestream/LensChangeVideoTypeEnum.java new file mode 100644 index 0000000..57dcbe0 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/LensChangeVideoTypeEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/25 + */ +public enum LensChangeVideoTypeEnum { + + ZOOM("zoom"), + + WIDE("wide"), + + IR("ir"); + + private final String type; + + LensChangeVideoTypeEnum(String type) { + this.type = type; + } + + @JsonValue + public String getType() { + return type; + } + + @JsonCreator + public static LensChangeVideoTypeEnum find(String videoType) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type.equals(videoType)).findAny() + .orElseThrow(() -> new CloudSDKException(LensChangeVideoTypeEnum.class , videoType)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/LiveErrorCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveErrorCodeEnum.java new file mode 100644 index 0000000..f45ea11 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveErrorCodeEnum.java @@ -0,0 +1,80 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.mqtt.services.IServicesErrorCode; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public enum LiveErrorCodeEnum implements IServicesErrorCode, IErrorInfo { + + NO_AIRCRAFT(13001, "No aircraft."), + + NO_CAMERA(13002, "No camera."), + + LIVE_STREAM_ALREADY_STARTED(13003, "The camera has started live streaming."), + + FUNCTION_NOT_SUPPORT(13004, "The function is not supported."), + + STRATEGY_NOT_SUPPORT(13005, "The strategy is not supported."), + + NOT_IN_CAMERA_INTERFACE(13006, "The current app is not in the camera interface."), + + NO_FLIGHT_CONTROL(13007, "The remote control has no flight control rights and cannot respond to control commands"), + + NO_STREAM_DATA(13008, "The current app has no stream data."), + + TOO_FREQUENT(13009, "The operation is too frequent."), + + ENABLE_FAILED(13010, "Please check whether the live stream service is normal."), + + NO_LIVE_STREAM(13011, "There are no live stream currently."), + + SWITCH_NOT_SUPPORT(13012, "There is already another camera in the live stream. It's not support to switch the stream directly."), + + URL_TYPE_NOT_SUPPORTED(13013, "This url type is not supported."), + + ERROR_PARAMETERS(13014, "The live stream parameters are abnormal or incomplete."), + + NETWORK_CONGESTION(13015, "Please check the network."), + + ERROR_FRAME(13016, "Live decoding failed."), + + DEVICE_UNKNOWN(13099, "Unknown error inside the device."), + + UNKNOWN(-1, "UNKNOWN"), + ; + + + private final String msg; + + private final int code; + + LiveErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String getMessage() { + return this.msg; + } + + @Override + public Integer getCode() { + return this.code; + } + + /** + * @param code error code + * @return enumeration object + */ + public static LiveErrorCodeEnum find(int code) { + return Arrays.stream(values()).filter(codeEnum -> codeEnum.code == code).findAny().orElse(UNKNOWN); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/LiveLensChangeRequest.java b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveLensChangeRequest.java new file mode 100644 index 0000000..ff498b9 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveLensChangeRequest.java @@ -0,0 +1,53 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.cloudapi.device.VideoId; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class LiveLensChangeRequest extends BaseModel { + + @NotNull + private LensChangeVideoTypeEnum videoType; + + /** + * The format is #{uav_sn}/#{camera_id}/#{video_index}, + * drone serial number/payload and mounted location enumeration value/payload lens numbering + */ + @NotNull + private VideoId videoId; + + public LiveLensChangeRequest() { + } + + @Override + public String toString() { + return "LiveLensChangeRequest{" + + "videoType=" + videoType + + ", videoId=" + videoId + + '}'; + } + + public LensChangeVideoTypeEnum getVideoType() { + return videoType; + } + + public LiveLensChangeRequest setVideoType(LensChangeVideoTypeEnum videoType) { + this.videoType = videoType; + return this; + } + + public VideoId getVideoId() { + return videoId; + } + + public LiveLensChangeRequest setVideoId(VideoId videoId) { + this.videoId = videoId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/LiveSetQualityRequest.java b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveSetQualityRequest.java new file mode 100644 index 0000000..541b25e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveSetQualityRequest.java @@ -0,0 +1,53 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.cloudapi.device.VideoId; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class LiveSetQualityRequest extends BaseModel { + + /** + * The format is #{uav_sn}/#{camera_id}/#{video_index}, + * drone serial number/payload and mounted location enumeration value/payload lens numbering + */ + @NotNull + private VideoId videoId; + + @NotNull + private VideoQualityEnum videoQuality; + + public LiveSetQualityRequest() { + } + + @Override + public String toString() { + return "LiveSetQualityRequest{" + + "videoId=" + videoId + + ", videoQuality=" + videoQuality + + '}'; + } + + public VideoId getVideoId() { + return videoId; + } + + public LiveSetQualityRequest setVideoId(VideoId videoId) { + this.videoId = videoId; + return this; + } + + public VideoQualityEnum getVideoQuality() { + return videoQuality; + } + + public LiveSetQualityRequest setVideoQuality(VideoQualityEnum videoQuality) { + this.videoQuality = videoQuality; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStartPushRequest.java b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStartPushRequest.java new file mode 100644 index 0000000..8748869 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStartPushRequest.java @@ -0,0 +1,93 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.cloudapi.device.VideoId; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class LiveStartPushRequest extends BaseModel { + + @NotNull + private UrlTypeEnum urlType; + + /** + * RTMP: (rtmp://xxxxxxx) Example: rtmp://192.168.1.1:8080/live + * RTSP:(uerName&password&port) Example: userName=dji-cloud-api&password=123456&port=8080 + * GB28181:(serverIP&serverPort&serverID&agentID&agentPassword&localPort&channel) + * Example: serverIP=192.168.1.1&serverPort=8080&serverID=34000000000000000000&agentID= + * 300000000010000000000&agentPassword=0000000&localPort=7060&channel=340000000000000000000 + * AGORA:(channel&sn&token&uid) + * Example: channel=1ZNDH360010162_39-0-7&sn=1ZNDH360010162&token=006dca67721582a48768ec4d8 + * 17b7b25a86IAB4cw2JgN6iX8BpTPdc3e4S1Iendz94IFJ56aSXKvzAJei27MqF2zyCIgCLIIoBt41+YAQAAQC3jX + * 5gAgC3jX5gAwC3jX5gBAC3jX5g&uid=50000 + * Notice: The token generated by Shengwang may have special characters such as '+' ' ', + * and need to do url encode, otherwise there will be a parsing error on the PILOT side + */ + @NotBlank + private String url; + + /** + * The format is #{uav_sn}/#{camera_id}/#{video_index}, + * drone serial number/payload and mounted location enumeration value/payload lens numbering + */ + @NotNull + private VideoId videoId; + + @NotNull + private VideoQualityEnum videoQuality; + + public LiveStartPushRequest() { + } + + @Override + public String toString() { + return "LiveStartPushRequest{" + + "urlType=" + urlType + + ", url='" + url + '\'' + + ", videoId=" + videoId + + ", videoQuality=" + videoQuality + + '}'; + } + + public UrlTypeEnum getUrlType() { + return urlType; + } + + public LiveStartPushRequest setUrlType(UrlTypeEnum urlType) { + this.urlType = urlType; + return this; + } + + public String getUrl() { + return url; + } + + public LiveStartPushRequest setUrl(String url) { + this.url = url; + return this; + } + + public VideoId getVideoId() { + return videoId; + } + + public LiveStartPushRequest setVideoId(VideoId videoId) { + this.videoId = videoId; + return this; + } + + public VideoQualityEnum getVideoQuality() { + return videoQuality; + } + + public LiveStartPushRequest setVideoQuality(VideoQualityEnum videoQuality) { + this.videoQuality = videoQuality; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStopPushRequest.java b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStopPushRequest.java new file mode 100644 index 0000000..9107c53 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStopPushRequest.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.cloudapi.device.VideoId; +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class LiveStopPushRequest extends BaseModel { + + /** + * The format is #{uav_sn}/#{camera_id}/#{video_index}, + * drone serial number/payload and mounted location enumeration value/payload lens numbering + */ + @NotNull + private VideoId videoId; + + public LiveStopPushRequest() { + } + + @Override + public String toString() { + return "LiveStopPushRequest{" + + "videoId=" + videoId + + '}'; + } + + public VideoId getVideoId() { + return videoId; + } + + public LiveStopPushRequest setVideoId(VideoId videoId) { + this.videoId = videoId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStreamMethodEnum.java b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStreamMethodEnum.java new file mode 100644 index 0000000..46e9273 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/LiveStreamMethodEnum.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +public enum LiveStreamMethodEnum { + + LIVE_START_PUSH("live_start_push"), + + LIVE_STOP_PUSH("live_stop_push"), + + LIVE_SET_QUALITY("live_set_quality"), + + LIVE_LENS_CHANGE("live_lens_change"); + + private final String method; + + LiveStreamMethodEnum(String method) { + this.method = method; + } + + @JsonValue + public String getMethod() { + return method; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacity.java b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacity.java new file mode 100644 index 0000000..f11fe80 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacity.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.livestream; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class RcLiveCapacity { + + /** + * Total number of video streams available for livestreaming. + * Indicates the total number of all available live video streams owned by the aircraft or device. + */ + private Integer availableVideoNumber; + + /** + * Maximum total number of video streams that can be lived stream simultaneously. + */ + private Integer coexistVideoNumberMax; + + /** + * Device live streaming capability list + */ + private List deviceList; + + public RcLiveCapacity() { + } + + @Override + public String toString() { + return "RcLiveCapacity{" + + "availableVideoNumber=" + availableVideoNumber + + ", coexistVideoNumberMax=" + coexistVideoNumberMax + + ", deviceList=" + deviceList + + '}'; + } + + public Integer getAvailableVideoNumber() { + return availableVideoNumber; + } + + public RcLiveCapacity setAvailableVideoNumber(Integer availableVideoNumber) { + this.availableVideoNumber = availableVideoNumber; + return this; + } + + public Integer getCoexistVideoNumberMax() { + return coexistVideoNumberMax; + } + + public RcLiveCapacity setCoexistVideoNumberMax(Integer coexistVideoNumberMax) { + this.coexistVideoNumberMax = coexistVideoNumberMax; + return this; + } + + public List getDeviceList() { + return deviceList; + } + + public RcLiveCapacity setDeviceList(List deviceList) { + this.deviceList = deviceList; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityCamera.java b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityCamera.java new file mode 100644 index 0000000..7ca3b66 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityCamera.java @@ -0,0 +1,80 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.cloudapi.device.PayloadIndex; + +import java.util.List; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class RcLiveCapacityCamera { + + /** + * Total number of video streams that can be used for livestreaming + * Total number of video streams that the camera can live stream + */ + private Integer availableVideoNumber; + + /** + * Maximum number of video streams that the camera can live stream at the same time. + */ + private Integer coexistVideoNumberMax; + + /** + * Camera index, composed of product type enumeration and gimbal index. + */ + private PayloadIndex cameraIndex; + + private List videoList; + + public RcLiveCapacityCamera() { + } + + @Override + public String toString() { + return "CapacityCameraReceiver{" + + "availableVideoNumber=" + availableVideoNumber + + ", coexistVideoNumberMax=" + coexistVideoNumberMax + + ", cameraIndex=" + cameraIndex + + ", videoList=" + videoList + + '}'; + } + + public Integer getAvailableVideoNumber() { + return availableVideoNumber; + } + + public RcLiveCapacityCamera setAvailableVideoNumber(Integer availableVideoNumber) { + this.availableVideoNumber = availableVideoNumber; + return this; + } + + public Integer getCoexistVideoNumberMax() { + return coexistVideoNumberMax; + } + + public RcLiveCapacityCamera setCoexistVideoNumberMax(Integer coexistVideoNumberMax) { + this.coexistVideoNumberMax = coexistVideoNumberMax; + return this; + } + + public PayloadIndex getCameraIndex() { + return cameraIndex; + } + + public RcLiveCapacityCamera setCameraIndex(PayloadIndex cameraIndex) { + this.cameraIndex = cameraIndex; + return this; + } + + public List getVideoList() { + return videoList; + } + + public RcLiveCapacityCamera setVideoList(List videoList) { + this.videoList = videoList; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityDevice.java b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityDevice.java new file mode 100644 index 0000000..831bfc4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityDevice.java @@ -0,0 +1,81 @@ +package com.dji.sdk.cloudapi.livestream; + +import java.util.List; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class RcLiveCapacityDevice { + + /** + * Device serial number + */ + private String sn; + + /** + * Total number of video streams that can be used for livestreaming + * Total number of video streams used for livestreaming that belongs to devices. + */ + private Integer availableVideoNumber; + + /** + * Maximum number of video streams that can be used for livestreaming at the same time + */ + private Integer coexistVideoNumberMax; + + /** + * Camera list on the device + */ + private List cameraList; + + public RcLiveCapacityDevice() { + } + + @Override + public String toString() { + return "CapacityDeviceReceiver{" + + "sn='" + sn + '\'' + + ", availableVideoNumber=" + availableVideoNumber + + ", coexistVideoNumberMax=" + coexistVideoNumberMax + + ", cameraList=" + cameraList + + '}'; + } + + public String getSn() { + return sn; + } + + public RcLiveCapacityDevice setSn(String sn) { + this.sn = sn; + return this; + } + + public Integer getAvailableVideoNumber() { + return availableVideoNumber; + } + + public RcLiveCapacityDevice setAvailableVideoNumber(Integer availableVideoNumber) { + this.availableVideoNumber = availableVideoNumber; + return this; + } + + public Integer getCoexistVideoNumberMax() { + return coexistVideoNumberMax; + } + + public RcLiveCapacityDevice setCoexistVideoNumberMax(Integer coexistVideoNumberMax) { + this.coexistVideoNumberMax = coexistVideoNumberMax; + return this; + } + + public List getCameraList() { + return cameraList; + } + + public RcLiveCapacityDevice setCameraList(List cameraList) { + this.cameraList = cameraList; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityVideo.java b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityVideo.java new file mode 100644 index 0000000..640a631 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLiveCapacityVideo.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.livestream; + +/** + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public class RcLiveCapacityVideo { + + private String videoIndex; + + private VideoTypeEnum videoType; + + public RcLiveCapacityVideo() { + } + + @Override + public String toString() { + return "CapacityVideoReceiver{" + + "videoIndex='" + videoIndex + '\'' + + ", videoType=" + videoType + + '}'; + } + + public String getVideoIndex() { + return videoIndex; + } + + public RcLiveCapacityVideo setVideoIndex(String videoIndex) { + this.videoIndex = videoIndex; + return this; + } + + public VideoTypeEnum getVideoType() { + return videoType; + } + + public RcLiveCapacityVideo setVideoType(VideoTypeEnum videoType) { + this.videoType = videoType; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/RcLivestreamAbilityUpdate.java b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLivestreamAbilityUpdate.java new file mode 100644 index 0000000..25bf191 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/RcLivestreamAbilityUpdate.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.livestream; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class RcLivestreamAbilityUpdate { + + private RcLiveCapacity liveCapacity; + + public RcLivestreamAbilityUpdate() { + } + + @Override + public String toString() { + return "RcLivestreamAbilityUpdate{" + + "liveCapacity=" + liveCapacity + + '}'; + } + + public RcLiveCapacity getLiveCapacity() { + return liveCapacity; + } + + public RcLivestreamAbilityUpdate setLiveCapacity(RcLiveCapacity liveCapacity) { + this.liveCapacity = liveCapacity; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/UrlTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/livestream/UrlTypeEnum.java new file mode 100644 index 0000000..dc59a10 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/UrlTypeEnum.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/22 + */ +public enum UrlTypeEnum { + + AGORA(0), + + RTMP(1), + + RTSP(2), + + GB28181(3); + + private final int type; + + UrlTypeEnum(int type) { + this.type = type; + } + + @JsonValue + public int getType() { + return type; + } + + @JsonCreator + public static UrlTypeEnum find(int type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(UrlTypeEnum.class, type)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/VideoQualityEnum.java b/src/main/java/com/dji/sdk/cloudapi/livestream/VideoQualityEnum.java new file mode 100644 index 0000000..df4479d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/VideoQualityEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 0.1 + * @date 2021/11/26 + */ +public enum VideoQualityEnum { + + AUTO (0), + + SMOOTH(1), + + STANDARD_DEFINITION(2), + + HIGH_DEFINITION(3), + + ULTRA_HD(4); + + private final int quality; + + VideoQualityEnum(int quality) { + this.quality = quality; + } + + @JsonValue + public int getQuality() { + return quality; + } + + @JsonCreator + public static VideoQualityEnum find(int quality) { + return Arrays.stream(values()).filter(qualityEnum -> qualityEnum.quality == quality).findAny() + .orElseThrow(() -> new CloudSDKException(VideoQualityEnum.class, quality)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/VideoTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/livestream/VideoTypeEnum.java new file mode 100644 index 0000000..256a1d1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/VideoTypeEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.livestream; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/25 + */ +public enum VideoTypeEnum { + + ZOOM("zoom"), + + WIDE("wide"), + + THERMAL("thermal"), + + NORMAL("normal"), + + IR("ir"); + + private final String type; + + VideoTypeEnum(String type) { + this.type = type; + } + + @JsonValue + public String getType() { + return type; + } + + @JsonCreator + public static VideoTypeEnum find(String videoType) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type.equals(videoType)).findAny() + .orElseThrow(() -> new CloudSDKException(VideoTypeEnum.class , videoType)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/livestream/api/AbstractLivestreamService.java b/src/main/java/com/dji/sdk/cloudapi/livestream/api/AbstractLivestreamService.java new file mode 100644 index 0000000..24597ea --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/livestream/api/AbstractLivestreamService.java @@ -0,0 +1,101 @@ +package com.dji.sdk.cloudapi.livestream.api; + +import com.dji.sdk.cloudapi.livestream.*; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.services.ServicesPublish; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; +import com.dji.sdk.mqtt.state.TopicStateRequest; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public abstract class AbstractLivestreamService { + + @Resource + private ServicesPublish servicesPublish; + + /** + * Livestream ability update for remote control + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_LIVESTREAM_ABILITY_UPDATE) + public void dockLivestreamAbilityUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("dockLivestreamAbilityUpdate not implemented"); + } + + /** + * Livestream ability update for dock + * @param request data + * @param headers The headers for a {@link Message}. + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_RC_LIVESTREAM_ABILITY_UPDATE) + public void rcLivestreamAbilityUpdate(TopicStateRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("rcLivestreamAbilityUpdate not implemented"); + } + + /** + * Start livestreaming + * @param gateway + * @param request data + * @return services_reply + */ + public TopicServicesResponse> liveStartPush(GatewayManager gateway, LiveStartPushRequest request) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + LiveStreamMethodEnum.LIVE_START_PUSH.getMethod(), + request); + } + + /** + * Stop livestreaming + * @param gateway + * @param request data + * @return services_reply + */ + public TopicServicesResponse liveStopPush(GatewayManager gateway, LiveStopPushRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + LiveStreamMethodEnum.LIVE_STOP_PUSH.getMethod(), + request); + } + + /** + * Set livestream quality + * @param gateway + * @param request data + * @return services_reply + */ + public TopicServicesResponse liveSetQuality(GatewayManager gateway, LiveSetQualityRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + LiveStreamMethodEnum.LIVE_SET_QUALITY.getMethod(), + request); + } + + /** + * Set livestream lens + * @param gateway + * @param request data + * @return services_reply + */ + public TopicServicesResponse liveLensChange(GatewayManager gateway, LiveLensChangeRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + LiveStreamMethodEnum.LIVE_LENS_CHANGE.getMethod(), + request); + } + + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListFile.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListFile.java new file mode 100644 index 0000000..40693e5 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListFile.java @@ -0,0 +1,68 @@ +package com.dji.sdk.cloudapi.log; + +import java.util.List; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/7 + */ +public class FileUploadListFile { + + private String deviceSn; + + private List list; + + private LogModuleEnum module; + + private Integer result; + + public FileUploadListFile() { + } + + @Override + public String toString() { + return "FileUploadListFile{" + + "deviceSn='" + deviceSn + '\'' + + ", list=" + list + + ", module=" + module + + ", result=" + result + + '}'; + } + + public String getDeviceSn() { + return deviceSn; + } + + public FileUploadListFile setDeviceSn(String deviceSn) { + this.deviceSn = deviceSn; + return this; + } + + public List getList() { + return list; + } + + public FileUploadListFile setList(List list) { + this.list = list; + return this; + } + + public LogModuleEnum getModule() { + return module; + } + + public FileUploadListFile setModule(LogModuleEnum module) { + this.module = module; + return this; + } + + public Integer getResult() { + return result; + } + + public FileUploadListFile setResult(Integer result) { + this.result = result; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListRequest.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListRequest.java new file mode 100644 index 0000000..229fd4c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListRequest.java @@ -0,0 +1,43 @@ +package com.dji.sdk.cloudapi.log; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class FileUploadListRequest extends BaseModel { + + /** + * Filter list of file + **/ + @NotNull + @Valid + @Size(min = 1, max = 2) + private List moduleList; + + public FileUploadListRequest() { + } + + @Override + public String toString() { + return "FileUploadListRequest{" + + "moduleList=" + moduleList + + '}'; + } + + public List getModuleList() { + return moduleList; + } + + public FileUploadListRequest setModuleList(List moduleList) { + this.moduleList = moduleList; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListResponse.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListResponse.java new file mode 100644 index 0000000..f814723 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadListResponse.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.log; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class FileUploadListResponse { + + private List files; + + public FileUploadListResponse() { + } + + @Override + public String toString() { + return "FileUploadListResponse{" + + "files=" + files + + '}'; + } + + public List getFiles() { + return files; + } + + public FileUploadListResponse setFiles(List files) { + this.files = files; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgress.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgress.java new file mode 100644 index 0000000..d06d24a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgress.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.log; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/9 + */ +public class FileUploadProgress { + + private FileUploadProgressExt ext; + + private FileUploadStatusEnum status; + + public FileUploadProgress() { + } + + @Override + public String toString() { + return "FileUploadProgress{" + + "ext=" + ext + + ", status=" + status + + '}'; + } + + public FileUploadProgressExt getExt() { + return ext; + } + + public FileUploadProgress setExt(FileUploadProgressExt ext) { + this.ext = ext; + return this; + } + + public FileUploadStatusEnum getStatus() { + return status; + } + + public FileUploadProgress setStatus(FileUploadStatusEnum status) { + this.status = status; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgressExt.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgressExt.java new file mode 100644 index 0000000..55c4748 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgressExt.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.log; + +import java.util.List; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/9 + */ +public class FileUploadProgressExt { + + private List files; + + public FileUploadProgressExt() { + } + + @Override + public String toString() { + return "FileUploadProgressExt{" + + "files=" + files + + '}'; + } + + public List getFiles() { + return files; + } + + public FileUploadProgressExt setFiles(List files) { + this.files = files; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgressFile.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgressFile.java new file mode 100644 index 0000000..30181ab --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadProgressFile.java @@ -0,0 +1,90 @@ +package com.dji.sdk.cloudapi.log; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/9 + */ +public class FileUploadProgressFile { + + private LogModuleEnum module; + + private Long size; + + private String deviceSn; + + private String key; + + private String fingerprint; + + private LogFileProgress progress; + + public FileUploadProgressFile() { + } + + @Override + public String toString() { + return "FileUploadProgressFile{" + + "module=" + module + + ", size=" + size + + ", deviceSn='" + deviceSn + '\'' + + ", key='" + key + '\'' + + ", fingerprint='" + fingerprint + '\'' + + ", progress=" + progress + + '}'; + } + + public LogModuleEnum getModule() { + return module; + } + + public FileUploadProgressFile setModule(LogModuleEnum module) { + this.module = module; + return this; + } + + public Long getSize() { + return size; + } + + public FileUploadProgressFile setSize(Long size) { + this.size = size; + return this; + } + + public String getDeviceSn() { + return deviceSn; + } + + public FileUploadProgressFile setDeviceSn(String deviceSn) { + this.deviceSn = deviceSn; + return this; + } + + public String getKey() { + return key; + } + + public FileUploadProgressFile setKey(String key) { + this.key = key; + return this; + } + + public String getFingerprint() { + return fingerprint; + } + + public FileUploadProgressFile setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + return this; + } + + public LogFileProgress getProgress() { + return progress; + } + + public FileUploadProgressFile setProgress(LogFileProgress progress) { + this.progress = progress; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartFile.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartFile.java new file mode 100644 index 0000000..751e38f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartFile.java @@ -0,0 +1,75 @@ +package com.dji.sdk.cloudapi.log; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/7 + */ +public class FileUploadStartFile { + + @NotNull + private String deviceSn; + + @NotNull + @Valid + private List list; + + @NotNull + private LogModuleEnum module; + + @NotNull + private String objectKey; + + public FileUploadStartFile() { + } + + @Override + public String toString() { + return "FileUploadStartFile{" + + "deviceSn='" + deviceSn + '\'' + + ", list=" + list + + ", module=" + module + + ", objectKey='" + objectKey + '\'' + + '}'; + } + + public String getDeviceSn() { + return deviceSn; + } + + public FileUploadStartFile setDeviceSn(String deviceSn) { + this.deviceSn = deviceSn; + return this; + } + + public List getList() { + return list; + } + + public FileUploadStartFile setList(List list) { + this.list = list; + return this; + } + + public LogModuleEnum getModule() { + return module; + } + + public FileUploadStartFile setModule(LogModuleEnum module) { + this.module = module; + return this; + } + + public String getObjectKey() { + return objectKey; + } + + public FileUploadStartFile setObjectKey(String objectKey) { + this.objectKey = objectKey; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartParam.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartParam.java new file mode 100644 index 0000000..df0e6af --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartParam.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.log; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/7 + */ +public class FileUploadStartParam { + + @NotNull + @Size(min = 1, max = 2) + @Valid + private List files; + + public FileUploadStartParam() { + } + + @Override + public String toString() { + return "FileUploadStartParam{" + + "files=" + files + + '}'; + } + + public List getFiles() { + return files; + } + + public FileUploadStartParam setFiles(List files) { + this.files = files; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartRequest.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartRequest.java new file mode 100644 index 0000000..366836a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStartRequest.java @@ -0,0 +1,138 @@ +package com.dji.sdk.cloudapi.log; + +import com.dji.sdk.cloudapi.storage.CredentialsToken; +import com.dji.sdk.cloudapi.storage.OssTypeEnum; +import com.dji.sdk.cloudapi.storage.StsCredentialsResponse; +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/8 + */ +public class FileUploadStartRequest extends BaseModel { + + @NotNull + private String bucket; + + @NotNull + @Valid + private CredentialsToken credentials; + + @NotNull + private String endpoint; + + @NotNull + private String fileStoreDir; + + @NotNull + private OssTypeEnum provider; + + @NotNull + private String fileType = "text_log"; + + @NotNull + @Valid + private FileUploadStartParam params; + + @NotNull + private String region; + + public FileUploadStartRequest(StsCredentialsResponse sts) { + this.bucket = sts.getBucket(); + long expire = sts.getCredentials().getExpire(); + sts.getCredentials().setExpire(System.currentTimeMillis() + (expire - 60) * 1000); + this.credentials = sts.getCredentials(); + this.endpoint = sts.getEndpoint(); + this.fileStoreDir = sts.getObjectKeyPrefix(); + this.provider = sts.getProvider(); + this.region = sts.getRegion(); + } + + public FileUploadStartRequest() { + } + + @Override + public String toString() { + return "FileUploadStartRequest{" + + "bucket='" + bucket + '\'' + + ", credentials=" + credentials + + ", endpoint='" + endpoint + '\'' + + ", fileStoreDir='" + fileStoreDir + '\'' + + ", provider=" + provider + + ", fileType='" + fileType + '\'' + + ", params=" + params + + ", region='" + region + '\'' + + '}'; + } + + public String getBucket() { + return bucket; + } + + public FileUploadStartRequest setBucket(String bucket) { + this.bucket = bucket; + return this; + } + + public CredentialsToken getCredentials() { + return credentials; + } + + public FileUploadStartRequest setCredentials(CredentialsToken credentials) { + this.credentials = credentials; + return this; + } + + public String getEndpoint() { + return endpoint; + } + + public FileUploadStartRequest setEndpoint(String endpoint) { + this.endpoint = endpoint; + return this; + } + + public String getFileStoreDir() { + return fileStoreDir; + } + + public FileUploadStartRequest setFileStoreDir(String fileStoreDir) { + this.fileStoreDir = fileStoreDir; + return this; + } + + public OssTypeEnum getProvider() { + return provider; + } + + public FileUploadStartRequest setProvider(OssTypeEnum provider) { + this.provider = provider; + return this; + } + + public String getFileType() { + return fileType; + } + + public FileUploadStartParam getParams() { + return params; + } + + public FileUploadStartRequest setParams(FileUploadStartParam params) { + this.params = params; + return this; + } + + public String getRegion() { + return region; + } + + public FileUploadStartRequest setRegion(String region) { + this.region = region; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStatusEnum.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStatusEnum.java new file mode 100644 index 0000000..b2f87c8 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadStatusEnum.java @@ -0,0 +1,61 @@ +package com.dji.sdk.cloudapi.log; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public enum FileUploadStatusEnum { + + FILE_PULL("file_pull", false), + + FILE_ZIP("file_zip", false), + + FILE_UPLOADING("file_uploading", false), + + SENT("sent", false), + + IN_PROGRESS("in_progress", false), + + OK("ok", true), + + PAUSED("paused", false), + + REJECTED("rejected", true), + + FAILED("failed", true), + + CANCELED("canceled", true), + + TIMEOUT("timeout", true); + + private final String status; + + private final boolean end; + + FileUploadStatusEnum(String status, boolean end) { + this.status = status; + this.end = end; + } + + public boolean isEnd() { + return end; + } + + @JsonValue + public String getStatus() { + return status; + } + + @JsonCreator + public static FileUploadStatusEnum find(String status) { + return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny() + .orElseThrow(() -> new CloudSDKException(FileUploadStatusEnum.class, status)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadUpdateRequest.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadUpdateRequest.java new file mode 100644 index 0000000..ce62daf --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadUpdateRequest.java @@ -0,0 +1,56 @@ +package com.dji.sdk.cloudapi.log; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class FileUploadUpdateRequest extends BaseModel { + + /** + * Filter list of file + **/ + @NotNull + @Valid + @Size(min = 1, max = 2) + private List moduleList; + + @NotNull + private FileUploadUpdateStatusEnum status; + + public FileUploadUpdateRequest() { + } + + @Override + public String toString() { + return "FileUploadUpdateRequest{" + + "moduleList=" + moduleList + + ", status=" + status + + '}'; + } + + public List getModuleList() { + return moduleList; + } + + public FileUploadUpdateRequest setModuleList(List moduleList) { + this.moduleList = moduleList; + return this; + } + + public FileUploadUpdateStatusEnum getStatus() { + return status; + } + + public FileUploadUpdateRequest setStatus(FileUploadUpdateStatusEnum status) { + this.status = status; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/FileUploadUpdateStatusEnum.java b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadUpdateStatusEnum.java new file mode 100644 index 0000000..64ac6c3 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/FileUploadUpdateStatusEnum.java @@ -0,0 +1,34 @@ +package com.dji.sdk.cloudapi.log; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public enum FileUploadUpdateStatusEnum { + + CANCEL("cancel"); + + private final String status; + + FileUploadUpdateStatusEnum(String status) { + this.status = status; + } + + @JsonValue + public String getStatus() { + return status; + } + + @JsonCreator + public static FileUploadUpdateStatusEnum find(String status) { + return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny() + .orElseThrow(() -> new CloudSDKException(FileUploadUpdateStatusEnum.class, status)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/LogErrorCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/log/LogErrorCodeEnum.java new file mode 100644 index 0000000..7f70da1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/LogErrorCodeEnum.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.log; + +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.mqtt.events.IEventsErrorCode; +import com.dji.sdk.mqtt.services.IServicesErrorCode; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public enum LogErrorCodeEnum implements IServicesErrorCode, IEventsErrorCode, IErrorInfo { + + DEVICE_RESTART(324001, "Device restart interrupts log export."), + + EXPORT_TIMEOUT(324012, "Compressing logs timed out. Too many logs selected. Unselect some logs and try again."), + + PULL_FAILED(324013, "Failed to obtain device log list. Try again later."), + + EMPTY_LOG_LIST(324014, "Device log list is empty. Refresh page or restart dock and try again."), + + AIRCRAFT_SHUTDOWN(324015, "Aircraft powered off or not connected. Unable to obtain log list. Make sure aircraft is inside dock. Remotely power on aircraft and try again."), + + INSUFFICIENT_STORAGE_SPACE(324016, "Insufficient dock storage space. Failed to compress logs. Clear space or try again later."), + + NO_LOG(324017, "Failed to compress logs. Unable to obtain logs of selected aircraft. Refresh page or restart dock and try again."), + + COMPRESSION_FAILED(324018, "Failed to compress logs and submit issue report. Try again later or restart dock and try again."), + + UPLOAD_FAILED(324019, "Due to network anomalies at the airport, the log upload has failed. Please retry later."), + + UNKNOWN(-1, "UNKNOWN"), + + ; + + + private final String msg; + + private final int code; + + LogErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String getMessage() { + return this.msg; + } + + @Override + public Integer getCode() { + return this.code; + } + + /** + * @param code error code + * @return enumeration object + */ + public static LogErrorCodeEnum find(int code) { + return Arrays.stream(values()).filter(codeEnum -> codeEnum.code == code).findAny().orElse(UNKNOWN); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/LogFileIndex.java b/src/main/java/com/dji/sdk/cloudapi/log/LogFileIndex.java new file mode 100644 index 0000000..b830901 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/LogFileIndex.java @@ -0,0 +1,72 @@ +package com.dji.sdk.cloudapi.log; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/7 + */ +public class LogFileIndex { + + @NotNull + private Integer bootIndex; + + @NotNull + private Long endTime; + + @NotNull + private Long startTime; + + @NotNull + private Long size; + + public LogFileIndex() { + } + + @Override + public String toString() { + return "LogFileIndex{" + + "bootIndex=" + bootIndex + + ", endTime=" + endTime + + ", startTime=" + startTime + + ", size=" + size + + '}'; + } + + public Integer getBootIndex() { + return bootIndex; + } + + public LogFileIndex setBootIndex(Integer bootIndex) { + this.bootIndex = bootIndex; + return this; + } + + public Long getEndTime() { + return endTime; + } + + public LogFileIndex setEndTime(Long endTime) { + this.endTime = endTime; + return this; + } + + public Long getStartTime() { + return startTime; + } + + public LogFileIndex setStartTime(Long startTime) { + this.startTime = startTime; + return this; + } + + public Long getSize() { + return size; + } + + public LogFileIndex setSize(Long size) { + this.size = size; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/LogFileProgress.java b/src/main/java/com/dji/sdk/cloudapi/log/LogFileProgress.java new file mode 100644 index 0000000..c60da51 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/LogFileProgress.java @@ -0,0 +1,102 @@ +package com.dji.sdk.cloudapi.log; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/9 + */ +public class LogFileProgress { + + private Integer currentStep; + + private Integer totalStep; + + private Integer progress; + + private Long finishTime; + + private Float uploadRate; + + private FileUploadStatusEnum status; + + private Integer result; + + public LogFileProgress() { + } + + @Override + public String toString() { + return "LogFileProgress{" + + "currentStep=" + currentStep + + ", totalStep=" + totalStep + + ", progress=" + progress + + ", finishTime=" + finishTime + + ", uploadRate=" + uploadRate + + ", status=" + status + + ", result=" + result + + '}'; + } + + public Integer getCurrentStep() { + return currentStep; + } + + public LogFileProgress setCurrentStep(Integer currentStep) { + this.currentStep = currentStep; + return this; + } + + public Integer getTotalStep() { + return totalStep; + } + + public LogFileProgress setTotalStep(Integer totalStep) { + this.totalStep = totalStep; + return this; + } + + public Integer getProgress() { + return progress; + } + + public LogFileProgress setProgress(Integer progress) { + this.progress = progress; + return this; + } + + public Long getFinishTime() { + return finishTime; + } + + public LogFileProgress setFinishTime(Long finishTime) { + this.finishTime = finishTime; + return this; + } + + public Float getUploadRate() { + return uploadRate; + } + + public LogFileProgress setUploadRate(Float uploadRate) { + this.uploadRate = uploadRate; + return this; + } + + public FileUploadStatusEnum getStatus() { + return status; + } + + public LogFileProgress setStatus(FileUploadStatusEnum status) { + this.status = status; + return this; + } + + public Integer getResult() { + return result; + } + + public LogFileProgress setResult(Integer result) { + this.result = result; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/LogMethodEnum.java b/src/main/java/com/dji/sdk/cloudapi/log/LogMethodEnum.java new file mode 100644 index 0000000..025d387 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/LogMethodEnum.java @@ -0,0 +1,25 @@ +package com.dji.sdk.cloudapi.log; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +public enum LogMethodEnum { + + FILE_UPLOAD_LIST("fileupload_list"), + + FILE_UPLOAD_START("fileupload_start"), + + FILE_UPLOAD_UPDATE("fileupload_update"); + + private final String method; + + LogMethodEnum(String method) { + this.method = method; + } + + public String getMethod() { + return method; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/LogModuleEnum.java b/src/main/java/com/dji/sdk/cloudapi/log/LogModuleEnum.java new file mode 100644 index 0000000..2354933 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/LogModuleEnum.java @@ -0,0 +1,36 @@ +package com.dji.sdk.cloudapi.log; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/29 + */ +public enum LogModuleEnum { + + DRONE("0"), + + DOCK ("3"); + + private final String domain; + + LogModuleEnum(String domain) { + this.domain = domain; + } + + @JsonCreator + public static LogModuleEnum find(String domain) { + return Arrays.stream(values()).filter(domainEnum -> domainEnum.domain.equals(domain)).findAny() + .orElseThrow(() -> new CloudSDKException(LogModuleEnum.class, domain)); + } + + @JsonValue + public String getDomain() { + return domain; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/log/api/AbstractLogService.java b/src/main/java/com/dji/sdk/cloudapi/log/api/AbstractLogService.java new file mode 100644 index 0000000..8958fa8 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/log/api/AbstractLogService.java @@ -0,0 +1,81 @@ +package com.dji.sdk.cloudapi.log.api; + +import com.dji.sdk.cloudapi.log.*; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.services.ServicesPublish; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/28 + */ +public abstract class AbstractLogService { + + @Resource + private ServicesPublish servicesPublish; + + /** + * Inform of file uploading progress + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILEUPLOAD_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse fileuploadProgress(TopicEventsRequest> request, MessageHeaders headers) { + throw new UnsupportedOperationException("fileuploadProgress not implemented"); + } + + /** + * Get file list of uploadable device + * @param gateway + * @param request data + * @return services_reply + */ + public TopicServicesResponse> fileuploadList(GatewayManager gateway, FileUploadListRequest request) { + return servicesPublish.publish( + new TypeReference() {}, + gateway.getGatewaySn(), + LogMethodEnum.FILE_UPLOAD_LIST.getMethod(), + request); + } + + /** + * Start the log file uploading + * @param gateway + * @param request data + * @return services_reply + */ + public TopicServicesResponse fileuploadStart(GatewayManager gateway, FileUploadStartRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + LogMethodEnum.FILE_UPLOAD_START.getMethod(), + request); + } + + /** + * Update the uploding state + * @param gateway + * @param request data + * @return services_reply + */ + public TopicServicesResponse fileuploadUpdate(GatewayManager gateway, FileUploadUpdateRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + LogMethodEnum.FILE_UPLOAD_UPDATE.getMethod(), + request); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/CreateMapElementRequest.java b/src/main/java/com/dji/sdk/cloudapi/map/CreateMapElementRequest.java new file mode 100644 index 0000000..8c5947c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/CreateMapElementRequest.java @@ -0,0 +1,68 @@ +package com.dji.sdk.cloudapi.map; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@Schema(description = "Create element request data") +public class CreateMapElementRequest { + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "element id", format = "uuid") + private String id; + + @Schema(description = "element name", example = "PILOT 1") + @NotNull + private String name; + + @NotNull + @Valid + private ElementResource resource; + + public CreateMapElementRequest() { + } + + @Override + public String toString() { + return "CreateMapElementRequest{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", resource=" + resource + + '}'; + } + + public String getId() { + return id; + } + + public CreateMapElementRequest setId(String id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public CreateMapElementRequest setName(String name) { + this.name = name; + return this; + } + + public ElementResource getResource() { + return resource; + } + + public CreateMapElementRequest setResource(ElementResource resource) { + this.resource = resource; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/CreateMapElementResponse.java b/src/main/java/com/dji/sdk/cloudapi/map/CreateMapElementResponse.java new file mode 100644 index 0000000..26769ed --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/CreateMapElementResponse.java @@ -0,0 +1,39 @@ +package com.dji.sdk.cloudapi.map; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/16 + */ +@Schema(description = "Create element response data") +public class CreateMapElementResponse { + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "element id", format = "uuid") + private String id; + + public CreateMapElementResponse() { + } + + @Override + public String toString() { + return "CreateMapElementResponse{" + + "id='" + id + '\'' + + '}'; + } + + public String getId() { + return id; + } + + public CreateMapElementResponse setId(String id) { + this.id = id; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementContent.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementContent.java new file mode 100644 index 0000000..b6aefbe --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementContent.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.map; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@Schema(description = "element content") +public class ElementContent { + + @Schema(defaultValue = "Feature", allowableValues = "Feature") + @NotNull + private String type = "Feature"; + + @NotNull + @Valid + private ElementProperty properties; + + @Valid + @NotNull + private ElementGeometryType geometry; + + public ElementContent() { + } + + @Override + public String toString() { + return "ElementContent{" + + "type='" + type + '\'' + + ", properties=" + properties + + ", geometry=" + geometry + + '}'; + } + + public String getType() { + return type; + } + + public ElementContent setType(String type) { + this.type = type; + return this; + } + + public ElementProperty getProperties() { + return properties; + } + + public ElementContent setProperties(ElementProperty properties) { + this.properties = properties; + return this; + } + + public ElementGeometryType getGeometry() { + return geometry; + } + + public ElementContent setGeometry(ElementGeometryType geometry) { + this.geometry = geometry; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementCoordinate.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementCoordinate.java new file mode 100644 index 0000000..8932943 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementCoordinate.java @@ -0,0 +1,64 @@ +package com.dji.sdk.cloudapi.map; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@Schema(description = "The coordinates of the element, the coordinate system is WGS84") +public class ElementCoordinate { + + @Schema(description = "longitude") + @NotNull + private Double longitude; + + @Schema(description = "latitude") + @NotNull + private Double latitude; + + @Schema(description = "altitude") + private Double altitude; + + public ElementCoordinate() { + } + + @Override + public String toString() { + return "ElementCoordinate{" + + "longitude=" + longitude + + ", latitude=" + latitude + + ", altitude=" + altitude + + '}'; + } + + public Double getLongitude() { + return longitude; + } + + public ElementCoordinate setLongitude(Double longitude) { + this.longitude = longitude; + return this; + } + + public Double getLatitude() { + return latitude; + } + + public ElementCoordinate setLatitude(Double latitude) { + this.latitude = latitude; + return this; + } + + public Double getAltitude() { + return altitude; + } + + public ElementCoordinate setAltitude(Double altitude) { + this.altitude = altitude; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementGeometryType.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementGeometryType.java new file mode 100644 index 0000000..0086762 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementGeometryType.java @@ -0,0 +1,53 @@ +package com.dji.sdk.cloudapi.map; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", + include = JsonTypeInfo.As.EXISTING_PROPERTY, defaultImpl = ElementGeometryType.class) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ElementPointGeometry.class, name = "Point"), + @JsonSubTypes.Type(value = ElementLineStringGeometry.class, name = "LineString"), + @JsonSubTypes.Type(value = ElementPolygonGeometry.class, name = "Polygon") +}) +@Schema(oneOf = {ElementPointGeometry.class, ElementLineStringGeometry.class, ElementPolygonGeometry.class}) +public abstract class ElementGeometryType { + + private String type; + + ElementGeometryType(String type) { + this.type = type; + } + + public ElementGeometryType() { + } + + public String getType() { + return type; + } + + public ElementGeometryType setType(String type) { + this.type = type; + return this; + } + + /** + * Convert coordinate data into objects and then add them to the collection. + * @return + */ + public abstract List convertToList(); + + /** + * Converts coordinates in a collection of objects to a specific type. + * @param coordinateList + */ + public abstract void adapterCoordinateType(List coordinateList); +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementLineStringGeometry.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementLineStringGeometry.java new file mode 100644 index 0000000..5aef1cf --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementLineStringGeometry.java @@ -0,0 +1,81 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.util.CollectionUtils; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@Schema(description = "line geometry") +public class ElementLineStringGeometry extends ElementGeometryType { + + @Schema(example = "LineString") + @NotNull + private String type = ElementResourceTypeEnum.LINE_STRING.getTypeName(); + + @Schema(example = "[[113.943109, 22.577378]]") + @NotNull + @Size(min = 2) + private Double[][] coordinates; + + public ElementLineStringGeometry() { + super(); + } + + @Override + public List convertToList() { + if (this.coordinates.length < 2) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + List coordinateList = new ArrayList<>(); + for (Double[] coordinate : this.coordinates) { + coordinateList.add(new ElementCoordinate() + .setLongitude(coordinate[0]) + .setLatitude(coordinate[1])); + } + return coordinateList; + } + + @Override + public void adapterCoordinateType(List coordinateList) { + if (CollectionUtils.isEmpty(coordinateList) || coordinateList.size() < 2) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + this.coordinates = new Double[coordinateList.size()][2]; + for (int i = 0; i < this.coordinates.length; i++) { + this.coordinates[i][0] = coordinateList.get(i).getLongitude(); + this.coordinates[i][1] = coordinateList.get(i).getLatitude(); + } + } + + @Override + public String toString() { + return "ElementLineStringGeometry{" + + "coordinates=" + Arrays.toString(coordinates) + + '}'; + } + + public Double[][] getCoordinates() { + return coordinates; + } + + public ElementLineStringGeometry setCoordinates(Double[][] coordinates) { + this.coordinates = coordinates; + return this; + } + + @Override + public String getType() { + return type; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementPointGeometry.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementPointGeometry.java new file mode 100644 index 0000000..5bed461 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementPointGeometry.java @@ -0,0 +1,77 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.util.CollectionUtils; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@Schema(description = "point geometry") +public class ElementPointGeometry extends ElementGeometryType { + + @Schema(example = "Point") + @NotNull + private String type = ElementResourceTypeEnum.POINT.getTypeName(); + + @Schema(example = "[113.943109, 22.577378]") + @NotNull + @Size(min = 2, max = 3) + private Double[] coordinates; + + public ElementPointGeometry() { + super(); + } + + @Override + public List convertToList() { + List coordinateList = new ArrayList<>(); + coordinateList.add(new ElementCoordinate() + .setLongitude(this.coordinates[0]) + .setLatitude(this.coordinates[1]) + .setAltitude(this.coordinates.length == 3 ? this.coordinates[2] : null)); + return coordinateList; + } + + @Override + public void adapterCoordinateType(List coordinateList) { + if (CollectionUtils.isEmpty(coordinateList)) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + this.coordinates = new Double[]{ + coordinateList.get(0).getLongitude(), + coordinateList.get(0).getLatitude(), + coordinateList.get(0).getAltitude() + }; + } + + @Override + public String toString() { + return "ElementPointGeometry{" + + "coordinates=" + Arrays.toString(coordinates) + + '}'; + } + + public Double[] getCoordinates() { + return coordinates; + } + + public ElementPointGeometry setCoordinates(Double[] coordinates) { + this.coordinates = coordinates; + return this; + } + + @Override + public String getType() { + return type; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementPolygonGeometry.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementPolygonGeometry.java new file mode 100644 index 0000000..7068c9d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementPolygonGeometry.java @@ -0,0 +1,82 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sample.map.model.enums.ElementTypeEnum; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.util.CollectionUtils; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@Schema(description = "polygon geometry") +public class ElementPolygonGeometry extends ElementGeometryType { + + @Schema(example = "Polygon") + @NotNull + private String type = ElementTypeEnum.POLYGON.getDesc(); + + @Schema(example = "[[[113.943109, 22.577378]]]") + @NotNull + @Size(min = 1, max = 1) + private Double[][][] coordinates; + + public ElementPolygonGeometry() { + super(); + } + + @Override + public List convertToList() { + if (this.coordinates[0].length < 3) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + List coordinateList = new ArrayList<>(); + for (Double[] coordinate : this.coordinates[0]) { + coordinateList.add(new ElementCoordinate() + .setLongitude(coordinate[0]) + .setLatitude(coordinate[1])); + } + return coordinateList; + } + + @Override + public void adapterCoordinateType(List coordinateList) { + if (CollectionUtils.isEmpty(coordinateList) || coordinateList.size() < 3) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + this.coordinates = new Double[1][coordinateList.size()][2]; + for (int i = 0; i < this.coordinates[0].length; i++) { + this.coordinates[0][i][0] = coordinateList.get(i).getLongitude(); + this.coordinates[0][i][1] = coordinateList.get(i).getLatitude(); + } + } + + @Override + public String toString() { + return "ElementPolygonGeometry{" + + "coordinates=" + Arrays.toString(coordinates) + + '}'; + } + + public Double[][][] getCoordinates() { + return coordinates; + } + + public ElementPolygonGeometry setCoordinates(Double[][][] coordinates) { + this.coordinates = coordinates; + return this; + } + + @Override + public String getType() { + return type; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementProperty.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementProperty.java new file mode 100644 index 0000000..d3a35aa --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementProperty.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.map; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@Schema(description = "properties of the element") +public class ElementProperty { + + @NotNull + @Schema(description = "element color", example = "#2D8CF0") + @Pattern(regexp = "^#[0-9a-fA-F]{6}$") + private String color; + + @JsonProperty("clampToGround") + @Schema(description = "Whether it is on the ground.") + private Boolean clampToGround; + + public ElementProperty() { + } + + @Override + public String toString() { + return "ElementProperty{" + + "color='" + color + '\'' + + ", clampToGround=" + clampToGround + + '}'; + } + + public String getColor() { + return color; + } + + public ElementProperty setColor(String color) { + this.color = color; + return this; + } + + public Boolean getClampToGround() { + return clampToGround; + } + + public ElementProperty setClampToGround(Boolean clampToGround) { + this.clampToGround = clampToGround; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementResource.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementResource.java new file mode 100644 index 0000000..33571d2 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementResource.java @@ -0,0 +1,67 @@ +package com.dji.sdk.cloudapi.map; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/29 + */ +@Schema(description = "element resource") +public class ElementResource { + + @NotNull + @Schema(type = "int", enumAsRef = true) + private ElementResourceTypeEnum type; + + @Schema(description = "the user who created the element", example = "pilot") + @JsonProperty(value = "user_name") + private String username; + + @NotNull + @Valid + private ElementContent content; + + public ElementResource() { + } + + @Override + public String toString() { + return "ElementResource{" + + "type=" + type + + ", username='" + username + '\'' + + ", content=" + content + + '}'; + } + + public ElementResourceTypeEnum getType() { + return type; + } + + public ElementResource setType(ElementResourceTypeEnum type) { + this.type = type; + return this; + } + + public String getUsername() { + return username; + } + + public ElementResource setUsername(String username) { + this.username = username; + return this; + } + + public ElementContent getContent() { + return content; + } + + public ElementResource setContent(ElementContent content) { + this.content = content; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/ElementResourceTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/map/ElementResourceTypeEnum.java new file mode 100644 index 0000000..423be37 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/ElementResourceTypeEnum.java @@ -0,0 +1,48 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Arrays; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/30 + */ +@Schema(enumAsRef = true, description = "

0: Point

1: LineString

2: Polygon

", allowableValues = {"0", "1", "2"}) +public enum ElementResourceTypeEnum { + + POINT(0, "Point"), + + LINE_STRING(1, "LineString"), + + POLYGON(2, "Polygon"); + + private final int type; + + private final String typeName; + + ElementResourceTypeEnum(int type, String typeName) { + this.type = type; + this.typeName = typeName; + } + + public String getTypeName() { + return typeName; + } + + @JsonValue + public int getType() { + return type; + } + + @JsonCreator + public static ElementResourceTypeEnum find(int type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(ElementResourceTypeEnum.class, type)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/GetMapElementsResponse.java b/src/main/java/com/dji/sdk/cloudapi/map/GetMapElementsResponse.java new file mode 100644 index 0000000..d2b0c71 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/GetMapElementsResponse.java @@ -0,0 +1,100 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.util.List; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/29 + */ +@Schema(description = "element group data") +public class GetMapElementsResponse extends BaseModel { + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "group id", format = "uuid") + private String id; + + @NotNull + @Schema(description = "group name", example = "Pilot Share Layer") + private String name; + + @NotNull + private GroupTypeEnum type; + + @NotNull + @Valid + @Schema(description = "data collection of elements") + private List elements; + + @JsonProperty(value = "is_lock") + @NotNull + @Schema(description = "Whether the element group is locked.") + private Boolean lock; + + public GetMapElementsResponse() { + } + + @Override + public String toString() { + return "GetMapElementsResponse{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", type=" + type + + ", elements=" + elements + + ", lock=" + lock + + '}'; + } + + public String getId() { + return id; + } + + public GetMapElementsResponse setId(String id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public GetMapElementsResponse setName(String name) { + this.name = name; + return this; + } + + public GroupTypeEnum getType() { + return type; + } + + public GetMapElementsResponse setType(GroupTypeEnum type) { + this.type = type; + return this; + } + + public List getElements() { + return elements; + } + + public GetMapElementsResponse setElements(List elements) { + this.elements = elements; + return this; + } + + public Boolean getLock() { + return lock; + } + + public GetMapElementsResponse setLock(Boolean lock) { + this.lock = lock; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/GroupTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/map/GroupTypeEnum.java new file mode 100644 index 0000000..1319a57 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/GroupTypeEnum.java @@ -0,0 +1,44 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/15 + */ +@Schema(description = "

0: custom element group

1: default element group

2: APP shared element group " + + "(type=2 is an APP element group, PILOT will add map elements to this element group by default, " + + "and there must be an APP shared element group. " + + "It is recommended that in the same workspace, there are And there is only one APP shared element group)

", + enumAsRef = true, type = "int", allowableValues = {"0", "1", "2"}) +public enum GroupTypeEnum { + + NORMAL(0), + + DEFAULT(1), + + SHARED(2); + + private final int type; + + GroupTypeEnum(int type) { + this.type = type; + } + + @JsonValue + public int getType() { + return type; + } + + @JsonCreator + public static GroupTypeEnum find(int type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(GroupTypeEnum.class, type)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/MapElementCreateWsResponse.java b/src/main/java/com/dji/sdk/cloudapi/map/MapElementCreateWsResponse.java new file mode 100644 index 0000000..74933f5 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/MapElementCreateWsResponse.java @@ -0,0 +1,120 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/19 + */ +@Schema(description = "BizCode: map_element_create.

Websocket response data when element is created.

") +public class MapElementCreateWsResponse extends BaseModel { + + @JsonProperty("group_id") + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "group id", format = "uuid") + private String groupId; + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "element id", format = "uuid") + private String id; + + @NotNull + @Schema(description = "element name", example = "PILOT 1") + private String name; + + @NotNull + @Schema(description = "element create time", example = "123456789012") + @JsonProperty(value = "create_time") + @Min(123456789012L) + private Long createTime; + + @NotNull + @Schema(description = "element update time", example = "123456789012") + @JsonProperty(value = "update_time") + @Min(123456789012L) + private Long updateTime; + + @NotNull + @Valid + private ElementResource resource; + + public MapElementCreateWsResponse() { + } + + @Override + public String toString() { + return "MapElementCreateWsResponse{" + + "groupId='" + groupId + '\'' + + ", id='" + id + '\'' + + ", name='" + name + '\'' + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + ", resource=" + resource + + '}'; + } + + public String getId() { + return id; + } + + public MapElementCreateWsResponse setId(String id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public MapElementCreateWsResponse setName(String name) { + this.name = name; + return this; + } + + public Long getCreateTime() { + return createTime; + } + + public MapElementCreateWsResponse setCreateTime(Long createTime) { + this.createTime = createTime; + return this; + } + + public Long getUpdateTime() { + return updateTime; + } + + public MapElementCreateWsResponse setUpdateTime(Long updateTime) { + this.updateTime = updateTime; + return this; + } + + public ElementResource getResource() { + return resource; + } + + public MapElementCreateWsResponse setResource(ElementResource resource) { + this.resource = resource; + return this; + } + + public String getGroupId() { + return groupId; + } + + public MapElementCreateWsResponse setGroupId(String groupId) { + this.groupId = groupId; + return this; + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/MapElementDeleteWsResponse.java b/src/main/java/com/dji/sdk/cloudapi/map/MapElementDeleteWsResponse.java new file mode 100644 index 0000000..fdf48ae --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/MapElementDeleteWsResponse.java @@ -0,0 +1,57 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/19 + */ +@Schema(description = "BizCode: map_element_delete.

Websocket response data when element is deleted.

") +public class MapElementDeleteWsResponse extends BaseModel { + + @JsonProperty("group_id") + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "group id", format = "uuid") + private String groupId; + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "element id", format = "uuid") + private String id; + + public MapElementDeleteWsResponse() { + } + + @Override + public String toString() { + return "MapElementDeleteWsResponse{" + + "groupId='" + groupId + '\'' + + ", id='" + id + '\'' + + '}'; + } + + public String getGroupId() { + return groupId; + } + + public MapElementDeleteWsResponse setGroupId(String groupId) { + this.groupId = groupId; + return this; + } + + public String getId() { + return id; + } + + public MapElementDeleteWsResponse setId(String id) { + this.id = id; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/MapElementUpdateWsResponse.java b/src/main/java/com/dji/sdk/cloudapi/map/MapElementUpdateWsResponse.java new file mode 100644 index 0000000..2ace077 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/MapElementUpdateWsResponse.java @@ -0,0 +1,120 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/19 + */ +@Schema(description = "BizCode: map_element_update.

Websocket response data when the element is updated.

") +public class MapElementUpdateWsResponse extends BaseModel { + + @JsonProperty("group_id") + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "group id", format = "uuid") + private String groupId; + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "element id", format = "uuid") + private String id; + + @NotNull + @Schema(description = "element name", example = "PILOT 1") + private String name; + + @NotNull + @Schema(description = "element create time", example = "123456789012") + @JsonProperty(value = "create_time") + @Min(123456789012L) + private Long createTime; + + @NotNull + @Schema(description = "element update time", example = "123456789012") + @JsonProperty(value = "update_time") + @Min(123456789012L) + private Long updateTime; + + @NotNull + @Valid + private ElementResource resource; + + public MapElementUpdateWsResponse() { + } + + @Override + public String toString() { + return "MapElementUpdateWsResponse{" + + "groupId='" + groupId + '\'' + + ", id='" + id + '\'' + + ", name='" + name + '\'' + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + ", resource=" + resource + + '}'; + } + + public String getId() { + return id; + } + + public MapElementUpdateWsResponse setId(String id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public MapElementUpdateWsResponse setName(String name) { + this.name = name; + return this; + } + + public Long getCreateTime() { + return createTime; + } + + public MapElementUpdateWsResponse setCreateTime(Long createTime) { + this.createTime = createTime; + return this; + } + + public Long getUpdateTime() { + return updateTime; + } + + public MapElementUpdateWsResponse setUpdateTime(Long updateTime) { + this.updateTime = updateTime; + return this; + } + + public ElementResource getResource() { + return resource; + } + + public MapElementUpdateWsResponse setResource(ElementResource resource) { + this.resource = resource; + return this; + } + + public String getGroupId() { + return groupId; + } + + public MapElementUpdateWsResponse setGroupId(String groupId) { + this.groupId = groupId; + return this; + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/MapGroupElement.java b/src/main/java/com/dji/sdk/cloudapi/map/MapGroupElement.java new file mode 100644 index 0000000..f360571 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/MapGroupElement.java @@ -0,0 +1,102 @@ +package com.dji.sdk.cloudapi.map; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/29 + */ +@Schema(description = "element data") +public class MapGroupElement { + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "element id", format = "uuid") + private String id; + + @NotNull + @Schema(description = "element name", example = "PILOT 1") + private String name; + + @NotNull + @Schema(description = "element create time", example = "123456789012") + @JsonProperty(value = "create_time") + @Min(123456789012L) + private Long createTime; + + @NotNull + @Schema(description = "element update time", example = "123456789012") + @JsonProperty(value = "update_time") + @Min(123456789012L) + private Long updateTime; + + @NotNull + @Valid + private ElementResource resource; + + public MapGroupElement() { + } + + @Override + public String toString() { + return "MapGroupElement{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + ", resource=" + resource + + '}'; + } + + public String getId() { + return id; + } + + public MapGroupElement setId(String id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public MapGroupElement setName(String name) { + this.name = name; + return this; + } + + public Long getCreateTime() { + return createTime; + } + + public MapGroupElement setCreateTime(Long createTime) { + this.createTime = createTime; + return this; + } + + public Long getUpdateTime() { + return updateTime; + } + + public MapGroupElement setUpdateTime(Long updateTime) { + this.updateTime = updateTime; + return this; + } + + public ElementResource getResource() { + return resource; + } + + public MapGroupElement setResource(ElementResource resource) { + this.resource = resource; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/MapGroupRefreshWsResponse.java b/src/main/java/com/dji/sdk/cloudapi/map/MapGroupRefreshWsResponse.java new file mode 100644 index 0000000..b30c2ea --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/MapGroupRefreshWsResponse.java @@ -0,0 +1,45 @@ +package com.dji.sdk.cloudapi.map; + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/19 + */ +@Schema(description = "BizCode: map_group_refresh.

When several elements have changed on the server end, such as drag an element on web end, the user end can be notified through WebSocket. " + + "The downward parameter has the layer group_id. The user end can call \"*Obtain Map Element List*\" to refresh the element list through http after receiving the ID.

") +public class MapGroupRefreshWsResponse extends BaseModel { + + @JsonProperty("ids") + @NotNull + @Size(min = 1) + @Schema(description = "group id collection", format = "uuid") + private List<@Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") String> ids; + + public MapGroupRefreshWsResponse() { + } + + @Override + public String toString() { + return "MapGroupRefreshWsResponse{" + + "ids=" + ids + + '}'; + } + + public List getIds() { + return ids; + } + + public MapGroupRefreshWsResponse setIds(List ids) { + this.ids = ids; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/UpdateMapElementRequest.java b/src/main/java/com/dji/sdk/cloudapi/map/UpdateMapElementRequest.java new file mode 100644 index 0000000..1c712be --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/UpdateMapElementRequest.java @@ -0,0 +1,52 @@ +package com.dji.sdk.cloudapi.map; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/1 + */ +@Schema(description = "Update element request data") +public class UpdateMapElementRequest { + + @Schema(description = "element name", example = "PILOT 1") + @NotNull + private String name; + + @NotNull + @Valid + private ElementContent content; + + public UpdateMapElementRequest() { + } + + @Override + public String toString() { + return "UpdateMapElementRequest{" + + "name='" + name + '\'' + + ", content=" + content + + '}'; + } + + public String getName() { + return name; + } + + public UpdateMapElementRequest setName(String name) { + this.name = name; + return this; + } + + public ElementContent getContent() { + return content; + } + + public UpdateMapElementRequest setContent(ElementContent content) { + this.content = content; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/map/api/IHttpMapService.java b/src/main/java/com/dji/sdk/cloudapi/map/api/IHttpMapService.java new file mode 100644 index 0000000..be0530b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/map/api/IHttpMapService.java @@ -0,0 +1,124 @@ +package com.dji.sdk.cloudapi.map.api; + +import com.dji.sdk.cloudapi.map.CreateMapElementRequest; +import com.dji.sdk.cloudapi.map.CreateMapElementResponse; +import com.dji.sdk.cloudapi.map.GetMapElementsResponse; +import com.dji.sdk.cloudapi.map.UpdateMapElementRequest; +import com.dji.sdk.common.HttpResultResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.util.List; + +/** + * @author sean + * @version 0.2 + * @date 2021/11/29 + */ +@Tag(name = "map interface") +public interface IHttpMapService { + + String PREFIX = "map/api/v1"; + + /** + * In the first connection, pilot will send out this http request to get the group element list. + * Also, if pilot receives a group refresh instruction from WebSocket, + * it needs the same interface to request the group element list. + * @param workspaceId + * @param groupId + * @param isDistributed + * @param req + * @param rsp + * @return + */ + @Operation(summary = "get map elements", description = "In the first connection, pilot will send out this http " + + "request to get the group element list. Also, if pilot receives a group refresh instruction from " + + "WebSocket, it needs the same interface to request the group element list.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")), + @Parameter(name = "group_id", description = "element group id. The same element group can contain " + + "multiple map elements, which is equivalent to grouping map elements. " + + "When initiating the request, if the group id parameter is not included, " + + "the server needs to return all map elements. If the group id is specified, " + + "it only needs to return the set of elements in the specified element group", schema = @Schema(format = "uuid")), + @Parameter(name = "is_distributed", description = "Whether the element group is distributed.") + }) + @GetMapping(PREFIX + "/workspaces/{workspace_id}/element-groups") + HttpResultResponse> getMapElements( + @PathVariable(name = "workspace_id") String workspaceId, + @RequestParam(name = "group_id", required = false) String groupId, + @RequestParam(name = "is_distributed", required = false) Boolean isDistributed, + HttpServletRequest req, HttpServletResponse rsp); + + /** + * When user draws a point, line or polygon on the PILOT/Web side. + * @param workspaceId + * @param groupId + * @param elementCreate + * @param req + * @param rsp + * @return + */ + @Operation(summary = "create map element", description = "When user draws a point, line or polygon on the PILOT/Web side.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")), + @Parameter(name = "group_id", description = "element group id. The same element group can contain " + + "multiple map elements, which is equivalent to grouping map elements. " + + "When initiating the request, if the group id parameter is not included, " + + "the server needs to return all map elements. If the group id is specified, " + + "it only needs to return the set of elements in the specified element group", schema = @Schema(format = "uuid")) + }) + @PostMapping(PREFIX + "/workspaces/{workspace_id}/element-groups/{group_id}/elements") + HttpResultResponse createMapElement( + @PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "group_id") String groupId, + @Valid @RequestBody CreateMapElementRequest elementCreate, + HttpServletRequest req, HttpServletResponse rsp); + + + /** + * When user edits a point, line or polygon on the PILOT/Web side. + * @param workspaceId + * @param elementId + * @param elementUpdate + * @param req + * @param rsp + * @return + */ + @Operation(summary = "update map element", description = "When user edits a point, line or polygon on the PILOT/Web side.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")), + @Parameter(name = "element_id", description = "element id", schema = @Schema(format = "uuid")) + }) + @PutMapping(PREFIX + "/workspaces/{workspace_id}/elements/{element_id}") + HttpResultResponse updateMapElement( + @PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "element_id") String elementId, + @Valid @RequestBody UpdateMapElementRequest elementUpdate, + HttpServletRequest req, HttpServletResponse rsp); + + + /** + * When user delete a point, line or polygon on the PILOT/Web side. + * @param workspaceId + * @param elementId + * @return + */ + @Operation(summary = "delete map element", description = "When user delete a point, line or polygon on the PILOT/Web side.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")), + @Parameter(name = "element_id", description = "element id", schema = @Schema(format = "uuid")) + }) + @DeleteMapping(PREFIX + "/workspaces/{workspace_id}/elements/{element_id}") + HttpResultResponse deleteMapElement( + @PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "element_id") String elementId, + HttpServletRequest req, HttpServletResponse rsp); + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/media/FastUploadExtension.java b/src/main/java/com/dji/sdk/cloudapi/media/FastUploadExtension.java new file mode 100644 index 0000000..5426ceb --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/FastUploadExtension.java @@ -0,0 +1,99 @@ +package com.dji.sdk.cloudapi.media; + +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +@Schema(description = "media file fast upload extension data") +public class FastUploadExtension { + + @NotNull + @JsonProperty("drone_model_key") + @Schema(description = "drone device product enum") + private DeviceEnum droneModelKey; + + @JsonProperty("is_original") + @NotNull + @Schema(description = "Whether the image is the original image.") + private Boolean original; + + @NotNull + @JsonProperty("payload_model_key") + @Schema(description = "payload device product enum", example = "1-42-0") + private DeviceEnum payloadModelKey; + + @NotNull + @JsonProperty("tinny_fingerprint") + @Schema(description = "tiny fingerprint of the file.", example = "297f490b0252690d3f93841818567cc6_2022_8_31_15_16_16") + private String tinnyFingerprint; + + @NotNull + @Schema(description = "drone sn", example = "1AD3CA2VL3LAD6") + private String sn; + + public FastUploadExtension() { + } + + @Override + public String toString() { + return "FastUploadExtension{" + + "droneModelKey=" + droneModelKey + + ", original=" + original + + ", payloadModelKey=" + payloadModelKey + + ", tinnyFingerprint='" + tinnyFingerprint + '\'' + + ", sn='" + sn + '\'' + + '}'; + } + + public DeviceEnum getDroneModelKey() { + return droneModelKey; + } + + public FastUploadExtension setDroneModelKey(DeviceEnum droneModelKey) { + this.droneModelKey = droneModelKey; + return this; + } + + public Boolean getOriginal() { + return original; + } + + public FastUploadExtension setOriginal(Boolean original) { + this.original = original; + return this; + } + + public DeviceEnum getPayloadModelKey() { + return payloadModelKey; + } + + public FastUploadExtension setPayloadModelKey(DeviceEnum payloadModelKey) { + this.payloadModelKey = payloadModelKey; + return this; + } + + public String getTinnyFingerprint() { + return tinnyFingerprint; + } + + public FastUploadExtension setTinnyFingerprint(String tinnyFingerprint) { + this.tinnyFingerprint = tinnyFingerprint; + return this; + } + + public String getSn() { + return sn; + } + + public FastUploadExtension setSn(String sn) { + this.sn = sn; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/FileUploadCallback.java b/src/main/java/com/dji/sdk/cloudapi/media/FileUploadCallback.java new file mode 100644 index 0000000..826c468 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/FileUploadCallback.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.media; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +public class FileUploadCallback { + + private Integer result; + + private Integer progress; + + private FileUploadCallbackFile file; + + public FileUploadCallback() { + } + + @Override + public String toString() { + return "FileUploadCallback{" + + "result=" + result + + ", progress=" + progress + + ", file=" + file + + '}'; + } + + public Integer getResult() { + return result; + } + + public FileUploadCallback setResult(Integer result) { + this.result = result; + return this; + } + + public Integer getProgress() { + return progress; + } + + public FileUploadCallback setProgress(Integer progress) { + this.progress = progress; + return this; + } + + public FileUploadCallbackFile getFile() { + return file; + } + + public FileUploadCallback setFile(FileUploadCallbackFile file) { + this.file = file; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/FileUploadCallbackFile.java b/src/main/java/com/dji/sdk/cloudapi/media/FileUploadCallbackFile.java new file mode 100644 index 0000000..65168d1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/FileUploadCallbackFile.java @@ -0,0 +1,78 @@ +package com.dji.sdk.cloudapi.media; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +public class FileUploadCallbackFile { + + private UploadCallbackFileExtension ext; + + private String name; + + private String path; + + private String objectKey; + + private UploadCallbackFileMetadata metadata; + + public FileUploadCallbackFile() { + } + + @Override + public String toString() { + return "FileUploadCallbackFile{" + + "ext=" + ext + + ", name='" + name + '\'' + + ", path='" + path + '\'' + + ", objectKey='" + objectKey + '\'' + + ", metadata=" + metadata + + '}'; + } + + public UploadCallbackFileExtension getExt() { + return ext; + } + + public FileUploadCallbackFile setExt(UploadCallbackFileExtension ext) { + this.ext = ext; + return this; + } + + public String getName() { + return name; + } + + public FileUploadCallbackFile setName(String name) { + this.name = name; + return this; + } + + public String getPath() { + return path; + } + + public FileUploadCallbackFile setPath(String path) { + this.path = path; + return this; + } + + public String getObjectKey() { + return objectKey; + } + + public FileUploadCallbackFile setObjectKey(String objectKey) { + this.objectKey = objectKey; + return this; + } + + public UploadCallbackFileMetadata getMetadata() { + return metadata; + } + + public FileUploadCallbackFile setMetadata(UploadCallbackFileMetadata metadata) { + this.metadata = metadata; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/FolderUploadCallbackRequest.java b/src/main/java/com/dji/sdk/cloudapi/media/FolderUploadCallbackRequest.java new file mode 100644 index 0000000..78abab4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/FolderUploadCallbackRequest.java @@ -0,0 +1,69 @@ +package com.dji.sdk.cloudapi.media; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/19 + */ +@Schema(description = "folder upload callback request data") +public class FolderUploadCallbackRequest { + + @NotNull + @JsonProperty("file_group_id") + @Schema(description = "file group id", format = "uuid") + private String fileGroupId; + + @NotNull + @JsonProperty("file_count") + @Schema(description = "total amount of media in the file group") + private Integer fileCount; + + @NotNull + @JsonProperty("file_uploaded_count") + @Schema(description = "the number of uploaded media in the file group") + private Integer fileUploadedCount; + + public FolderUploadCallbackRequest() { + } + + @Override + public String toString() { + return "FolderUploadCallbackRequest{" + + "fileGroupId='" + fileGroupId + '\'' + + ", fileCount=" + fileCount + + ", fileUploadedCount=" + fileUploadedCount + + '}'; + } + + public String getFileGroupId() { + return fileGroupId; + } + + public FolderUploadCallbackRequest setFileGroupId(String fileGroupId) { + this.fileGroupId = fileGroupId; + return this; + } + + public Integer getFileCount() { + return fileCount; + } + + public FolderUploadCallbackRequest setFileCount(Integer fileCount) { + this.fileCount = fileCount; + return this; + } + + public Integer getFileUploadedCount() { + return fileUploadedCount; + } + + public FolderUploadCallbackRequest setFileUploadedCount(Integer fileUploadedCount) { + this.fileUploadedCount = fileUploadedCount; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/GetFileFingerprintRequest.java b/src/main/java/com/dji/sdk/cloudapi/media/GetFileFingerprintRequest.java new file mode 100644 index 0000000..7fe08bb --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/GetFileFingerprintRequest.java @@ -0,0 +1,41 @@ +package com.dji.sdk.cloudapi.media; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/16 + */ +@Schema(description = "get request data for tiny fingerprints of existing files") +public class GetFileFingerprintRequest { + + @NotNull + @Schema(description = "tiny fingerprints collection", example = "[\"297f490b0252690d3f93841818567cc6_2022_8_31_15_16_16\"]") + @JsonProperty("tiny_fingerprints") + private List tinyFingerprints; + + public GetFileFingerprintRequest() { + } + + @Override + public String toString() { + return "GetFileFingerprintRequest{" + + "tinyFingerprints=" + tinyFingerprints + + '}'; + } + + public List getTinyFingerprints() { + return tinyFingerprints; + } + + public GetFileFingerprintRequest setTinyFingerprints(List tinyFingerprints) { + this.tinyFingerprints = tinyFingerprints; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/GetFileFingerprintResponse.java b/src/main/java/com/dji/sdk/cloudapi/media/GetFileFingerprintResponse.java new file mode 100644 index 0000000..d0ab21a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/GetFileFingerprintResponse.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.media; + + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/16 + */ +@Schema(description = "response data for tiny fingerprints of existing files") +public class GetFileFingerprintResponse extends BaseModel { + + @NotNull + @Schema(description = "tiny fingerprints collection", example = "[\"297f490b0252690d3f93841818567cc6_2022_8_31_15_16_16\"]") + @JsonProperty("tiny_fingerprints") + private List tinyFingerprints; + + public GetFileFingerprintResponse() { + } + + @Override + public String toString() { + return "GetFileFingerprintResponse{" + + "tinyFingerprints=" + tinyFingerprints + + '}'; + } + + public List getTinyFingerprints() { + return tinyFingerprints; + } + + public GetFileFingerprintResponse setTinyFingerprints(List tinyFingerprints) { + this.tinyFingerprints = tinyFingerprints; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/HighestPriorityUploadFlightTaskMedia.java b/src/main/java/com/dji/sdk/cloudapi/media/HighestPriorityUploadFlightTaskMedia.java new file mode 100644 index 0000000..8eadb0d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/HighestPriorityUploadFlightTaskMedia.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.media; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/27 + */ +public class HighestPriorityUploadFlightTaskMedia { + + private String flightId; + + public HighestPriorityUploadFlightTaskMedia() { + } + + @Override + public String toString() { + return "HighestPriorityUploadFlightTaskMedia{" + + "flightId='" + flightId + '\'' + + '}'; + } + + public String getFlightId() { + return flightId; + } + + public HighestPriorityUploadFlightTaskMedia setFlightId(String flightId) { + this.flightId = flightId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/MediaFastUploadRequest.java b/src/main/java/com/dji/sdk/cloudapi/media/MediaFastUploadRequest.java new file mode 100644 index 0000000..78c22b3 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/MediaFastUploadRequest.java @@ -0,0 +1,79 @@ +package com.dji.sdk.cloudapi.media; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +@Schema(description = "media fast upload request data") +public class MediaFastUploadRequest { + + @NotNull + @Valid + private FastUploadExtension ext; + + @NotNull + @Schema(description = "media file fingerprint", example = "7F78C9F1999425CB61F10E1FE206009E") + private String fingerprint; + + @NotNull + @Schema(description = "media file name", example = "DJI_20220831151616_0004_W_Waypoint4.JPG") + private String name; + + @Schema(description = "media file path. This value is empty if the photo was not taken in the wayline.", example = "DJI_202208311455_008_Waypoint1") + private String path; + + public MediaFastUploadRequest() { + } + + @Override + public String toString() { + return "MediaFastUploadRequest{" + + "ext=" + ext + + ", fingerprint='" + fingerprint + '\'' + + ", name='" + name + '\'' + + ", path='" + path + '\'' + + '}'; + } + + public FastUploadExtension getExt() { + return ext; + } + + public MediaFastUploadRequest setExt(FastUploadExtension ext) { + this.ext = ext; + return this; + } + + public String getFingerprint() { + return fingerprint; + } + + public MediaFastUploadRequest setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + return this; + } + + public String getName() { + return name; + } + + public MediaFastUploadRequest setName(String name) { + this.name = name; + return this; + } + + public String getPath() { + return path; + } + + public MediaFastUploadRequest setPath(String path) { + this.path = path; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/MediaFileExtension.java b/src/main/java/com/dji/sdk/cloudapi/media/MediaFileExtension.java new file mode 100644 index 0000000..8e2f7c9 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/MediaFileExtension.java @@ -0,0 +1,114 @@ +package com.dji.sdk.cloudapi.media; + +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +@Schema(description = "media file upload callback extension data") +public class MediaFileExtension { + + @NotNull + @JsonProperty("drone_model_key") + @Schema(description = "drone device product enum") + private DeviceEnum droneModelKey; + + @NotNull + @JsonProperty("file_group_id") + @Schema(description = "If the media file was shot during the wayline, this value will not be null.", format = "uuid") + private String fileGroupId; + + @JsonProperty("is_original") + @NotNull + @Schema(description = "Whether the image is the original image.") + private Boolean original; + + @NotNull + @JsonProperty("payload_model_key") + @Schema(description = "payload device product enum", example = "1-42-0") + private DeviceEnum payloadModelKey; + + @NotNull + @JsonProperty("tinny_fingerprint") + @Schema(description = "tiny fingerprint of the file.", example = "297f490b0252690d3f93841818567cc6_2022_8_31_15_16_16") + private String tinnyFingerprint; + + @NotNull + @Schema(description = "drone sn", example = "1AD3CA2VL3LAD6") + private String sn; + + public MediaFileExtension() { + } + + @Override + public String toString() { + return "MediaFileExtension{" + + "droneModelKey=" + droneModelKey + + ", fileGroupId='" + fileGroupId + '\'' + + ", original=" + original + + ", payloadModelKey=" + payloadModelKey + + ", tinnyFingerprint='" + tinnyFingerprint + '\'' + + ", sn='" + sn + '\'' + + '}'; + } + + public DeviceEnum getDroneModelKey() { + return droneModelKey; + } + + public MediaFileExtension setDroneModelKey(DeviceEnum droneModelKey) { + this.droneModelKey = droneModelKey; + return this; + } + + public Boolean getOriginal() { + return original; + } + + public MediaFileExtension setOriginal(Boolean original) { + this.original = original; + return this; + } + + public DeviceEnum getPayloadModelKey() { + return payloadModelKey; + } + + public MediaFileExtension setPayloadModelKey(DeviceEnum payloadModelKey) { + this.payloadModelKey = payloadModelKey; + return this; + } + + public String getTinnyFingerprint() { + return tinnyFingerprint; + } + + public MediaFileExtension setTinnyFingerprint(String tinnyFingerprint) { + this.tinnyFingerprint = tinnyFingerprint; + return this; + } + + public String getSn() { + return sn; + } + + public MediaFileExtension setSn(String sn) { + this.sn = sn; + return this; + } + + public String getFileGroupId() { + return fileGroupId; + } + + public MediaFileExtension setFileGroupId(String fileGroupId) { + this.fileGroupId = fileGroupId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/MediaFileMetadata.java b/src/main/java/com/dji/sdk/cloudapi/media/MediaFileMetadata.java new file mode 100644 index 0000000..5a5a17b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/MediaFileMetadata.java @@ -0,0 +1,104 @@ +package com.dji.sdk.cloudapi.media; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +@Schema(description = "media file metadata") +public class MediaFileMetadata { + + @JsonProperty("absolute_altitude") + @NotNull + @Schema(description = "absolute height", example = "-36.889") + private Double absoluteAltitude; + + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssVV") + @NotNull + @Schema(description = "media create time", example = "2023-01-01T20:00:00+08:00") + @JsonProperty("created_time") + private LocalDateTime createdTime; + + @NotNull + @JsonProperty("gimbal_yaw_degree") + @Schema(description = "gimbal yaw degree", example = "-4.3") + private Double gimbalYawDegree; + + @NotNull + @JsonProperty("shoot_position") + @Valid + @Schema(description = "The latitude and longitude of the drone when the photo was taken") + private Position shootPosition; + + @NotNull + @JsonProperty("relative_altitude") + @Schema(description = "relative altitude", example = "100.001") + private Double relativeAltitude; + + public MediaFileMetadata() { + } + + @Override + public String toString() { + return "MediaFileMetadata{" + + "absoluteAltitude=" + absoluteAltitude + + ", createdTime=" + createdTime + + ", gimbalYawDegree=" + gimbalYawDegree + + ", shootPosition=" + shootPosition + + ", relativeAltitude=" + relativeAltitude + + '}'; + } + + public Double getAbsoluteAltitude() { + return absoluteAltitude; + } + + public MediaFileMetadata setAbsoluteAltitude(Double absoluteAltitude) { + this.absoluteAltitude = absoluteAltitude; + return this; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } + + public MediaFileMetadata setCreatedTime(LocalDateTime createdTime) { + this.createdTime = createdTime; + return this; + } + + public Double getGimbalYawDegree() { + return gimbalYawDegree; + } + + public MediaFileMetadata setGimbalYawDegree(Double gimbalYawDegree) { + this.gimbalYawDegree = gimbalYawDegree; + return this; + } + + public Position getShootPosition() { + return shootPosition; + } + + public MediaFileMetadata setShootPosition(Position shootPosition) { + this.shootPosition = shootPosition; + return this; + } + + public Double getRelativeAltitude() { + return relativeAltitude; + } + + public MediaFileMetadata setRelativeAltitude(Double relativeAltitude) { + this.relativeAltitude = relativeAltitude; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/MediaMethodEnum.java b/src/main/java/com/dji/sdk/cloudapi/media/MediaMethodEnum.java new file mode 100644 index 0000000..9f6f354 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/MediaMethodEnum.java @@ -0,0 +1,24 @@ +package com.dji.sdk.cloudapi.media; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +public enum MediaMethodEnum { + + UPLOAD_FLIGHTTASK_MEDIA_PRIORITIZE("upload_flighttask_media_prioritize"); + + private final String method; + + MediaMethodEnum(String method) { + this.method = method; + } + + @JsonValue + public String getMethod() { + return method; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/MediaSubFileTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/media/MediaSubFileTypeEnum.java new file mode 100644 index 0000000..724bd36 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/MediaSubFileTypeEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.media; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/16 + */ +@Schema(description = "The type of image file.

0: normal picture;

1: panorama.

") +public enum MediaSubFileTypeEnum { + + NORMAL(0), + + PANORAMA(1); + + private final int type; + + MediaSubFileTypeEnum(int type) { + this.type = type; + } + + @JsonValue + public int getType() { + return type; + } + + @JsonCreator + public static MediaSubFileTypeEnum find(int type) { + return Arrays.stream(values()).filter(subFile -> subFile.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(MediaSubFileTypeEnum.class, type)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/MediaUploadCallbackRequest.java b/src/main/java/com/dji/sdk/cloudapi/media/MediaUploadCallbackRequest.java new file mode 100644 index 0000000..6567ea3 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/MediaUploadCallbackRequest.java @@ -0,0 +1,124 @@ +package com.dji.sdk.cloudapi.media; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +@Schema(description = "media fast upload request data") +public class MediaUploadCallbackRequest { + + @NotNull + @Valid + private MediaFileExtension ext; + + @NotNull + @Schema(description = "media file fingerprint", example = "7F78C9F1999425CB61F10E1FE206009E") + private String fingerprint; + + @NotNull + @Schema(description = "media file name", example = "DJI_20220831151616_0004_W_Waypoint4.JPG") + private String name; + + @Schema(description = "media file path. This value is empty if the photo was not taken in the wayline.", example = "DJI_202208311455_008_Waypoint1") + private String path; + + @JsonProperty("object_key") + @NotNull + @Schema(description = "The key of the object in the bucket", example = "media/DJI_20220831151616_0004_W_Waypoint4.JPG") + private String objectKey; + + @Schema(type = "int") + @JsonProperty("sub_file_type") + @NotNull + private MediaSubFileTypeEnum subFileType; + + @Valid + @NotNull + private MediaFileMetadata metadata; + + public MediaUploadCallbackRequest() { + } + + @Override + public String toString() { + return "MediaUploadCallbackRequest{" + + "ext=" + ext + + ", fingerprint='" + fingerprint + '\'' + + ", name='" + name + '\'' + + ", path='" + path + '\'' + + ", objectKey='" + objectKey + '\'' + + ", subFileType=" + subFileType + + ", metadata=" + metadata + + '}'; + } + + public MediaFileExtension getExt() { + return ext; + } + + public MediaUploadCallbackRequest setExt(MediaFileExtension ext) { + this.ext = ext; + return this; + } + + public String getFingerprint() { + return fingerprint; + } + + public MediaUploadCallbackRequest setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + return this; + } + + public String getName() { + return name; + } + + public MediaUploadCallbackRequest setName(String name) { + this.name = name; + return this; + } + + public String getPath() { + return path; + } + + public MediaUploadCallbackRequest setPath(String path) { + this.path = path; + return this; + } + + public String getObjectKey() { + return objectKey; + } + + public MediaUploadCallbackRequest setObjectKey(String objectKey) { + this.objectKey = objectKey; + return this; + } + + public MediaSubFileTypeEnum getSubFileType() { + return subFileType; + } + + public MediaUploadCallbackRequest setSubFileType(MediaSubFileTypeEnum subFileType) { + this.subFileType = subFileType; + return this; + } + + public MediaFileMetadata getMetadata() { + return metadata; + } + + public MediaUploadCallbackRequest setMetadata(MediaFileMetadata metadata) { + this.metadata = metadata; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/Position.java b/src/main/java/com/dji/sdk/cloudapi/media/Position.java new file mode 100644 index 0000000..f192f6e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/Position.java @@ -0,0 +1,50 @@ +package com.dji.sdk.cloudapi.media; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +public class Position { + + @Schema(description = "latitude", example = "22.577666000000001") + @NotNull + private Double lat; + + @Schema(description = "longitude", example = "113.9431940000000") + @NotNull + private Double lng; + + public Position() { + } + + @Override + public String toString() { + return "Position{" + + "lat=" + lat + + ", lng=" + lng + + '}'; + } + + public Double getLat() { + return lat; + } + + public Position setLat(Double lat) { + this.lat = lat; + return this; + } + + public Double getLng() { + return lng; + } + + public Position setLng(Double lng) { + this.lng = lng; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/StorageConfigGet.java b/src/main/java/com/dji/sdk/cloudapi/media/StorageConfigGet.java new file mode 100644 index 0000000..d8f964f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/StorageConfigGet.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.media; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/27 + */ +public class StorageConfigGet { + + private StorageConfigGetModuleEnum module; + + public StorageConfigGet() { + } + + @Override + public String toString() { + return "StorageConfigGet{" + + "module=" + module + + '}'; + } + + public StorageConfigGetModuleEnum getModule() { + return module; + } + + public StorageConfigGet setModule(StorageConfigGetModuleEnum module) { + this.module = module; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/StorageConfigGetModuleEnum.java b/src/main/java/com/dji/sdk/cloudapi/media/StorageConfigGetModuleEnum.java new file mode 100644 index 0000000..b61fe8a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/StorageConfigGetModuleEnum.java @@ -0,0 +1,34 @@ +package com.dji.sdk.cloudapi.media; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/27 + */ +public enum StorageConfigGetModuleEnum { + + MEDIA(0); + + private final int module; + + StorageConfigGetModuleEnum(int module) { + this.module = module; + } + + @JsonValue + public int getModule() { + return module; + } + + @JsonCreator + public StorageConfigGetModuleEnum find(int module) { + return Arrays.stream(values()).filter(moduleEnum -> moduleEnum.module == module).findAny() + .orElseThrow(() -> new CloudSDKException(StorageConfigGetModuleEnum.class, module)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/UploadCallbackFileExtension.java b/src/main/java/com/dji/sdk/cloudapi/media/UploadCallbackFileExtension.java new file mode 100644 index 0000000..d5ead91 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/UploadCallbackFileExtension.java @@ -0,0 +1,70 @@ +package com.dji.sdk.cloudapi.media; + +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +public class UploadCallbackFileExtension { + + private DeviceEnum droneModelKey; + + @JsonProperty("is_original") + private Boolean original; + + private DeviceEnum payloadModelKey; + + private String flightId; + + public UploadCallbackFileExtension() { + } + + @Override + public String toString() { + return "UploadCallbackFileExtension{" + + "droneModelKey=" + droneModelKey + + ", original=" + original + + ", payloadModelKey=" + payloadModelKey + + ", flightId='" + flightId + '\'' + + '}'; + } + + public DeviceEnum getDroneModelKey() { + return droneModelKey; + } + + public UploadCallbackFileExtension setDroneModelKey(DeviceEnum droneModelKey) { + this.droneModelKey = droneModelKey; + return this; + } + + public Boolean getOriginal() { + return original; + } + + public UploadCallbackFileExtension setOriginal(Boolean original) { + this.original = original; + return this; + } + + public DeviceEnum getPayloadModelKey() { + return payloadModelKey; + } + + public UploadCallbackFileExtension setPayloadModelKey(DeviceEnum payloadModelKey) { + this.payloadModelKey = payloadModelKey; + return this; + } + + public String getFlightId() { + return flightId; + } + + public UploadCallbackFileExtension setFlightId(String flightId) { + this.flightId = flightId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/UploadCallbackFileMetadata.java b/src/main/java/com/dji/sdk/cloudapi/media/UploadCallbackFileMetadata.java new file mode 100644 index 0000000..e2ea7a8 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/UploadCallbackFileMetadata.java @@ -0,0 +1,85 @@ +package com.dji.sdk.cloudapi.media; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.time.LocalDateTime; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +@Schema(description = "media file metadata") +public class UploadCallbackFileMetadata { + + private Double absoluteAltitude; + + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssVV") + private LocalDateTime createdTime; + + private Double gimbalYawDegree; + + private Position shootPosition; + + private Double relativeAltitude; + + public UploadCallbackFileMetadata() { + } + + @Override + public String toString() { + return "MediaFileMetadata{" + + "absoluteAltitude=" + absoluteAltitude + + ", createdTime=" + createdTime + + ", gimbalYawDegree=" + gimbalYawDegree + + ", shootPosition=" + shootPosition + + ", relativeAltitude=" + relativeAltitude + + '}'; + } + + public Double getAbsoluteAltitude() { + return absoluteAltitude; + } + + public UploadCallbackFileMetadata setAbsoluteAltitude(Double absoluteAltitude) { + this.absoluteAltitude = absoluteAltitude; + return this; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } + + public UploadCallbackFileMetadata setCreatedTime(LocalDateTime createdTime) { + this.createdTime = createdTime; + return this; + } + + public Double getGimbalYawDegree() { + return gimbalYawDegree; + } + + public UploadCallbackFileMetadata setGimbalYawDegree(Double gimbalYawDegree) { + this.gimbalYawDegree = gimbalYawDegree; + return this; + } + + public Position getShootPosition() { + return shootPosition; + } + + public UploadCallbackFileMetadata setShootPosition(Position shootPosition) { + this.shootPosition = shootPosition; + return this; + } + + public Double getRelativeAltitude() { + return relativeAltitude; + } + + public UploadCallbackFileMetadata setRelativeAltitude(Double relativeAltitude) { + this.relativeAltitude = relativeAltitude; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/UploadFlighttaskMediaPrioritize.java b/src/main/java/com/dji/sdk/cloudapi/media/UploadFlighttaskMediaPrioritize.java new file mode 100644 index 0000000..59c197f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/UploadFlighttaskMediaPrioritize.java @@ -0,0 +1,37 @@ +package com.dji.sdk.cloudapi.media; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/27 + */ +public class UploadFlighttaskMediaPrioritize extends BaseModel { + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + private String flightId; + + public UploadFlighttaskMediaPrioritize() { + } + + @Override + public String toString() { + return "UploadFlighttaskMediaPrioritize{" + + "flightId='" + flightId + '\'' + + '}'; + } + + public String getFlightId() { + return flightId; + } + + public UploadFlighttaskMediaPrioritize setFlightId(String flightId) { + this.flightId = flightId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/api/AbstractMediaService.java b/src/main/java/com/dji/sdk/cloudapi/media/api/AbstractMediaService.java new file mode 100644 index 0000000..1aa0afa --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/api/AbstractMediaService.java @@ -0,0 +1,78 @@ +package com.dji.sdk.cloudapi.media.api; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.cloudapi.media.*; +import com.dji.sdk.cloudapi.storage.StsCredentialsResponse; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.GatewayTypeEnum; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; +import com.dji.sdk.mqtt.services.ServicesPublish; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public abstract class AbstractMediaService { + + @Resource + private ServicesPublish servicesPublish; + + /** + * Result reporting of media file uploading + * @param request + * @param headers + * @return + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse fileUploadCallback(TopicEventsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("fileUploadCallback not implemented"); + } + + /** + * Priority report of the media file uploading + * @param request + * @param headers + * @return + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse highestPriorityUploadFlightTaskMedia(TopicEventsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("highestPriorityUploadFlightTaskMedia not implemented"); + } + + /** + * Set the uploading file to highest priority + * @param gateway + * @param request data + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse uploadFlighttaskMediaPrioritize(GatewayManager gateway, UploadFlighttaskMediaPrioritize request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + MediaMethodEnum.UPLOAD_FLIGHTTASK_MEDIA_PRIORITIZE.getMethod(), + request); + } + + /** + * Obtain upload temporary credentials + * @param request + * @param headers + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET, outputChannel = ChannelName.OUTBOUND_REQUESTS) + public TopicRequestsResponse> storageConfigGet(TopicRequestsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("storageConfigGet not implemented"); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/media/api/IHttpMediaService.java b/src/main/java/com/dji/sdk/cloudapi/media/api/IHttpMediaService.java new file mode 100644 index 0000000..81a23e0 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/media/api/IHttpMediaService.java @@ -0,0 +1,115 @@ +package com.dji.sdk.cloudapi.media.api; + +import com.dji.sdk.cloudapi.media.*; +import com.dji.sdk.common.HttpResultResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/9 + */ +@Tag(name = "media interface") +public interface IHttpMediaService { + + String PREFIX = "media/api/v1"; + + /** + * Check if the file has been uploaded by the fingerprint. + * @param workspaceId + * @param request + * @param req + * @param rsp + * @return + */ + @Operation(summary = "media fast upload", description = "Check if the file has been uploaded by the fingerprint.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")) + }) + @PostMapping(PREFIX + "/workspaces/{workspace_id}/fast-upload") + HttpResultResponse mediaFastUpload( + @PathVariable(name = "workspace_id") String workspaceId, + @Valid @RequestBody MediaFastUploadRequest request, + HttpServletRequest req, HttpServletResponse rsp); + + + /** + * When the file is uploaded to the storage server by pilot, + * the basic information of the file is reported through this interface. + * @param workspaceId + * @param request + * @param req + * @param rsp + * @return + */ + @Operation(summary = "app reports file upload result", description = "When the file is uploaded to the storage server by pilot, " + + "the basic information of the file is reported through this interface.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")) + }, responses = @ApiResponse(responseCode = "200", description = "OK", + content = @Content(mediaType = "application/json", + examples = {@ExampleObject(name = "responseObjectKey", + summary = "response object key", + description = "response object key", + value = "{\"code\": 0, \"message\":\"success\", \"data\": \"media/DJI_20220831151616_0004_W_Waypoint4.JPG\"}" + )}))) + + @PostMapping(PREFIX + "/workspaces/{workspace_id}/upload-callback") + HttpResultResponse mediaUploadCallback( + @PathVariable(name = "workspace_id") String workspaceId, + @Valid @RequestBody MediaUploadCallbackRequest request, + HttpServletRequest req, HttpServletResponse rsp); + + /** + * Query the files that already exist in this workspace based on the workspace id and the collection of tiny fingerprints. + * @param workspaceId + * @param request There is only one tiny_fingerprint parameter in the body. + * @param req + * @param rsp + * @return + */ + @Operation(summary = "checks whether the file fingerprint exists", description = "Query the files that already exist in this " + + "workspace based on the workspace id and the collection of tiny fingerprints.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")) + }) + @PostMapping(PREFIX + "/workspaces/{workspace_id}/files/tiny-fingerprints") + HttpResultResponse getExistFileTinyFingerprint( + @PathVariable(name = "workspace_id") String workspaceId, + @Valid @RequestBody GetFileFingerprintRequest request, + HttpServletRequest req, HttpServletResponse rsp); + + /** + * Report the upload status of the media files in the file group in real time. + * @param workspaceId + * @param request + * @param req + * @param rsp + * @return + */ + @Operation(summary = "callback after the file group upload complete", description = "Report the upload status of " + + "the media files in the file group in real time.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")) + }) + @PostMapping(PREFIX + "/workspaces/{workspace_id}/group-upload-callback") + HttpResultResponse folderUploadCallback( + @PathVariable(name = "workspace_id") String workspaceId, + @Valid @RequestBody FolderUploadCallbackRequest request, + HttpServletRequest req, HttpServletResponse rsp); + + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/AirportBindStatusRequest.java b/src/main/java/com/dji/sdk/cloudapi/organization/AirportBindStatusRequest.java new file mode 100644 index 0000000..6697990 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/AirportBindStatusRequest.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.organization; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class AirportBindStatusRequest { + + private List devices; + + public AirportBindStatusRequest() { + } + + @Override + public String toString() { + return "AirportBindStatusRequest{" + + "devices=" + devices + + '}'; + } + + public List getDevices() { + return devices; + } + + public AirportBindStatusRequest setDevices(List devices) { + this.devices = devices; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/AirportBindStatusResponse.java b/src/main/java/com/dji/sdk/cloudapi/organization/AirportBindStatusResponse.java new file mode 100644 index 0000000..9928c5c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/AirportBindStatusResponse.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.organization; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class AirportBindStatusResponse extends BaseModel { + + @NotNull + @Valid + @Size(min = 1, max = 2) + private List bindStatus; + + public AirportBindStatusResponse() { + } + + @Override + public String toString() { + return "AirportBindStatusResponse{" + + "bindStatus=" + bindStatus + + '}'; + } + + public List getBindStatus() { + return bindStatus; + } + + public AirportBindStatusResponse setBindStatus(List bindStatus) { + this.bindStatus = bindStatus; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationBindRequest.java b/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationBindRequest.java new file mode 100644 index 0000000..0facdd0 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationBindRequest.java @@ -0,0 +1,32 @@ +package com.dji.sdk.cloudapi.organization; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class AirportOrganizationBindRequest { + + private List bindDevices; + + public AirportOrganizationBindRequest() { + } + + @Override + public String toString() { + return "AirportOrganizationBindRequest{" + + "bindDevices=" + bindDevices + + '}'; + } + + public List getBindDevices() { + return bindDevices; + } + + public AirportOrganizationBindRequest setBindDevices(List bindDevices) { + this.bindDevices = bindDevices; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationBindResponse.java b/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationBindResponse.java new file mode 100644 index 0000000..8155091 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationBindResponse.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.organization; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class AirportOrganizationBindResponse extends BaseModel { + + @NotNull + @Valid + @Size(min = 1, max = 2) + private List errInfos; + + public AirportOrganizationBindResponse() { + } + + @Override + public String toString() { + return "AirportOrganizationBindResponse{" + + "errInfos=" + errInfos + + '}'; + } + + public List getErrInfos() { + return errInfos; + } + + public AirportOrganizationBindResponse setErrInfos(List errInfos) { + this.errInfos = errInfos; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationGetRequest.java b/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationGetRequest.java new file mode 100644 index 0000000..e3b4f4d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationGetRequest.java @@ -0,0 +1,42 @@ +package com.dji.sdk.cloudapi.organization; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/13 + */ +public class AirportOrganizationGetRequest { + + private String deviceBindingCode; + + private String organizationId; + + public AirportOrganizationGetRequest() { + } + + @Override + public String toString() { + return "AirportOrganizationGetRequest{" + + "deviceBindingCode='" + deviceBindingCode + '\'' + + ", organizationId='" + organizationId + '\'' + + '}'; + } + + public String getDeviceBindingCode() { + return deviceBindingCode; + } + + public AirportOrganizationGetRequest setDeviceBindingCode(String deviceBindingCode) { + this.deviceBindingCode = deviceBindingCode; + return this; + } + + public String getOrganizationId() { + return organizationId; + } + + public AirportOrganizationGetRequest setOrganizationId(String organizationId) { + this.organizationId = organizationId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationGetResponse.java b/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationGetResponse.java new file mode 100644 index 0000000..cb10abf --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/AirportOrganizationGetResponse.java @@ -0,0 +1,35 @@ +package com.dji.sdk.cloudapi.organization; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/13 + */ +public class AirportOrganizationGetResponse extends BaseModel { + + @NotNull + private String organizationName; + + public AirportOrganizationGetResponse() { + } + + @Override + public String toString() { + return "AirportOrganizationGetResponse{" + + "organizationName='" + organizationName + '\'' + + '}'; + } + + public String getOrganizationName() { + return organizationName; + } + + public AirportOrganizationGetResponse setOrganizationName(String organizationName) { + this.organizationName = organizationName; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/BindStatusRequestDevice.java b/src/main/java/com/dji/sdk/cloudapi/organization/BindStatusRequestDevice.java new file mode 100644 index 0000000..68a263e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/BindStatusRequestDevice.java @@ -0,0 +1,88 @@ +package com.dji.sdk.cloudapi.organization; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/14 + */ +public class BindStatusRequestDevice { + + @NotNull + private String sn; + + @NotNull + @JsonProperty("is_device_bind_organization") + private Boolean deviceBindOrganization; + + @NotNull + private String organizationId; + + @NotNull + private String organizationName; + + @NotNull + private String deviceCallsign; + + public BindStatusRequestDevice() { + } + + @Override + public String toString() { + return "BindStatusRequestDevice{" + + "sn='" + sn + '\'' + + ", deviceBindOrganization=" + deviceBindOrganization + + ", organizationId='" + organizationId + '\'' + + ", organizationName='" + organizationName + '\'' + + ", deviceCallsign='" + deviceCallsign + '\'' + + '}'; + } + + public String getSn() { + return sn; + } + + public BindStatusRequestDevice setSn(String sn) { + this.sn = sn; + return this; + } + + public Boolean getDeviceBindOrganization() { + return deviceBindOrganization; + } + + public BindStatusRequestDevice setDeviceBindOrganization(Boolean deviceBindOrganization) { + this.deviceBindOrganization = deviceBindOrganization; + return this; + } + + public String getOrganizationId() { + return organizationId; + } + + public BindStatusRequestDevice setOrganizationId(String organizationId) { + this.organizationId = organizationId; + return this; + } + + public String getOrganizationName() { + return organizationName; + } + + public BindStatusRequestDevice setOrganizationName(String organizationName) { + this.organizationName = organizationName; + return this; + } + + public String getDeviceCallsign() { + return deviceCallsign; + } + + public BindStatusRequestDevice setDeviceCallsign(String deviceCallsign) { + this.deviceCallsign = deviceCallsign; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/BindStatusResponseDevice.java b/src/main/java/com/dji/sdk/cloudapi/organization/BindStatusResponseDevice.java new file mode 100644 index 0000000..9fb647f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/BindStatusResponseDevice.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.organization; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class BindStatusResponseDevice { + + private String sn; + + public BindStatusResponseDevice() { + } + + @Override + public String toString() { + return "BindStatusResponseDevice{" + + "sn='" + sn + '\'' + + '}'; + } + + public String getSn() { + return sn; + } + + public BindStatusResponseDevice setSn(String sn) { + this.sn = sn; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/OrganizationBindDevice.java b/src/main/java/com/dji/sdk/cloudapi/organization/OrganizationBindDevice.java new file mode 100644 index 0000000..6b896d8 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/OrganizationBindDevice.java @@ -0,0 +1,80 @@ +package com.dji.sdk.cloudapi.organization; + +import com.dji.sdk.cloudapi.device.DeviceEnum; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/13 + */ +public class OrganizationBindDevice { + + private String deviceBindingCode; + + private String organizationId; + + private String deviceCallsign; + + private String sn; + + private DeviceEnum deviceModelKey; + + public OrganizationBindDevice() { + } + + @Override + public String toString() { + return "OrganizationBindDevice{" + + "deviceBindingCode='" + deviceBindingCode + '\'' + + ", organizationId='" + organizationId + '\'' + + ", deviceCallsign='" + deviceCallsign + '\'' + + ", sn='" + sn + '\'' + + ", deviceModelKey=" + deviceModelKey + + '}'; + } + + public String getDeviceBindingCode() { + return deviceBindingCode; + } + + public OrganizationBindDevice setDeviceBindingCode(String deviceBindingCode) { + this.deviceBindingCode = deviceBindingCode; + return this; + } + + public String getOrganizationId() { + return organizationId; + } + + public OrganizationBindDevice setOrganizationId(String organizationId) { + this.organizationId = organizationId; + return this; + } + + public String getDeviceCallsign() { + return deviceCallsign; + } + + public OrganizationBindDevice setDeviceCallsign(String deviceCallsign) { + this.deviceCallsign = deviceCallsign; + return this; + } + + public String getSn() { + return sn; + } + + public OrganizationBindDevice setSn(String sn) { + this.sn = sn; + return this; + } + + public DeviceEnum getDeviceModelKey() { + return deviceModelKey; + } + + public OrganizationBindDevice setDeviceModelKey(DeviceEnum deviceModelKey) { + this.deviceModelKey = deviceModelKey; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/OrganizationBindInfo.java b/src/main/java/com/dji/sdk/cloudapi/organization/OrganizationBindInfo.java new file mode 100644 index 0000000..128c44b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/OrganizationBindInfo.java @@ -0,0 +1,57 @@ +package com.dji.sdk.cloudapi.organization; + +import com.dji.sdk.mqtt.MqttReply; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/14 + */ +public class OrganizationBindInfo { + + @NotNull + private String sn; + + @NotNull + private Integer errCode; + + public OrganizationBindInfo() { + } + + public OrganizationBindInfo(String sn, Integer errCode) { + this.sn = sn; + this.errCode = errCode; + } + + public static OrganizationBindInfo success(String sn) { + return new OrganizationBindInfo(sn, MqttReply.CODE_SUCCESS); + } + + @Override + public String toString() { + return "OrganizationBindInfo{" + + "sn='" + sn + '\'' + + ", errCode=" + errCode + + '}'; + } + + public String getSn() { + return sn; + } + + public OrganizationBindInfo setSn(String sn) { + this.sn = sn; + return this; + } + + public Integer getErrCode() { + return errCode; + } + + public OrganizationBindInfo setErrCode(Integer errCode) { + this.errCode = errCode; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/organization/api/AbstractOrganizationService.java b/src/main/java/com/dji/sdk/cloudapi/organization/api/AbstractOrganizationService.java new file mode 100644 index 0000000..369cc00 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/organization/api/AbstractOrganizationService.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.organization.api; + +import com.dji.sdk.cloudapi.organization.*; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public abstract class AbstractOrganizationService { + + /** + * Obtain organization binding information + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS, outputChannel = ChannelName.OUTBOUND_REQUESTS) + public TopicRequestsResponse> airportBindStatus( + TopicRequestsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("airportBindStatus not implemented"); + } + + /** + * Search for the organization information that device bound to + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET, outputChannel = ChannelName.OUTBOUND_REQUESTS) + public TopicRequestsResponse> airportOrganizationGet( + TopicRequestsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("airportOrganizationGet not implemented"); + } + + /** + * Device bind to organization + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND, outputChannel = ChannelName.OUTBOUND_REQUESTS) + public TopicRequestsResponse> airportOrganizationBind( + TopicRequestsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("airportOrganizationBind not implemented"); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/DistanceLimitStatusData.java b/src/main/java/com/dji/sdk/cloudapi/property/DistanceLimitStatusData.java new file mode 100644 index 0000000..cf03f33 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/DistanceLimitStatusData.java @@ -0,0 +1,52 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.SwitchActionEnum; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; + +/** + * The state of the drone's limited distance + * @author sean + * @version 1.3 + * @date 2022/10/27 + */ +public class DistanceLimitStatusData { + + private SwitchActionEnum state; + + @Min(15) + @Max(8000) + @JsonProperty("distance_limit") + private Integer distanceLimit; + + public DistanceLimitStatusData() { + } + + @Override + public String toString() { + return "DistanceLimitStatusData{" + + "state=" + state + + ", distanceLimit=" + distanceLimit + + '}'; + } + + public SwitchActionEnum getState() { + return state; + } + + public DistanceLimitStatusData setState(SwitchActionEnum state) { + this.state = state; + return this; + } + + public Integer getDistanceLimit() { + return distanceLimit; + } + + public DistanceLimitStatusData setDistanceLimit(Integer distanceLimit) { + this.distanceLimit = distanceLimit; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/DistanceLimitStatusSet.java b/src/main/java/com/dji/sdk/cloudapi/property/DistanceLimitStatusSet.java new file mode 100644 index 0000000..ddca848 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/DistanceLimitStatusSet.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * The state of the drone's limited distance + * @author sean + * @version 1.3 + * @date 2022/10/27 + */ +public class DistanceLimitStatusSet extends BaseModel { + + @Valid + @NotNull + private DistanceLimitStatusData distanceLimitStatus; + + public DistanceLimitStatusSet() { + } + + @Override + public String toString() { + return "DistanceLimitStatusSet{" + + "distanceLimitStatus=" + distanceLimitStatus + + '}'; + } + + public DistanceLimitStatusData getDistanceLimitStatus() { + return distanceLimitStatus; + } + + public DistanceLimitStatusSet setDistanceLimitStatus(DistanceLimitStatusData distanceLimitStatus) { + this.distanceLimitStatus = distanceLimitStatus; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/ExitWaylineWhenRcLostSet.java b/src/main/java/com/dji/sdk/cloudapi/property/ExitWaylineWhenRcLostSet.java new file mode 100644 index 0000000..d9053fa --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/ExitWaylineWhenRcLostSet.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.ExitWaylineWhenRcLostEnum; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/3 + */ +public class ExitWaylineWhenRcLostSet extends BaseModel { + + @NotNull + @JsonProperty("exit_wayline_when_rc_lost") + private ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost; + + public ExitWaylineWhenRcLostSet() { + } + + @Override + public String toString() { + return "ExitWaylineWhenRcLostSet{" + + "exitWaylineWhenRcLost=" + exitWaylineWhenRcLost + + '}'; + } + + public ExitWaylineWhenRcLostEnum getExitWaylineWhenRcLost() { + return exitWaylineWhenRcLost; + } + + public ExitWaylineWhenRcLostSet setExitWaylineWhenRcLost(ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost) { + this.exitWaylineWhenRcLost = exitWaylineWhenRcLost; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/HeightLimitSet.java b/src/main/java/com/dji/sdk/cloudapi/property/HeightLimitSet.java new file mode 100644 index 0000000..0fc3180 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/HeightLimitSet.java @@ -0,0 +1,41 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/10/28 + */ +public class HeightLimitSet extends BaseModel { + + @NotNull + @Max(1500) + @Min(20) + @JsonProperty("height_limit") + private Integer heightLimit; + + public HeightLimitSet() { + } + + @Override + public String toString() { + return "HeightLimitSet{" + + "heightLimit=" + heightLimit + + '}'; + } + + public Integer getHeightLimit() { + return heightLimit; + } + + public HeightLimitSet setHeightLimit(Integer heightLimit) { + this.heightLimit = heightLimit; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/NightLightsStateSet.java b/src/main/java/com/dji/sdk/cloudapi/property/NightLightsStateSet.java new file mode 100644 index 0000000..feb5fea --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/NightLightsStateSet.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.SwitchActionEnum; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/25 + */ +public class NightLightsStateSet extends BaseModel { + + @NotNull + @JsonProperty("night_lights_state") + private SwitchActionEnum nightLightsState; + + public NightLightsStateSet() { + } + + @Override + public String toString() { + return "NightLightsStateSet{" + + "nightLightsState=" + nightLightsState + + '}'; + } + + public SwitchActionEnum getNightLightsState() { + return nightLightsState; + } + + public NightLightsStateSet setNightLightsState(SwitchActionEnum nightLightsState) { + this.nightLightsState = nightLightsState; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/ObstacleAvoidanceSet.java b/src/main/java/com/dji/sdk/cloudapi/property/ObstacleAvoidanceSet.java new file mode 100644 index 0000000..f84ec4c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/ObstacleAvoidanceSet.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.ObstacleAvoidance; +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.3 + * @date 2022/10/27 + */ +public class ObstacleAvoidanceSet extends BaseModel { + + @Valid + @NotNull + private ObstacleAvoidance obstacleAvoidance; + + public ObstacleAvoidanceSet() { + } + + @Override + public String toString() { + return "ObstacleAvoidanceSet{" + + "obstacleAvoidance=" + obstacleAvoidance + + '}'; + } + + public ObstacleAvoidance getObstacleAvoidance() { + return obstacleAvoidance; + } + + public ObstacleAvoidanceSet setObstacleAvoidance(ObstacleAvoidance obstacleAvoidance) { + this.obstacleAvoidance = obstacleAvoidance; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/PropertySetEnum.java b/src/main/java/com/dji/sdk/cloudapi/property/PropertySetEnum.java new file mode 100644 index 0000000..549c6fd --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/PropertySetEnum.java @@ -0,0 +1,62 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/10/27 + */ +public enum PropertySetEnum { + + NIGHT_LIGHTS_STATE("night_lights_state", NightLightsStateSet.class), + + HEIGHT_LIMIT("height_limit", HeightLimitSet.class), + + DISTANCE_LIMIT_STATUS("distance_limit_status", DistanceLimitStatusSet.class), + + OBSTACLE_AVOIDANCE("obstacle_avoidance", ObstacleAvoidanceSet.class), + + RTH_ALTITUDE("rth_altitude", RthAltitudeSet.class), + + OUT_OF_CONTROL_ACTION("rc_lost_action", RcLostActionSet.class), + + EXIT_WAYLINE_WHEN_RC_LOST("exit_wayline_when_rc_lost", ExitWaylineWhenRcLostSet.class), + + THERMAL_CURRENT_PALETTE_STYLE("thermal_current_palette_style", ThermalCurrentPaletteStyleSet.class), + + THERMAL_GAIN_MODE("thermal_gain_mode", ThermalGainModeSet.class), + + THERMAL_ISOTHERM_STATE("thermal_isotherm_state", ThermalIsothermStateSet.class), + + THERMAL_ISOTHERM_UPPER_LIMIT("thermal_isotherm_upper_limit", ThermalIsothermUpperLimitSet.class), + + THERMAL_ISOTHERM_LOWER_LIMIT("thermal_isotherm_lower_limit", ThermalIsothermLowerLimitSet.class), + + ; + + private final String property; + + private final Class clazz; + + PropertySetEnum(String property, Class clazz) { + this.property = property; + this.clazz = clazz; + } + + public String getProperty() { + return property; + } + + public Class getClazz() { + return clazz; + } + + public static PropertySetEnum find(String property) { + return Arrays.stream(values()).filter(propertyEnum -> propertyEnum.property.equals(property)).findAny() + .orElseThrow(() -> new CloudSDKException(PropertySetEnum.class, property)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/RcLostActionSet.java b/src/main/java/com/dji/sdk/cloudapi/property/RcLostActionSet.java new file mode 100644 index 0000000..6866478 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/RcLostActionSet.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.RcLostActionEnum; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/3 + */ +public class RcLostActionSet extends BaseModel { + + @NotNull + @JsonProperty("rc_lost_action") + private RcLostActionEnum rcLostAction; + + public RcLostActionSet() { + } + + @Override + public String toString() { + return "RcLostActionSet{" + + "rcLostAction=" + rcLostAction + + '}'; + } + + public RcLostActionEnum getRcLostAction() { + return rcLostAction; + } + + public RcLostActionSet setRcLostAction(RcLostActionEnum rcLostAction) { + this.rcLostAction = rcLostAction; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/RthAltitudeSet.java b/src/main/java/com/dji/sdk/cloudapi/property/RthAltitudeSet.java new file mode 100644 index 0000000..94e3094 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/RthAltitudeSet.java @@ -0,0 +1,41 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.4 + * @date 2023/3/3 + */ +public class RthAltitudeSet extends BaseModel { + + @NotNull + @Min(20) + @Max(50) + @JsonProperty("rth_altitude") + private Integer rthAltitude; + + public RthAltitudeSet() { + } + + @Override + public String toString() { + return "RthAltitudeSet{" + + "rthAltitude=" + rthAltitude + + '}'; + } + + public Integer getRthAltitude() { + return rthAltitude; + } + + public RthAltitudeSet setRthAltitude(Integer rthAltitude) { + this.rthAltitude = rthAltitude; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/ThermalCurrentPaletteStyleSet.java b/src/main/java/com/dji/sdk/cloudapi/property/ThermalCurrentPaletteStyleSet.java new file mode 100644 index 0000000..6b5e847 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/ThermalCurrentPaletteStyleSet.java @@ -0,0 +1,59 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.cloudapi.device.ThermalPaletteStyleEnum; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonValue; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class ThermalCurrentPaletteStyleSet extends BaseModel { + + @NotNull + @Valid + private PayloadIndex payloadIndex; + + @NotNull + private ThermalPaletteStyleEnum thermalCurrentPaletteStyle; + + public ThermalCurrentPaletteStyleSet() { + } + + @Override + public String toString() { + return "ThermalCurrentPaletteStyleSet{" + + "payloadIndex=" + payloadIndex + + ", thermalCurrentPaletteStyle=" + thermalCurrentPaletteStyle + + '}'; + } + + @JsonValue + public Map toMap() { + return Map.of(payloadIndex.toString(), Map.of("thermal_current_palette_style", thermalCurrentPaletteStyle.getStyle())); + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public ThermalCurrentPaletteStyleSet setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public ThermalPaletteStyleEnum getThermalCurrentPaletteStyle() { + return thermalCurrentPaletteStyle; + } + + public ThermalCurrentPaletteStyleSet setThermalCurrentPaletteStyle(ThermalPaletteStyleEnum thermalCurrentPaletteStyle) { + this.thermalCurrentPaletteStyle = thermalCurrentPaletteStyle; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/ThermalGainModeSet.java b/src/main/java/com/dji/sdk/cloudapi/property/ThermalGainModeSet.java new file mode 100644 index 0000000..f49bea4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/ThermalGainModeSet.java @@ -0,0 +1,59 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.cloudapi.device.ThermalGainModeEnum; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonValue; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class ThermalGainModeSet extends BaseModel { + + @NotNull + @Valid + private PayloadIndex payloadIndex; + + @NotNull + private ThermalGainModeEnum thermalGainMode; + + public ThermalGainModeSet() { + } + + @Override + public String toString() { + return "ThermalGainModeSet{" + + "payloadIndex=" + payloadIndex + + ", thermalGainMode=" + thermalGainMode + + '}'; + } + + @JsonValue + public Map toMap() { + return Map.of(payloadIndex.toString(), Map.of("thermal_gain_mode", thermalGainMode.getMode())); + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public ThermalGainModeSet setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public ThermalGainModeEnum getThermalGainMode() { + return thermalGainMode; + } + + public ThermalGainModeSet setThermalGainMode(ThermalGainModeEnum thermalGainMode) { + this.thermalGainMode = thermalGainMode; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermLowerLimitSet.java b/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermLowerLimitSet.java new file mode 100644 index 0000000..66ff5b4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermLowerLimitSet.java @@ -0,0 +1,58 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonValue; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class ThermalIsothermLowerLimitSet extends BaseModel { + + @NotNull + @Valid + private PayloadIndex payloadIndex; + + @NotNull + private Integer thermalIsothermLowerLimit; + + public ThermalIsothermLowerLimitSet() { + } + + @Override + public String toString() { + return "ThermalIsothermLowerLimitSet{" + + "payloadIndex=" + payloadIndex + + ", thermalIsothermLowerLimit=" + thermalIsothermLowerLimit + + '}'; + } + + @JsonValue + public Map toMap() { + return Map.of(payloadIndex.toString(), Map.of("thermal_isotherm_upper_limit", thermalIsothermLowerLimit)); + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public ThermalIsothermLowerLimitSet setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public Integer getThermalIsothermLowerLimit() { + return thermalIsothermLowerLimit; + } + + public ThermalIsothermLowerLimitSet setThermalIsothermLowerLimit(Integer thermalIsothermLowerLimit) { + this.thermalIsothermLowerLimit = thermalIsothermLowerLimit; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermStateSet.java b/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermStateSet.java new file mode 100644 index 0000000..df25b38 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermStateSet.java @@ -0,0 +1,59 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.cloudapi.device.SwitchActionEnum; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonValue; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class ThermalIsothermStateSet extends BaseModel { + + @NotNull + @Valid + private PayloadIndex payloadIndex; + + @NotNull + private SwitchActionEnum thermalIsothermState; + + public ThermalIsothermStateSet() { + } + + @Override + public String toString() { + return "ThermalGainModeSet{" + + "payloadIndex=" + payloadIndex + + ", thermalIsothermState=" + thermalIsothermState + + '}'; + } + + @JsonValue + public Map toMap() { + return Map.of(payloadIndex.toString(), Map.of("thermal_isotherm_state", thermalIsothermState.getAction())); + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public ThermalIsothermStateSet setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public SwitchActionEnum getThermalIsothermState() { + return thermalIsothermState; + } + + public ThermalIsothermStateSet setThermalIsothermState(SwitchActionEnum thermalIsothermState) { + this.thermalIsothermState = thermalIsothermState; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermUpperLimitSet.java b/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermUpperLimitSet.java new file mode 100644 index 0000000..3d5df2b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/ThermalIsothermUpperLimitSet.java @@ -0,0 +1,58 @@ +package com.dji.sdk.cloudapi.property; + +import com.dji.sdk.cloudapi.device.PayloadIndex; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonValue; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public class ThermalIsothermUpperLimitSet extends BaseModel { + + @NotNull + @Valid + private PayloadIndex payloadIndex; + + @NotNull + private Integer thermalIsothermUpperLimit; + + public ThermalIsothermUpperLimitSet() { + } + + @Override + public String toString() { + return "ThermalIsothermUpperLimitSet{" + + "payloadIndex=" + payloadIndex + + ", thermalIsothermUpperLimit=" + thermalIsothermUpperLimit + + '}'; + } + + @JsonValue + public Map toMap() { + return Map.of(payloadIndex.toString(), Map.of("thermal_isotherm_upper_limit", thermalIsothermUpperLimit)); + } + + public PayloadIndex getPayloadIndex() { + return payloadIndex; + } + + public ThermalIsothermUpperLimitSet setPayloadIndex(PayloadIndex payloadIndex) { + this.payloadIndex = payloadIndex; + return this; + } + + public Integer getThermalIsothermUpperLimit() { + return thermalIsothermUpperLimit; + } + + public ThermalIsothermUpperLimitSet setThermalIsothermUpperLimit(Integer thermalIsothermUpperLimit) { + this.thermalIsothermUpperLimit = thermalIsothermUpperLimit; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/property/api/AbstractPropertyService.java b/src/main/java/com/dji/sdk/cloudapi/property/api/AbstractPropertyService.java new file mode 100644 index 0000000..eec89bc --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/property/api/AbstractPropertyService.java @@ -0,0 +1,78 @@ +package com.dji.sdk.cloudapi.property.api; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.cloudapi.property.PropertySetEnum; +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.common.Common; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.GatewayTypeEnum; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.property.PropertySetPublish; +import com.dji.sdk.mqtt.property.PropertySetReplyResultEnum; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/30 + */ +public abstract class AbstractPropertyService { + + @Resource + private PropertySetPublish propertySetPublish; + + /** + * Device property set + * @param gateway + * @param propertyEnum + * @param request + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public PropertySetReplyResultEnum propertySet(GatewayManager gateway, PropertySetEnum propertyEnum, BaseModel request) { + if (Objects.isNull(request) || propertyEnum.getClazz() != request.getClass()) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + Common.validateModel(request); + Field[] fields = request.getClass().getDeclaredFields(); + if (fields.length > 1 || null == fields[0].getDeclaredAnnotation(Valid.class)) { + return propertySetPublish.publish(gateway.getGatewaySn(), request); + } + + try { + Map map = new HashMap<>(); + fields[0].setAccessible(true); + Object child = fields[0].get(request); + for (Field field : ((Class) fields[0].getGenericType()).getDeclaredFields()) { + field.setAccessible(true); + Object value = field.get(child); + if (Objects.isNull(value)) { + continue; + } + map.put(Optional.ofNullable(field.getDeclaredAnnotation(JsonProperty.class)) + .map(JsonProperty::value).orElse(field.getName()), value); + field.setAccessible(false); + PropertySetReplyResultEnum result = propertySetPublish.publish( + gateway.getGatewaySn(), Map.of(propertyEnum.getProperty(), map)); + if (PropertySetReplyResultEnum.SUCCESS != result) { + return result; + } + map.clear(); + } + fields[0].setAccessible(false); + + } catch (IllegalAccessException e) { + throw new CloudSDKException(e); + } + return PropertySetReplyResultEnum.SUCCESS; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/storage/CredentialsToken.java b/src/main/java/com/dji/sdk/cloudapi/storage/CredentialsToken.java new file mode 100644 index 0000000..93317b4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/storage/CredentialsToken.java @@ -0,0 +1,111 @@ +package com.dji.sdk.cloudapi.storage; + +import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.minio.credentials.Credentials; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +@Schema(description = "The token data of the temporary credential") +public class CredentialsToken { + + private static final int DELAY = 300; + + @NotNull + @Schema(description = "access key id", example = "3POX6W77L1EF4C86L2RE") + @JsonProperty("access_key_id") + private String accessKeyId; + + @NotNull + @Schema(description = "access key secret", example = "9NG2P2yJaUrck576CkdRoRbchKssJiZygi5D93CBsduY") + @JsonProperty("access_key_secret") + private String accessKeySecret; + + @NotNull + @Min(1) + @Schema(description = "The valid time of the token, in seconds.", example = "3600") + private Long expire; + + @NotNull + @JsonProperty("security_token") + @Schema(description = "security token", example = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiIzUE9YNlc3N0wxRUY0Qzg2TDJSRSIsImV4cCI6MTY4NjgxOTgyOSwicGFyZW50IjoibWluaW8ifQ.cWJXI90UidrBOTD0gWxKt8PT5Qp_6dEK5wNfJuE6lR9dH6Us7jtSB8vcttRDwPhpCNrAGsv91ydw6NLMyjqAOw") + private String securityToken; + + public CredentialsToken(Credentials credentials, long expire) { + this.accessKeyId = credentials.accessKey(); + this.accessKeySecret = credentials.secretKey(); + this.securityToken = credentials.sessionToken(); + this.expire = expire - DELAY; + } + + public CredentialsToken(AssumeRoleResponse.Credentials credentials, long expire) { + this.accessKeyId = credentials.getAccessKeyId(); + this.accessKeySecret = credentials.getAccessKeySecret(); + this.securityToken = credentials.getSecurityToken(); + this.expire = expire - DELAY; + } + + public CredentialsToken(com.amazonaws.services.securitytoken.model.Credentials credentials) { + this.accessKeyId = credentials.getAccessKeyId(); + this.accessKeySecret = credentials.getSecretAccessKey(); + this.securityToken = credentials.getSessionToken(); + this.expire = (credentials.getExpiration().getTime() - System.currentTimeMillis()) / 1000 - DELAY; + } + + public CredentialsToken() { + } + + @Override + public String toString() { + return "CredentialsToken{" + + "accessKeyId='" + accessKeyId + '\'' + + ", accessKeySecret='" + accessKeySecret + '\'' + + ", expire=" + expire + + ", securityToken='" + securityToken + '\'' + + '}'; + } + + public String getAccessKeyId() { + return accessKeyId; + } + + public CredentialsToken setAccessKeyId(String accessKeyId) { + this.accessKeyId = accessKeyId; + return this; + } + + public String getAccessKeySecret() { + return accessKeySecret; + } + + public CredentialsToken setAccessKeySecret(String accessKeySecret) { + this.accessKeySecret = accessKeySecret; + return this; + } + + public Long getExpire() { + return expire; + } + + public CredentialsToken setExpire(Long expire) { + this.expire = expire; + return this; + } + + public String getSecurityToken() { + return securityToken; + } + + public CredentialsToken setSecurityToken(String securityToken) { + this.securityToken = securityToken; + return this; + } +} diff --git a/src/main/java/com/dji/sample/component/oss/model/enums/OssTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/storage/OssTypeEnum.java similarity index 56% rename from src/main/java/com/dji/sample/component/oss/model/enums/OssTypeEnum.java rename to src/main/java/com/dji/sdk/cloudapi/storage/OssTypeEnum.java index a5dafe0..16401af 100644 --- a/src/main/java/com/dji/sample/component/oss/model/enums/OssTypeEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/storage/OssTypeEnum.java @@ -1,10 +1,14 @@ -package com.dji.sample.component.oss.model.enums; +package com.dji.sdk.cloudapi.storage; + +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; /** * @author sean * @version 1.0 * @date 2022/5/30 */ +@Schema(description = "oss type", example = "minio", enumAsRef = true) public enum OssTypeEnum { ALIYUN("ali"), @@ -19,6 +23,7 @@ public enum OssTypeEnum { this.type = type; } + @JsonValue public String getType() { return type; } diff --git a/src/main/java/com/dji/sdk/cloudapi/storage/StsCredentialsResponse.java b/src/main/java/com/dji/sdk/cloudapi/storage/StsCredentialsResponse.java new file mode 100644 index 0000000..0821274 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/storage/StsCredentialsResponse.java @@ -0,0 +1,113 @@ +package com.dji.sdk.cloudapi.storage; + +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/7 + */ +@Schema(description = "Temporary credential data") +public class StsCredentialsResponse extends BaseModel { + + @Schema(description = "bucket name", example = "bucket-api") + @NotNull + private String bucket; + + @NotNull + @Valid + @Schema(description = "The token data of the temporary credential") + private CredentialsToken credentials; + + @NotNull + @Schema(description = "access domain name for external services", example = "https://oss-cn-hangzhou.aliyuncs.com") + @Pattern(regexp = "^http[s]?://.*$") + private String endpoint; + + @NotNull + @JsonProperty("object_key_prefix") + @Schema(description = "The folder path where the object needs to be stored.", example = "files/wayline") + private String objectKeyPrefix; + + @NotNull + private OssTypeEnum provider; + + @NotNull + @Schema(description = "The region where the bucket is located.", example = "us-east-1") + private String region; + + public StsCredentialsResponse() { + } + + @Override + public String toString() { + return "StsCredentialsResponse{" + + "bucket='" + bucket + '\'' + + ", credentials=" + credentials + + ", endpoint='" + endpoint + '\'' + + ", objectKeyPrefix='" + objectKeyPrefix + '\'' + + ", provider='" + provider + '\'' + + ", region='" + region + '\'' + + '}'; + } + + public String getBucket() { + return bucket; + } + + public StsCredentialsResponse setBucket(String bucket) { + this.bucket = bucket; + return this; + } + + public CredentialsToken getCredentials() { + return credentials; + } + + public StsCredentialsResponse setCredentials(CredentialsToken credentials) { + this.credentials = credentials; + return this; + } + + public String getEndpoint() { + return endpoint; + } + + public StsCredentialsResponse setEndpoint(String endpoint) { + this.endpoint = endpoint; + return this; + } + + public String getObjectKeyPrefix() { + return objectKeyPrefix; + } + + public StsCredentialsResponse setObjectKeyPrefix(String objectKeyPrefix) { + this.objectKeyPrefix = objectKeyPrefix; + return this; + } + + public OssTypeEnum getProvider() { + return provider; + } + + public StsCredentialsResponse setProvider(OssTypeEnum provider) { + this.provider = provider; + return this; + } + + public String getRegion() { + return region; + } + + public StsCredentialsResponse setRegion(String region) { + this.region = region; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/storage/api/IHttpStorageService.java b/src/main/java/com/dji/sdk/cloudapi/storage/api/IHttpStorageService.java new file mode 100644 index 0000000..602a8d2 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/storage/api/IHttpStorageService.java @@ -0,0 +1,39 @@ +package com.dji.sdk.cloudapi.storage.api; + +import com.dji.sdk.cloudapi.storage.StsCredentialsResponse; +import com.dji.sdk.common.HttpResultResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author sean + * @version 0.3 + * @date 2021/12/29 + */ +public interface IHttpStorageService { + + String PREFIX = "storage/api/v1"; + + /** + * Get temporary credentials for uploading the media and wayline in DJI Pilot. + * @param workspaceId workspace id + * @param req + * @param rsp + * @return + */ + @Operation(summary = "Get STS Token", description = "Get temporary credentials for uploading the media and wayline in DJI Pilot.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")) + }) + @PostMapping(PREFIX + "/workspaces/{workspace_id}/sts") + HttpResultResponse getTemporaryCredential( + @PathVariable(name = "workspace_id") String workspaceId, + HttpServletRequest req, HttpServletResponse rsp); + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/tsa/DeviceIconUrl.java b/src/main/java/com/dji/sdk/cloudapi/tsa/DeviceIconUrl.java new file mode 100644 index 0000000..937cb4a --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/tsa/DeviceIconUrl.java @@ -0,0 +1,56 @@ +package com.dji.sdk.cloudapi.tsa; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.3 + * @date 2022/1/5 + */ +@Schema(description = "device icon url.
You can use icons from the web, and the App internally downloads and caches these icons and" + + " loads them at a fixed size (28dp) to display on the map. Example: http://r56978dr7.hn-bkt.clouddn.com/tsa_equipment_normal.png", + anyOf = IconUrlEnum.class) +public class DeviceIconUrl { + + @JsonProperty("normal_icon_url") + @NotNull + @Schema(description = "icon displayed in normal state", example = "resource://pilot/drawable/tsa_car_normal") + private String normalIconUrl; + + @JsonProperty("selected_icon_url") + @NotNull + @Schema(description = "icon displayed in selected state", example = "resource://pilot/drawable/tsa_car_select") + private String selectIconUrl; + + public DeviceIconUrl() { + } + + @Override + public String toString() { + return "DeviceIconUrl{" + + "normalIconUrl='" + normalIconUrl + '\'' + + ", selectIconUrl='" + selectIconUrl + '\'' + + '}'; + } + + public String getNormalIconUrl() { + return normalIconUrl; + } + + public DeviceIconUrl setNormalIconUrl(String normalIconUrl) { + this.normalIconUrl = normalIconUrl; + return this; + } + + public String getSelectIconUrl() { + return selectIconUrl; + } + + public DeviceIconUrl setSelectIconUrl(String selectIconUrl) { + this.selectIconUrl = selectIconUrl; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/tsa/DeviceTopology.java b/src/main/java/com/dji/sdk/cloudapi/tsa/DeviceTopology.java new file mode 100644 index 0000000..437783d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/tsa/DeviceTopology.java @@ -0,0 +1,128 @@ +package com.dji.sdk.cloudapi.tsa; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/8 + */ +@Schema(description = "device topology data") +public class DeviceTopology { + + @NotNull + @Schema(description = "device sn", example = "1AEC32CK4AD23R") + private String sn; + + @NotNull + @JsonProperty("device_callsign") + @Schema(description = "device nickname", example = "my M350") + private String deviceCallsign; + + @NotNull + @JsonProperty("device_model") + @Valid + private TopologyDeviceModel deviceModel; + + @NotNull + @Schema(description = "online status") + @JsonProperty("online_status") + private Boolean onlineStatus; + + @Schema(description = "the id of the person using the device", format = "uuid") + @JsonProperty("user_id") + private String userId; + + @NotNull + @Schema(description = "the nickname of the person using the device", example = "admin") + @JsonProperty("user_callsign") + private String userCallsign; + + @NotNull + @JsonProperty("icon_urls") + @Valid + private DeviceIconUrl iconUrls; + + public DeviceTopology() { + } + + @Override + public String toString() { + return "DeviceTopology{" + + "sn='" + sn + '\'' + + ", deviceCallsign='" + deviceCallsign + '\'' + + ", deviceModel=" + deviceModel + + ", onlineStatus=" + onlineStatus + + ", userId='" + userId + '\'' + + ", userCallsign='" + userCallsign + '\'' + + ", iconUrls=" + iconUrls + + '}'; + } + + public String getSn() { + return sn; + } + + public DeviceTopology setSn(String sn) { + this.sn = sn; + return this; + } + + public String getDeviceCallsign() { + return deviceCallsign; + } + + public DeviceTopology setDeviceCallsign(String deviceCallsign) { + this.deviceCallsign = deviceCallsign; + return this; + } + + public TopologyDeviceModel getDeviceModel() { + return deviceModel; + } + + public DeviceTopology setDeviceModel(TopologyDeviceModel deviceModel) { + this.deviceModel = deviceModel; + return this; + } + + public Boolean getOnlineStatus() { + return onlineStatus; + } + + public DeviceTopology setOnlineStatus(Boolean onlineStatus) { + this.onlineStatus = onlineStatus; + return this; + } + + public String getUserId() { + return userId; + } + + public DeviceTopology setUserId(String userId) { + this.userId = userId; + return this; + } + + public String getUserCallsign() { + return userCallsign; + } + + public DeviceTopology setUserCallsign(String userCallsign) { + this.userCallsign = userCallsign; + return this; + } + + public DeviceIconUrl getIconUrls() { + return iconUrls; + } + + public DeviceTopology setIconUrls(DeviceIconUrl iconUrls) { + this.iconUrls = iconUrls; + return this; + } +} diff --git a/src/main/java/com/dji/sample/manage/model/enums/IconUrlEnum.java b/src/main/java/com/dji/sdk/cloudapi/tsa/IconUrlEnum.java similarity index 66% rename from src/main/java/com/dji/sample/manage/model/enums/IconUrlEnum.java rename to src/main/java/com/dji/sdk/cloudapi/tsa/IconUrlEnum.java index 3803d2f..8c91ea2 100644 --- a/src/main/java/com/dji/sample/manage/model/enums/IconUrlEnum.java +++ b/src/main/java/com/dji/sdk/cloudapi/tsa/IconUrlEnum.java @@ -1,4 +1,7 @@ -package com.dji.sample.manage.model.enums; +package com.dji.sdk.cloudapi.tsa; + +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; /** * The system icon that comes with the pilot. @@ -6,6 +9,7 @@ package com.dji.sample.manage.model.enums; * @version 0.3 * @date 2022/1/5 */ +@Schema(enumAsRef = true) public enum IconUrlEnum { SELECT_CAR("resource://pilot/drawable/tsa_car_select"), @@ -20,17 +24,13 @@ public enum IconUrlEnum { NORMAL_EQUIPMENT("resource://pilot/drawable/tsa_equipment_normal"); - /** - * You can use icons from the web, and the App internally downloads and caches these icons and - * loads them at a fixed size (28dp) to display on the map. - * Example: http://r56978dr7.hn-bkt.clouddn.com/tsa_equipment_normal.png - */ - private String url; + private final String url; IconUrlEnum(String url) { this.url = url; } + @JsonValue public String getUrl() { return url; } diff --git a/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyDeviceModel.java b/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyDeviceModel.java new file mode 100644 index 0000000..770c28b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyDeviceModel.java @@ -0,0 +1,82 @@ +package com.dji.sdk.cloudapi.tsa; + +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.dji.sdk.cloudapi.device.DeviceSubTypeEnum; +import com.dji.sdk.cloudapi.device.DeviceTypeEnum; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/8 + */ +@Schema(description = "topology device model") +public class TopologyDeviceModel { + + @NotNull + @JsonProperty("device_model_key") + private DeviceEnum deviceModelKey; + + @NotNull + private DeviceDomainEnum domain; + + @NotNull + private DeviceTypeEnum type; + + @NotNull + @JsonProperty("sub_type") + private DeviceSubTypeEnum subType; + + public TopologyDeviceModel() { + } + + @Override + public String toString() { + return "TopologyDeviceModel{" + + "deviceModelKey=" + deviceModelKey + + ", domain=" + domain + + ", type=" + type + + ", subType=" + subType + + '}'; + } + + public DeviceEnum getDeviceModelKey() { + return deviceModelKey; + } + + public TopologyDeviceModel setDeviceModelKey(DeviceEnum deviceModelKey) { + this.deviceModelKey = deviceModelKey; + return this; + } + + public DeviceDomainEnum getDomain() { + return domain; + } + + public TopologyDeviceModel setDomain(DeviceDomainEnum domain) { + this.domain = domain; + return this; + } + + public DeviceTypeEnum getType() { + return type; + } + + public TopologyDeviceModel setType(DeviceTypeEnum type) { + this.type = type; + return this; + } + + public DeviceSubTypeEnum getSubType() { + return subType; + } + + public TopologyDeviceModel setSubType(DeviceSubTypeEnum subType) { + this.subType = subType; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyList.java b/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyList.java new file mode 100644 index 0000000..4f41c57 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyList.java @@ -0,0 +1,55 @@ +package com.dji.sdk.cloudapi.tsa; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/8 + */ +@Schema(description = "device topology list") +public class TopologyList { + + @Valid + @Schema(description = "drone device topology collection") + @NotNull + private List hosts; + + @Valid + @Schema(description = "gateway device topology collection") + @NotNull + private List parents; + + public TopologyList() { + } + + @Override + public String toString() { + return "TopologyList{" + + "hosts=" + hosts + + ", parents=" + parents + + '}'; + } + + public List getHosts() { + return hosts; + } + + public TopologyList setHosts(List hosts) { + this.hosts = hosts; + return this; + } + + public List getParents() { + return parents; + } + + public TopologyList setParents(List parents) { + this.parents = parents; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyResponse.java b/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyResponse.java new file mode 100644 index 0000000..b3a2216 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/tsa/TopologyResponse.java @@ -0,0 +1,40 @@ +package com.dji.sdk.cloudapi.tsa; + +import com.dji.sdk.common.BaseModel; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/16 + */ +@Schema(description = "topology response data") +public class TopologyResponse extends BaseModel { + + @Valid + @NotNull + private List list; + + public TopologyResponse() { + } + + @Override + public String toString() { + return "TopologyResponse{" + + "list=" + list + + '}'; + } + + public List getList() { + return list; + } + + public TopologyResponse setList(List list) { + this.list = list; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/tsa/api/IHttpTsaService.java b/src/main/java/com/dji/sdk/cloudapi/tsa/api/IHttpTsaService.java new file mode 100644 index 0000000..6e9a2d8 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/tsa/api/IHttpTsaService.java @@ -0,0 +1,44 @@ +package com.dji.sdk.cloudapi.tsa.api; + +import com.dji.sdk.cloudapi.tsa.TopologyResponse; +import com.dji.sdk.common.HttpResultResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author sean + * @version 0.2 + * @date 2021/12/8 + */ +@Tag(name = "tsa interface") +public interface IHttpTsaService { + + String PREFIX = "manage/api/v1"; + + /** + * Get the topology list of all devices in the current user workspace for pilot display. + * @param workspaceId + * @param req + * @param rsp + * @return + */ + @Operation(summary = "obtain device topology list", description = "Get the topology list of all devices in the current user workspace for pilot display." + + "In the first connection, DJI Pilot 2 will call this interface to obtain the list topology of all devices." + + "Also, when Pilot receives a websocket command to notify the device of online, offline, and update, " + + "it will also call this interface to request the device topology list to be updated.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")), + }) + @GetMapping(PREFIX + "/workspaces/{workspace_id}/devices/topologies") + HttpResultResponse obtainDeviceTopologyList( + @PathVariable(name = "workspace_id") String workspaceId, + HttpServletRequest req, HttpServletResponse rsp); + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/BreakpointStateEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/BreakpointStateEnum.java new file mode 100644 index 0000000..ec958ee --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/BreakpointStateEnum.java @@ -0,0 +1,43 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public enum BreakpointStateEnum { + + WAYLINE_SEGMENT(0, "On the wayline segment"), + + WAYPOINT(1, "On the waypoint"); + + private final int state; + + private final String msg; + + BreakpointStateEnum(int state, String msg) { + this.state = state; + this.msg = msg; + } + + public int getState() { + return state; + } + + @JsonValue + public String getMsg() { + return msg; + } + + @JsonCreator + public static BreakpointStateEnum find(int state) { + return Arrays.stream(values()).filter(stateEnum -> stateEnum.state == state).findAny() + .orElseThrow(() -> new CloudSDKException(BreakpointStateEnum.class, state)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/DeviceExitHomingNotify.java b/src/main/java/com/dji/sdk/cloudapi/wayline/DeviceExitHomingNotify.java new file mode 100644 index 0000000..0225663 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/DeviceExitHomingNotify.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.wayline; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public class DeviceExitHomingNotify { + + private ExitingRTHActionEnum action; + + private String sn; + + private ExitingRTHReasonEnum reason; + + public DeviceExitHomingNotify() { + } + + @Override + public String toString() { + return "DeviceExitHomingNotify{" + + "action=" + action + + ", sn='" + sn + '\'' + + ", reason=" + reason + + '}'; + } + + public ExitingRTHActionEnum getAction() { + return action; + } + + public DeviceExitHomingNotify setAction(ExitingRTHActionEnum action) { + this.action = action; + return this; + } + + public String getSn() { + return sn; + } + + public DeviceExitHomingNotify setSn(String sn) { + this.sn = sn; + return this; + } + + public ExitingRTHReasonEnum getReason() { + return reason; + } + + public DeviceExitHomingNotify setReason(ExitingRTHReasonEnum reason) { + this.reason = reason; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/ExecutableConditions.java b/src/main/java/com/dji/sdk/cloudapi/wayline/ExecutableConditions.java new file mode 100755 index 0000000..768b301 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/ExecutableConditions.java @@ -0,0 +1,39 @@ +package com.dji.sdk.cloudapi.wayline; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public class ExecutableConditions { + + /** + * Storage capacity + * The minimum storage capacity of DJI dock or aircraft that can execute a task. Unit: MB. + * If the storage capacity doesn't satisfy the `storage_capacity`, task execution will fail. + */ + @NotNull + @Min(0) + private Integer storageCapacity; + + public ExecutableConditions() {} + + @Override + public String toString() { + return "ExecutableConditions{" + + "storageCapacity=" + storageCapacity + + '}'; + } + + public Integer getStorageCapacity() { + return storageCapacity; + } + + public ExecutableConditions setStorageCapacity(Integer storageCapacity) { + this.storageCapacity = storageCapacity; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/ExecutionStepEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/ExecutionStepEnum.java new file mode 100644 index 0000000..f142ce6 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/ExecutionStepEnum.java @@ -0,0 +1,66 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public enum ExecutionStepEnum { + + STARTING(0, 4, "MissionCenter starting, checking and resuming"), + + WAITING_STATE(5, 18, "Waiting state"), + + EXECUTING(19, 20, "Task executing"), + + RTH(21, 29, "Returning to home"), + + PULLING_LOGS(30, 39, "Pulling logs"), + + COMPLETE(40, 65534, "Interaction completed"), + + UNKNOWN(65535, 65535, "Unknown"); + + private final int min; + + private final int max; + + private final String msg; + + ExecutionStepEnum(int min, int max, String msg) { + this.min = min; + this.max = max; + this.msg = msg; + } + + @JsonValue + public int getMin() { + return min; + } + + public int getMax() { + return max; + } + + public String getMsg() { + return msg; + } + + @JsonCreator + public static ExecutionStepEnum find(int step) { + return Arrays.stream(values()).filter(stepEnum -> stepEnum.min <= step && stepEnum.max >= step).findAny() + .orElseThrow(() -> new CloudSDKException(ExecutionStepEnum.class, step)); + } + + @JsonCreator + public static ExecutionStepEnum find(String msg) { + return Arrays.stream(values()).filter(stepEnum -> stepEnum.msg.equals(msg)).findAny() + .orElseThrow(() -> new CloudSDKException(ExecutionStepEnum.class, msg)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/ExitingRTHActionEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/ExitingRTHActionEnum.java new file mode 100644 index 0000000..402cfa1 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/ExitingRTHActionEnum.java @@ -0,0 +1,44 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.cloudapi.device.ExitWaylineWhenRcLostEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public enum ExitingRTHActionEnum { + + EXIT(0, "Exit exiting RTH state"), + + Enter(1, "Enter exiting RTH state"); + + private final int action; + + private final String msg; + + ExitingRTHActionEnum(int action, String msg) { + this.action = action; + this.msg = msg; + } + + public int getAction() { + return action; + } + + @JsonValue + public String getMsg() { + return msg; + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static ExitingRTHActionEnum find(int action) { + return Arrays.stream(values()).filter(actionEnum -> actionEnum.action == action).findAny() + .orElseThrow(() -> new CloudSDKException(ExitWaylineWhenRcLostEnum.class, action)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/ExitingRTHReasonEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/ExitingRTHReasonEnum.java new file mode 100644 index 0000000..63b407d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/ExitingRTHReasonEnum.java @@ -0,0 +1,61 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public enum ExitingRTHReasonEnum { + + ADD_JOYSTICK_THROTTLE(0, "Add joystick throttle"), + + ADD_JOYSTICK_PITCH(1, "Add joystick pitch"), + + INITIALIZATION_FAILED(2, "The initialization of behavior tree is failed"), + + SURROUNDED_BY_OBSTACLES(3, "Surrounded by obstacles"), + + FLIGHT_RESTRICTION(4, "Flight restriction is triggered"), + + OBSTACLE_IS_TOO_CLOSED(5, "Obstacle is too closed"), + + NO_GPS(6, "No GPS signal"), + + GPS_AND_VIO_ARE_FALSE(7, "The output flag of GPS and VIO location is false"), + + ERROR_OF_GPS_AND_VIO(8, "The error of GPS and VIO fusion position is too large"), + + SHORT_DISTANCE_BACKTRACKING(9, "Backtrack in a short distance"), + + TRIGGER_RTH(10, "Trigger the RTH in a short distanc"); + + private final int reason; + + private final String msg; + + ExitingRTHReasonEnum(int reason, String msg) { + this.reason = reason; + this.msg = msg; + } + + public int getReason() { + return reason; + } + + @JsonValue + public String getMsg() { + return msg; + } + + @JsonCreator + public static ExitingRTHReasonEnum find(int reason) { + return Arrays.stream(values()).filter(reasonEnum -> reasonEnum.reason == reason).findAny() + .orElseThrow(() -> new CloudSDKException(ExitingRTHReasonEnum.class, reason)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakPoint.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakPoint.java new file mode 100755 index 0000000..6d0c232 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakPoint.java @@ -0,0 +1,163 @@ +package com.dji.sdk.cloudapi.wayline; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public class FlighttaskBreakPoint { + + /** + * Breakpoint index + */ + @NotNull + @Min(0) + private Integer index; + + /** + * Breakpoint state + */ + @NotNull + private BreakpointStateEnum state; + + /** + * Current wayline segment process + */ + @NotNull + @Min(0) + @Max(1) + private Float progress; + + /** + * Wayline ID + */ + private Integer waylineID; + + /** + * Break reason + */ + private FlighttaskBreakReasonEnum breakReason; + + /** + * Breakpoint latitude + */ + private Float latitude; + + /** + * Breakpoint longitude + */ + private Float longitude; + + /** + * Breakpoint altitude relative to the Earth's ellipsoid surface + * + */ + private Float height; + + /** + * Yaw angle relative to true north (meridian), with positive values from 0 to 6 o'clock direction and negative values from 6 to 12 o'clock direction + */ + private Integer attitudeHead; + + public FlighttaskBreakPoint() {} + + @Override + public String toString() { + return "FlighttaskBreakPoint{" + + "index=" + index + + ", state=" + state + + ", progress=" + progress + + ", waylineID=" + waylineID + + ", breakReason=" + breakReason + + ", latitude=" + latitude + + ", longitude=" + longitude + + ", height=" + height + + ", attitudeHead=" + attitudeHead + + '}'; + } + + public Integer getIndex() { + return index; + } + + public FlighttaskBreakPoint setIndex(Integer index) { + this.index = index; + return this; + } + + public BreakpointStateEnum getState() { + return state; + } + + public FlighttaskBreakPoint setState(BreakpointStateEnum state) { + this.state = state; + return this; + } + + public Float getProgress() { + return progress; + } + + public FlighttaskBreakPoint setProgress(Float progress) { + this.progress = progress; + return this; + } + + public Integer getWaylineID() { + return waylineID; + } + + public FlighttaskBreakPoint setWaylineID(Integer waylineID) { + this.waylineID = waylineID; + return this; + } + + public FlighttaskBreakReasonEnum getBreakReason() { + return breakReason; + } + + public FlighttaskBreakPoint setBreakReason(FlighttaskBreakReasonEnum breakReason) { + this.breakReason = breakReason; + return this; + } + + public Float getLatitude() { + return latitude; + } + + public FlighttaskBreakPoint setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public FlighttaskBreakPoint setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } + + public Float getHeight() { + return height; + } + + public FlighttaskBreakPoint setHeight(Float height) { + this.height = height; + return this; + } + + public Integer getAttitudeHead() { + return attitudeHead; + } + + public FlighttaskBreakPoint setAttitudeHead(Integer attitudeHead) { + this.attitudeHead = attitudeHead; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakReasonEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakReasonEnum.java new file mode 100644 index 0000000..8926a49 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakReasonEnum.java @@ -0,0 +1,11 @@ +package com.dji.sdk.cloudapi.wayline; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public enum FlighttaskBreakReasonEnum { + + // TODO 增加枚举值 +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskCreateFile.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskCreateFile.java new file mode 100755 index 0000000..271d2d4 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskCreateFile.java @@ -0,0 +1,51 @@ +package com.dji.sdk.cloudapi.wayline; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public class FlighttaskCreateFile { + + /** + * File URL + */ + @NotNull + private String url; + + /** + * MD5 signature + */ + @NotNull + private String sign; + + public FlighttaskCreateFile() {} + + @Override + public String toString() { + return "FlighttaskCreateFile{" + + "url='" + url + '\'' + + ", sign='" + sign + '\'' + + '}'; + } + + public String getUrl() { + return url; + } + + public FlighttaskCreateFile setUrl(String url) { + this.url = url; + return this; + } + + public String getSign() { + return sign; + } + + public FlighttaskCreateFile setSign(String sign) { + this.sign = sign; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskCreateRequest.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskCreateRequest.java new file mode 100755 index 0000000..c614c96 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskCreateRequest.java @@ -0,0 +1,73 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class FlighttaskCreateRequest extends BaseModel { + + /** + * Task ID + */ + @NotNull + private String flightId; + + /** + * Task type + */ + @NotNull + @Pattern(regexp = "^wayline$") + private String type = "wayline"; + + /** + * Wayline flighttaskFile object + */ + @NotNull + @Valid + private FlighttaskCreateFile file; + + public FlighttaskCreateRequest() {} + + @Override + public String toString() { + return "FlighttaskCreateRequest{" + + "flightId='" + flightId + '\'' + + ", type='" + type + '\'' + + ", file=" + file + + '}'; + } + + public String getFlightId() { + return flightId; + } + + public FlighttaskCreateRequest setFlightId(String flightId) { + this.flightId = flightId; + return this; + } + + public String getType() { + return type; + } + + public FlighttaskCreateRequest setType(String type) { + this.type = type; + return this; + } + + public FlighttaskCreateFile getFile() { + return file; + } + + public FlighttaskCreateRequest setFile(FlighttaskCreateFile file) { + this.file = file; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskExecuteRequest.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskExecuteRequest.java new file mode 100644 index 0000000..451b784 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskExecuteRequest.java @@ -0,0 +1,37 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +public class FlighttaskExecuteRequest extends BaseModel { + + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + private String flightId; + + public FlighttaskExecuteRequest() { + } + + @Override + public String toString() { + return "FlighttaskExecuteRequest{" + + "flightId='" + flightId + '\'' + + '}'; + } + + public String getFlightId() { + return flightId; + } + + public FlighttaskExecuteRequest setFlightId(String flightId) { + this.flightId = flightId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskFile.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskFile.java new file mode 100644 index 0000000..70837fc --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskFile.java @@ -0,0 +1,52 @@ +package com.dji.sdk.cloudapi.wayline; + +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public class FlighttaskFile { + + /** + * File URL + */ + @NotNull + private String url; + + /** + * File signature + */ + @NotNull + private String fingerprint; + + public FlighttaskFile() { + } + + @Override + public String toString() { + return "FlighttaskFile{" + + "url='" + url + '\'' + + ", fingerprint='" + fingerprint + '\'' + + '}'; + } + + public String getUrl() { + return url; + } + + public FlighttaskFile setUrl(String url) { + this.url = url; + return this; + } + + public String getFingerprint() { + return fingerprint; + } + + public FlighttaskFile setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskPrepareRequest.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskPrepareRequest.java new file mode 100755 index 0000000..41a7934 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskPrepareRequest.java @@ -0,0 +1,242 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.cloudapi.device.ExitWaylineWhenRcLostEnum; +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.common.CloudSDKVersionEnum; + +import javax.validation.Valid; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public class FlighttaskPrepareRequest extends BaseModel { + + /** + * Task ID + */ + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + private String flightId; + + /** + * Time to execute + * Millisecond timestamp of task execution time. Optional field. + * When the `task_type` is 0 or 1, it is required. When the `task_type` is 2, it is not required. + */ + @Min(123456789012L) + private Long executeTime; + + /** + * Task type + * The execution time of immediate task and timed task are defined by `execute_time`. + * The conditional task supports the task readiness condition defined by `ready_conditions`. + * The task can be executed if conditions are satisfied within a specified period. + * Immediate task has the highest priority. Timed task and conditional task have the same priority. + */ + @NotNull + private TaskTypeEnum taskType; + + /** + * Wayline type + */ + @NotNull + private WaylineTypeEnum waylineType; + + /** + * Wayline file object + */ + @NotNull + @Valid + private FlighttaskFile file; + + /** + * Task readiness condition + */ + private ReadyConditions readyConditions; + + /** + * Task executable condition + */ + private ExecutableConditions executableConditions; + + /** + * Wayline breakpoint information + */ + private FlighttaskBreakPoint breakPoint; + + /** + * Height for RTH + */ + @NotNull + @Min(20) + @Max(1500) + private Integer rthAltitude; + + /** + * Remote controller out of control action + * Out of control action: the current fixed transmitted value is 0, meaning Return-to-Home (RTH). + * Note that this enumeration value definition is inconsistent with the flight control and dock definitions, + * and a conversion exists at the dock end. + */ + @NotNull + private OutOfControlActionEnum outOfControlAction; + + /** + * wayline out of control action + * consistent with the KMZ file + */ + @NotNull + private ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost; + + @CloudSDKVersion(since = CloudSDKVersionEnum.V1_0_0) + private RthModeEnum rthMode = RthModeEnum.SETTING_HEIGHT; + + @Valid + @CloudSDKVersion(since = CloudSDKVersionEnum.V1_0_0) + private SimulateMission simulateMission; + + public FlighttaskPrepareRequest() {} + + @Override + public String toString() { + return "FlighttaskPrepareRequest{" + + "flightId='" + flightId + '\'' + + ", executeTime=" + executeTime + + ", taskType=" + taskType + + ", waylineType=" + waylineType + + ", file=" + file + + ", readyConditions=" + readyConditions + + ", executableConditions=" + executableConditions + + ", breakPoint=" + breakPoint + + ", rthAltitude=" + rthAltitude + + ", outOfControlAction=" + outOfControlAction + + ", exitWaylineWhenRcLost=" + exitWaylineWhenRcLost + + ", rthMode=" + rthMode + + ", simulateMission=" + simulateMission + + '}'; + } + + public String getFlightId() { + return flightId; + } + + public FlighttaskPrepareRequest setFlightId(String flightId) { + this.flightId = flightId; + return this; + } + + public Long getExecuteTime() { + return executeTime; + } + + public FlighttaskPrepareRequest setExecuteTime(Long executeTime) { + this.executeTime = executeTime; + return this; + } + + public TaskTypeEnum getTaskType() { + return taskType; + } + + public FlighttaskPrepareRequest setTaskType(TaskTypeEnum taskType) { + this.taskType = taskType; + return this; + } + + public WaylineTypeEnum getWaylineType() { + return waylineType; + } + + public FlighttaskPrepareRequest setWaylineType(WaylineTypeEnum waylineType) { + this.waylineType = waylineType; + return this; + } + + public FlighttaskFile getFile() { + return file; + } + + public FlighttaskPrepareRequest setFile(FlighttaskFile file) { + this.file = file; + return this; + } + + public ReadyConditions getReadyConditions() { + return readyConditions; + } + + public FlighttaskPrepareRequest setReadyConditions(ReadyConditions readyConditions) { + this.readyConditions = readyConditions; + return this; + } + + public ExecutableConditions getExecutableConditions() { + return executableConditions; + } + + public FlighttaskPrepareRequest setExecutableConditions(ExecutableConditions executableConditions) { + this.executableConditions = executableConditions; + return this; + } + + public FlighttaskBreakPoint getBreakPoint() { + return breakPoint; + } + + public FlighttaskPrepareRequest setBreakPoint(FlighttaskBreakPoint breakPoint) { + this.breakPoint = breakPoint; + return this; + } + + public Integer getRthAltitude() { + return rthAltitude; + } + + public FlighttaskPrepareRequest setRthAltitude(Integer rthAltitude) { + this.rthAltitude = rthAltitude; + return this; + } + + public OutOfControlActionEnum getOutOfControlAction() { + return outOfControlAction; + } + + public FlighttaskPrepareRequest setOutOfControlAction(OutOfControlActionEnum outOfControlAction) { + this.outOfControlAction = outOfControlAction; + return this; + } + + public ExitWaylineWhenRcLostEnum getExitWaylineWhenRcLost() { + return exitWaylineWhenRcLost; + } + + public FlighttaskPrepareRequest setExitWaylineWhenRcLost(ExitWaylineWhenRcLostEnum exitWaylineWhenRcLost) { + this.exitWaylineWhenRcLost = exitWaylineWhenRcLost; + return this; + } + + public RthModeEnum getRthMode() { + return rthMode; + } + + public FlighttaskPrepareRequest setRthMode(RthModeEnum rthMode) { + this.rthMode = rthMode; + return this; + } + + public SimulateMission getSimulateMission() { + return simulateMission; + } + + public FlighttaskPrepareRequest setSimulateMission(SimulateMission simulateMission) { + this.simulateMission = simulateMission; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgress.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgress.java new file mode 100644 index 0000000..d71bcca --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgress.java @@ -0,0 +1,54 @@ +package com.dji.sdk.cloudapi.wayline; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +public class FlighttaskProgress { + + private FlighttaskProgressExt ext; + + private FlighttaskProgressData progress; + + private FlighttaskStatusEnum status; + + public FlighttaskProgress() { + } + + @Override + public String toString() { + return "FlighttaskProgress{" + + "ext=" + ext + + ", progress=" + progress + + ", status=" + status + + '}'; + } + + public FlighttaskProgressExt getExt() { + return ext; + } + + public FlighttaskProgress setExt(FlighttaskProgressExt ext) { + this.ext = ext; + return this; + } + + public FlighttaskProgressData getProgress() { + return progress; + } + + public FlighttaskProgress setProgress(FlighttaskProgressData progress) { + this.progress = progress; + return this; + } + + public FlighttaskStatusEnum getStatus() { + return status; + } + + public FlighttaskProgress setStatus(FlighttaskStatusEnum status) { + this.status = status; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgressData.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgressData.java new file mode 100644 index 0000000..25693e2 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgressData.java @@ -0,0 +1,48 @@ +package com.dji.sdk.cloudapi.wayline; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +public class FlighttaskProgressData { + + /** + * Execution step + */ + private ExecutionStepEnum currentStep; + + /** + * Progress value + */ + private Integer percent; + + public FlighttaskProgressData() { + } + + @Override + public String toString() { + return "FlighttaskProgressData{" + + "currentStep=" + currentStep + + ", percent=" + percent + + '}'; + } + + public ExecutionStepEnum getCurrentStep() { + return currentStep; + } + + public FlighttaskProgressData setCurrentStep(ExecutionStepEnum currentStep) { + this.currentStep = currentStep; + return this; + } + + public Integer getPercent() { + return percent; + } + + public FlighttaskProgressData setPercent(Integer percent) { + this.percent = percent; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgressExt.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgressExt.java new file mode 100644 index 0000000..a924773 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskProgressExt.java @@ -0,0 +1,80 @@ +package com.dji.sdk.cloudapi.wayline; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +public class FlighttaskProgressExt { + + private Integer currentWaypointIndex; + + private Integer mediaCount; + + private String flightId; + + private String trackId; + + private FlighttaskBreakPoint breakPoint; + + public FlighttaskProgressExt() { + } + + @Override + public String toString() { + return "FlighttaskProgressExt{" + + "currentWaypointIndex=" + currentWaypointIndex + + ", mediaCount=" + mediaCount + + ", flightId='" + flightId + '\'' + + ", trackId='" + trackId + '\'' + + ", breakPoint=" + breakPoint + + '}'; + } + + public Integer getCurrentWaypointIndex() { + return currentWaypointIndex; + } + + public FlighttaskProgressExt setCurrentWaypointIndex(Integer currentWaypointIndex) { + this.currentWaypointIndex = currentWaypointIndex; + return this; + } + + public Integer getMediaCount() { + return mediaCount; + } + + public FlighttaskProgressExt setMediaCount(Integer mediaCount) { + this.mediaCount = mediaCount; + return this; + } + + public String getFlightId() { + return flightId; + } + + public FlighttaskProgressExt setFlightId(String flightId) { + this.flightId = flightId; + return this; + } + + public String getTrackId() { + return trackId; + } + + public FlighttaskProgressExt setTrackId(String trackId) { + this.trackId = trackId; + return this; + } + + public FlighttaskBreakPoint getBreakPoint() { + return breakPoint; + } + + public FlighttaskProgressExt setBreakPoint(FlighttaskBreakPoint breakPoint) { + this.breakPoint = breakPoint; + return this; + } +} + + diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskReady.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskReady.java new file mode 100644 index 0000000..471321f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskReady.java @@ -0,0 +1,35 @@ +package com.dji.sdk.cloudapi.wayline; + +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public class FlighttaskReady { + + /** + * The task ID set that currently satisfies the task readiness conditions + */ + private List flightIds; + + public FlighttaskReady() { + } + + @Override + public String toString() { + return "FlighttaskReady{" + + "flightIds=" + flightIds + + '}'; + } + + public List getFlightIds() { + return flightIds; + } + + public FlighttaskReady setFlightIds(List flightIds) { + this.flightIds = flightIds; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskResourceGetRequest.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskResourceGetRequest.java new file mode 100644 index 0000000..5da16d6 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskResourceGetRequest.java @@ -0,0 +1,30 @@ +package com.dji.sdk.cloudapi.wayline; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/25 + */ +public class FlighttaskResourceGetRequest { + + private String flightId; + + public FlighttaskResourceGetRequest() { + } + + @Override + public String toString() { + return "FlighttaskResourceGetRequest{" + + "flightId='" + flightId + '\'' + + '}'; + } + + public String getFlightId() { + return flightId; + } + + public FlighttaskResourceGetRequest setFlightId(String flightId) { + this.flightId = flightId; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskResourceGetResponse.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskResourceGetResponse.java new file mode 100755 index 0000000..1a1f275 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskResourceGetResponse.java @@ -0,0 +1,39 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public class FlighttaskResourceGetResponse extends BaseModel { + + /** + * Wayline file object + */ + @NotNull + @Valid + private FlighttaskFile file; + + public FlighttaskResourceGetResponse() {} + + @Override + public String toString() { + return "FlighttaskResourceGetResponse{" + + "file=" + file + + '}'; + } + + public FlighttaskFile getFile() { + return file; + } + + public FlighttaskResourceGetResponse setFile(FlighttaskFile file) { + this.file = file; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskStatusEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskStatusEnum.java new file mode 100644 index 0000000..d75d991 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskStatusEnum.java @@ -0,0 +1,59 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.2 + * @date 2022/8/17 + */ +public enum FlighttaskStatusEnum { + + SENT("sent", false), + + IN_PROGRESS("in_progress", false), + + OK("ok", true), + + PAUSED("paused", false), + + REJECTED("rejected", true), + + FAILED("failed", true), + + CANCELED("canceled", true), + + TIMEOUT("timeout", true), + + PARTIALLY_DONE("partially_done", true); + + private final String status; + + private final boolean end; + + FlighttaskStatusEnum(String status, boolean end) { + this.status = status; + this.end = end; + } + + @JsonValue + public String getStatus() { + return status; + } + + public boolean isEnd() { + return end; + } + + @JsonCreator + public static FlighttaskStatusEnum find(String status) { + return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny() + .orElseThrow(() -> new CloudSDKException(FlighttaskStatusEnum.class, status)); + } +} + + diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskUndoRequest.java b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskUndoRequest.java new file mode 100644 index 0000000..a38af28 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskUndoRequest.java @@ -0,0 +1,39 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/7 + */ +public class FlighttaskUndoRequest extends BaseModel { + + @NotNull + @Size(min = 1) + private List<@Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") String> flightIds; + + public FlighttaskUndoRequest() { + } + + @Override + public String toString() { + return "FlighttaskUndoRequest{" + + "flightIds=" + flightIds + + '}'; + } + + public List getFlightIds() { + return flightIds; + } + + public FlighttaskUndoRequest setFlightIds(List flightIds) { + this.flightIds = flightIds; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/GetWaylineListRequest.java b/src/main/java/com/dji/sdk/cloudapi/wayline/GetWaylineListRequest.java new file mode 100644 index 0000000..8ea9c41 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/GetWaylineListRequest.java @@ -0,0 +1,116 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/8 + */ +@Schema(description = "Query parameter to get list of wayline files") +public class GetWaylineListRequest { + + /** + * Is the wayline file favorited? + */ + @Parameter(name = "favorited", description = "Is the wayline file favorited?") + private Boolean favorited; + + /** + * order(xxx_column desc or xxx_column asc) + * Pilot2 optional value: nameupdate_time desc + */ + @NotNull + @JsonProperty("order_by") + @Parameter(name = "order_by", description = "sort field name", example = "update_time desc", schema = @Schema(allowableValues = {"name", "update_time desc"})) + private String orderBy; + + /** + * current page + */ + @Min(1) + @Parameter(name = "page", description = "current page", schema = @Schema(defaultValue = "1", type = "int")) + private int page = 1; + + /** + * page size + */ + @Min(1) + @JsonProperty("page_size") + @Parameter(name = "page_size", description = "page size", schema = @Schema(defaultValue = "10", type = "int")) + private int pageSize = 10; + + /** + * wayline template type collection + */ + @Size(min = 1) + @JsonProperty("template_type") + @Parameter(name = "template_type", description = "wayline template type collection", example = "[0]") + private List templateType; + + public GetWaylineListRequest() { + } + + @Override + public String toString() { + return "GetWaylineListRequest{" + + "favorited=" + favorited + + ", orderBy='" + orderBy + '\'' + + ", page=" + page + + ", pageSize=" + pageSize + + ", templateType=" + templateType + + '}'; + } + + public Boolean getFavorited() { + return favorited; + } + + public GetWaylineListRequest setFavorited(Boolean favorited) { + this.favorited = favorited; + return this; + } + + public String getOrderBy() { + return orderBy; + } + + public GetWaylineListRequest setOrderBy(String orderBy) { + this.orderBy = orderBy; + return this; + } + + public int getPage() { + return page; + } + + public GetWaylineListRequest setPage(int page) { + this.page = page; + return this; + } + + public int getPageSize() { + return pageSize; + } + + public GetWaylineListRequest setPageSize(int pageSize) { + this.pageSize = pageSize; + return this; + } + + public List getTemplateType() { + return templateType; + } + + public GetWaylineListRequest setTemplateType(List templateType) { + this.templateType = templateType; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/GetWaylineListResponse.java b/src/main/java/com/dji/sdk/cloudapi/wayline/GetWaylineListResponse.java new file mode 100644 index 0000000..8e856da --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/GetWaylineListResponse.java @@ -0,0 +1,203 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.dji.sdk.common.BaseModel; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 0.3 + * @date 2021/12/22 + */ +@Schema(description = "The data of the wayline file.") +public class GetWaylineListResponse extends BaseModel { + + /** + * wayline file name + */ + @NotNull + @Schema(description = "wayline file name", example = "waylineFile") + // TODO 排除特殊字符 + private String name; + + /** + * wayline file id + */ + @NotNull + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + @Schema(description = "wayline file id", format = "uuid") + private String id; + + /** + * drone device product enum + */ + @NotNull + @JsonProperty("drone_model_key") + @Schema(description = "drone device product enum", example = "0-67-0") + private DeviceEnum droneModelKey; + + private String sign; + + /** + * payload device product enum + */ + @NotNull + @Size(min = 1) + @JsonProperty("payload_model_keys") + @Schema(description = "payload device product enum", example = "[\"1-53-0\"]") + private List payloadModelKeys; + + /** + * Is the wayline file favorited? + */ + @NotNull + @Schema(description = "Is the wayline file favorited?") + private Boolean favorited; + + /** + * wayline template collection + */ + @NotNull + @Size(min = 1) + @Schema(description = "wayline template collection", example = "[0]") + @JsonProperty("template_types") + private List templateTypes; + + @NotNull + @Schema(description = "The key of the object in the bucket", example = "wayline/waylineFile.kmz") + @JsonProperty("object_key") + private String objectKey; + + /** + * uploader + */ + @NotNull + @JsonProperty("user_name") + @Schema(description = "uploader's username", example = "admin") + private String username; + + /** + * update time (millisecond) + */ + @NotNull + @Min(123456789012L) + @Schema(description = "update time (millisecond). The field named `update time` must exist in the table.", example = "123456789012") + @JsonProperty("update_time") + private Long updateTime; + + public GetWaylineListResponse() { + } + + @Override + public String toString() { + return "GetWaylineListResponse{" + + "name='" + name + '\'' + + ", id='" + id + '\'' + + ", droneModelKey=" + droneModelKey + + ", sign='" + sign + '\'' + + ", payloadModelKeys=" + payloadModelKeys + + ", favorited=" + favorited + + ", templateTypes=" + templateTypes + + ", objectKey='" + objectKey + '\'' + + ", username='" + username + '\'' + + ", updateTime=" + updateTime + + '}'; + } + + public String getName() { + return name; + } + + public GetWaylineListResponse setName(String name) { + this.name = name; + return this; + } + + public String getId() { + return id; + } + + public GetWaylineListResponse setId(String id) { + this.id = id; + return this; + } + + public DeviceEnum getDroneModelKey() { + return droneModelKey; + } + + public GetWaylineListResponse setDroneModelKey(DeviceEnum droneModelKey) { + this.droneModelKey = droneModelKey; + return this; + } + + public String getSign() { + return sign; + } + + public GetWaylineListResponse setSign(String sign) { + this.sign = sign; + return this; + } + + public List getPayloadModelKeys() { + return payloadModelKeys; + } + + public GetWaylineListResponse setPayloadModelKeys(List payloadModelKeys) { + this.payloadModelKeys = payloadModelKeys; + return this; + } + + public Boolean getFavorited() { + return favorited; + } + + public GetWaylineListResponse setFavorited(Boolean favorited) { + this.favorited = favorited; + return this; + } + + public List getTemplateTypes() { + return templateTypes; + } + + public GetWaylineListResponse setTemplateTypes(List templateTypes) { + this.templateTypes = templateTypes; + return this; + } + + public String getObjectKey() { + return objectKey; + } + + public GetWaylineListResponse setObjectKey(String objectKey) { + this.objectKey = objectKey; + return this; + } + + public String getUsername() { + return username; + } + + public GetWaylineListResponse setUsername(String username) { + this.username = username; + return this; + } + + public Long getUpdateTime() { + return updateTime; + } + + public GetWaylineListResponse setUpdateTime(Long updateTime) { + this.updateTime = updateTime; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/OutOfControlActionEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/OutOfControlActionEnum.java new file mode 100755 index 0000000..4a7cc17 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/OutOfControlActionEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/6 + */ +public enum OutOfControlActionEnum { + + RETURN_TO_HOME(0), + + HOVERING(1), + + LANDING(2); + + private final int action; + + OutOfControlActionEnum(int action) { + this.action = action; + } + + @JsonValue + public int getAction() { + return action; + } + + @JsonCreator + public static OutOfControlActionEnum find(int action) { + return Arrays.stream(values()).filter(actionEnum -> actionEnum.action == action).findAny() + .orElseThrow(() -> new CloudSDKException(OutOfControlActionEnum.class, action)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/ReadyConditions.java b/src/main/java/com/dji/sdk/cloudapi/wayline/ReadyConditions.java new file mode 100755 index 0000000..6194e8d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/ReadyConditions.java @@ -0,0 +1,77 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.common.BaseModel; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class ReadyConditions extends BaseModel { + + /** + * Battery capacity + * The aircraft battery percentage threshold of the executable task. + * The aircraft battery must be greater than the `battery_capacity` when the task starts. + */ + @NotNull + @Min(0) + @Max(1) + private Integer batteryCapacity; + + /** + * Start time of the task executable period + * Start millisecond timestamp of the task executable period. The task execution time should be later than the `begin_time`. + */ + @NotNull + private Long beginTime; + + /** + * End time of the task executable period + * End millisecond timestamp of the task executable period. The task execution time should be earlier than the `end_time`. + */ + @NotNull + private Long endTime; + + public ReadyConditions() {} + + @Override + public String toString() { + return "ReadyConditions{" + + "batteryCapacity=" + batteryCapacity + + ", beginTime=" + beginTime + + ", endTime=" + endTime + + '}'; + } + + public Integer getBatteryCapacity() { + return batteryCapacity; + } + + public ReadyConditions setBatteryCapacity(Integer batteryCapacity) { + this.batteryCapacity = batteryCapacity; + return this; + } + + public Long getBeginTime() { + return beginTime; + } + + public ReadyConditions setBeginTime(Long beginTime) { + this.beginTime = beginTime; + return this; + } + + public Long getEndTime() { + return endTime; + } + + public ReadyConditions setEndTime(Long endTime) { + this.endTime = endTime; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/RthModeEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/RthModeEnum.java new file mode 100644 index 0000000..2d4713d --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/RthModeEnum.java @@ -0,0 +1,37 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/8/4 + */ +public enum RthModeEnum { + + SMART_HEIGHT(0), + + SETTING_HEIGHT(1); + + private final int rthMode; + + RthModeEnum(int rthMode) { + this.rthMode = rthMode; + } + + @JsonValue + public int getRthMode() { + return rthMode; + } + + @JsonCreator + public static RthModeEnum find(int rthMode) { + return Arrays.stream(values()).filter(rthModeEnum -> rthModeEnum.rthMode == rthMode).findAny() + .orElseThrow(() -> new CloudSDKException(RthModeEnum.class, rthMode)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/SimulateMission.java b/src/main/java/com/dji/sdk/cloudapi/wayline/SimulateMission.java new file mode 100644 index 0000000..bf3c4bc --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/SimulateMission.java @@ -0,0 +1,65 @@ +package com.dji.sdk.cloudapi.wayline; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 1.7 + * @date 2023/8/4 + */ +public class SimulateMission { + + @NotNull + private SimulateSwitchEnum isEnable; + + @NotNull + @Min(-90) + @Max(90) + private Float latitude; + + @NotNull + @Min(-180) + @Max(180) + private Float longitude; + + public SimulateMission() { + } + + @Override + public String toString() { + return "SimulateMission{" + + "isEnable=" + isEnable + + ", latitude=" + latitude + + ", longitude=" + longitude + + '}'; + } + + public SimulateSwitchEnum getIsEnable() { + return isEnable; + } + + public SimulateMission setIsEnable(SimulateSwitchEnum isEnable) { + this.isEnable = isEnable; + return this; + } + + public Float getLatitude() { + return latitude; + } + + public SimulateMission setLatitude(Float latitude) { + this.latitude = latitude; + return this; + } + + public Float getLongitude() { + return longitude; + } + + public SimulateMission setLongitude(Float longitude) { + this.longitude = longitude; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/SimulateSwitchEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/SimulateSwitchEnum.java new file mode 100644 index 0000000..3036a4b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/SimulateSwitchEnum.java @@ -0,0 +1,37 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/8/4 + */ +public enum SimulateSwitchEnum { + + DISABLE(0), + + ENABLE(1); + + private final int state; + + SimulateSwitchEnum(int state) { + this.state = state; + } + + @JsonValue + public int getState() { + return state; + } + + @JsonCreator + public static SimulateSwitchEnum find(int state) { + return Arrays.stream(values()).filter(stateEnum -> stateEnum.state == state).findAny() + .orElseThrow(() -> new CloudSDKException(SimulateSwitchEnum.class, state)); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/TaskTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/TaskTypeEnum.java new file mode 100755 index 0000000..b1179fb --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/TaskTypeEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +public enum TaskTypeEnum { + + IMMEDIATE(0), + + TIMED(1), + + CONDITIONAL(2); + + private final int type; + + TaskTypeEnum(int type) { + this.type = type; + } + + @JsonValue + public int getType() { + return this.type; + } + + @JsonCreator + public static TaskTypeEnum find(int type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type == type).findAny() + .orElseThrow(() -> new CloudSDKException(TaskTypeEnum.class, type)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineErrorCodeEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineErrorCodeEnum.java new file mode 100644 index 0000000..0c1275c --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineErrorCodeEnum.java @@ -0,0 +1,322 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.mqtt.events.IEventsErrorCode; +import com.dji.sdk.mqtt.services.IServicesErrorCode; +import com.fasterxml.jackson.annotation.JsonCreator; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public enum WaylineErrorCodeEnum implements IServicesErrorCode, IEventsErrorCode, IErrorInfo { + + SUCCESS(0, "success"), + + WRONG_PARAM(314001, "Failed to distribute task. Try again later"), + + MD5_EMPTY(314002, "The issued wayline task md5 is empty."), + + WRONG_WAYLINE_FILE(314003, "Wayline file format not supported. Check file."), + + DISTRIBUTE_TASK_FAILED_1(314004, "Failed to distribute task."), + + MD5_CHECK_FAILED(314005, "Wayline MD5 check failed."), + + INITIATE_AIRCRAFT_FAILED_1(314006, "Failed to initiate aircraft. Restart dock and try again."), + + TRANSFER_KMZ_FILE_FAILED(314007, "Failed to distribute wayline file from dock to aircraft."), + + PREPARATION_TIMED_OUT(314008, "Aircraft task preparation timed out. Restart dock and try again."), + + INITIATE_AIRCRAFT_FAILED_2(314009, "Failed to initiate aircraft. Restart dock and try again."), + + PERFORM_TASK_FAILED(314010, "Unable to perform task."), + + QUERY_TIMEOUT(314011, "Wayline execution result query timed out."), + + PREPARATION_FAILED_1(314012, "Aircraft task preparation failed. Unable to perform task. Restart dock and try again."), + + WRONG_KMZ_URL(314013, "Get KMZ download address failed."), + + DOCK_SYSTEM_ERROR_1(314014, "Dock system error. Failed to perform task. Try again later."), + + CLOSE_FOURTH_GENERATION_FAILED(314015, "Failed to distribute AI-Spot Check wayline from dock to aircraft. Unable to perform task. Try again later or restart dock and try again."), + + PROCESS_KMZ_FILE_FAILED_1(314016, "Failed to process flight route file. Unable to perform task. Check file."), + + MODIFY_KMZ_FILE_FAILED(314017, "Failed to modify the KMZ file of AI Spot-Check."), + + AIRCRAFT_RTK_ERROR(314018, "Aircraft RTK positioning error. Unable to perform task. Try again later or restart dock and try again."), + + CONVERGE_RTK_FAILED_1(314019, "Failed to converge aircraft RTK data. Unable to perform task. Try again later or restart dock and try again."), + + AIRCRAFT_POSITION_ERROR(314020, "Aircraft not in the middle of landing pad or aircraft heading incorrect. Unable to perform task. Check aircraft position and heading."), + + AIRCRAFT_RTK_POSITIONING_ERROR(314021, "Aircraft RTK positioning error. Unable to perform task. Try again later or restart dock and try again."), + + MODIFY_KMZ_BREAKPOINT_FILE_FAILED(314022, "Failed to modify KMZ file of resuming flight from breakpoint"), + + SETTING_BACKUP_LANDING_POINT_FAILED(316001, "Backup landing point setting failed"), + + SETTING_BACKUP_SAFE_HEIGHT_FAILED(316002, "Backup safe height for transfer setting failed"), + + SETTING_TAKEOFF_HEIGHT_FAILED(316003, "Take-off height setting failed. Note: The default safe take-off height of the aircraft is set to 1.8 meters by dock. The aircraft will fly to 1.8 meters after take-off, and cannot be interrupted during the 0-1.8 meters take-off process, and other actions can only be performed after take-off. This altitude is used by the dock by default and does not support modification. The purpose is to prevent personal injury."), + + SETTING_OUT_OF_CONTROL_ACTION_FAILED(316004, "Out of control action setting failed."), + + CONVERGE_RTK_FAILED_2(316005, "Failed to converge aircraft RTK data. Unable to perform task. Restart dock and try again."), + + DOCK_PREPARATION_FAILED(316006, "Aircraft unable to land on dock. Dock cover closed or driving rods pushed into place. Check aircraft status on dock deployment site."), + + INITIATE_AIRCRAFT_FAILED(316007, "Failed to initiate aircraft. Restart dock and try again."), + + OBTAIN_FLIGHT_CONTROL_FAILED(316008, "Dock failed to obtain aircraft flight control. Unable to perform task. Make sure flight control not locked by remote controller."), + + LOW_POWER(316009, "Aircraft battery level low. Unable to perform task. Wait until aircraft is charged up to 50% and try again"), + + AIRCRAFT_NOT_DETECTED(316010, "Aircraft not detected. Unable to perform task. Check if aircraft is inside dock and linked to dock, or restart dock and try again."), + + LANDED_ON_INCORRECT_LOCATION(316011, "Aircraft landed on incorrect location. Check if aircraft should be manually placed on dock deployment site."), + + FOLDER_COLORING_FAILED(316012, "Aircraft task preparation failed. Folder coloring failed."), + + OBTAIN_BATTERY_POWER_FAILED(316013, "Query of battery power failed."), + + FLIGHT_CONTROL_PUSHING_TIMED_OUT(316014, "The receive of flight control pushing timed out."), + + AIRCRAFT_LOCATION_TOO_FAR(316015, "Aircraft location calibrated by RTK device is far from dock. Unable to perform task. Restart dock and try again."), + + LANDING_TIMEOUT(316016, "Aircraft landing on dock timed out. Aircraft and dock may be disconnected. Check livestream view to see if aircraft landed on dock"), + + OBTAIN_MEDIA_TIMEOUT(316017, "Obtaining number of aircraft media files timed out. Aircraft and dock may be disconnected. Check livestream view to see if aircraft landed on dock"), + + TASK_PERFORMANCE_TIMED_OUT(316018, "Task performance timed out. Aircraft and dock may be disconnected. Check livestream view to see if aircraft landed on dock"), + + CAMERA_COLORING_TIMED_OUT(316019, "Camera coloring timed out"), + + RTK_SOURCE_ERROR(316020, "Aircraft RTK signal source error."), + + RTK_SOURCE_TIMEOUT(316021, "Checking aircraft RTK signal source timed out."), + + AIRCRAFT_NOT_CONNECTED(316022, "Aircraft unable to return to home. Check if aircraft is powered on, aircraft and dock are connected, and try again"), + + NO_FLIGHT_CONTROL_1(316023, "Aircraft controlled by Controller B and unable to return to home. Control aircraft from Controller B or power off remote controller and try again."), + + WRONG_COMMAND(316024, "Aircraft failed to return to home. Check if aircraft has taken off and try again."), + + SETTING_AIRCRAFT_PARAMETERS_FAILED(316025, "Failed to configure aircraft parameters. Try again later or restart dock and try again."), + + EMERGENCY_BUTTON_PRESSED_DOWN(316026, "Dock emergency stop button pressed down. Unable to perform task. Release button and try again."), + + SETTING_AIRCRAFT_PARAMETERS_TIMEOUT(316027, "Setting aircraft parameters timed out. Try again later or restart dock and try again."), + + FLYING_TO_BACKUP_POINT_1(316029, "Dock emergency stop button pressed down. Aircraft flying to alternate landing site. Make sure aircraft has safely landed and place aircraft inside dock"), + + REFRESH_HOME_POINT_FAILED(316030, "Refresh of home point failed. Please try again."), + + SETTING_RTH_MODE_FAILED(316031, "Failed to set return home mode. Please try again."), + + LOW_POWER_LANDING_OUTSIDE(316050, "The aircraft has landed outside the dock due to low battery. Please check immediately whether the aircraft has landed safely and return the aircraft to the dock."), + + TASK_ABNORMAL_LANDING_OUTSIDE(316051, "The wayline task is abnormal, the aircraft landed outside the dock, please check immediately whether the aircraft has landed safely and return the aircraft to the dock."), + + FLYING_TO_BACKUP_POINT_2(316052, "The wayline task is abnormal, the aircraft will fly to the backup landing point, please check immediately whether the aircraft has landed safely and return the aircraft to the dock."), + + USER_CONTROL_LANDING(316053, "The user controls the aircraft to land."), + + OBTAIN_MEDIA_FAILED(317001, "Failed to obtain number of aircraft media files."), + + CAMERA_NOT_CONNECTED(317002, "Failed to format aircraft storage. Make sure aircraft is powered on and connected to dock and camera can be detected. Or restart aircraft and try again."), + + FORMAT_AIRCRAFT_STORAGE_FAILED(317003, "Failed to format aircraft storage."), + + FORMAT_MEDIA_FILES_FAILED(317004, "Failed to format media files."), + + STOP_RECORDING_FAILED(317005, "Aircraft video recording terminated unsuccessfully, media files for this flight mission may not be able to be uploaded."), + + NOT_IDLE(319001, "Unable to perform task. Dock is performing task or uploading issue logs. Wait until task is complete or logs uploaded and try again."), + + DOCK_SYSTEM_ERROR_2(319002, "Dock system error. Restart dock and try again."), + + TASK_ID_NOT_EXIST(319003, "Task ID doesn't exist in dock"), + + TASK_EXPIRE(319004, "The task has expired."), + + FLIGHTTASK_EXECUTE_COMMAND_TIMEOUT(319005, "Execution command delivery timed out. Unable to perform task."), + + CANCEL_TASK_FAILED_1(319006, "Failed to cancel task. Task in progress."), + + EDIT_TASK_FAILED(319007, "Failed to edit task. Task in progress."), + + TIME_NOT_SYNCED(319008, "Dock and cloud time not synced. Dock unable to perform task."), + + DISTRIBUTE_TASK_FAILED_2(319009, "Failed to distribute task. Try again later or restart dock and try again."), + + VERSION_TOO_EARLY(319010, "Dock firmware version too early. Unable to perform task. Update dock to latest version and try again."), + + INITIALIZING_DOCK(319015, "Initializing dock. Unable to perform task. Wait until initialization is complete."), + + PERFORMING_OTHER_TASK(319016, "Dock performing other task. Unable to perform current task."), + + PROCESSING_MEDIA_FILE(319017, "Dock processing media files captured in last task. Unable to perform current task. Try again later."), + + EXPORTING_LOGS(319018, "Unable to perform task. Dock uploading issue logs. Try again later."), + + PULLING_LOGS(319019, "Unable to perform task. Dock obtaining issue logs. Try again later."), + + PAUSE_TASK_FAILED(319020, "Failed to pause flight task."), + + DISABLE_FLIGHT_CONTROL_FAILED(319021, "Failed to disable Live Flight Controls."), + + FLYTO_TASK_FAILED(319022, "FlyTo task failed."), + + STOP_FLYTO_TASK_FAILED(319023, "Failed to stop FlyTo task."), + + TAKING_OFF_TASK_FAILED(319024, "One-key taking off failed."), + + TASK_IN_PREPARATION(319025, "Task in preparation. Dock unable to perform task distributed from cloud. Try again later"), + + LOW_POWER_THAN_SET_VALUE(319026, "Aircraft battery level lower than set value. Unable to perform task. Wait until charging completes and try again."), + + INSUFFICIENT_STORAGE(319027, "Insufficient storage on dock or aircraft. Unable to perform task. Wait until media files are uploaded to cloud and try again."), + + NO_FLIGHT_CONTROL_2(319030, "Dock has no flight control authority."), + + NO_PAYLOAD_CONTROL(319031, "Dock has no payload control authority"), + + WRONG_POINT_NUMBER(319032, "Flyto target point, the point number is wrong."), + + SEQ_NUMBER_SMALLER_THAN_LAST(319033, "DRC - flight control failed. Package sequence number is smaller than last one."), + + DELAY_TIME_SMALLER_THAN_SET(319034, "DRC - flight control failed. Package received time out."), + + EMERGENCY_STOP_FAILED(319035, "Emergency stop failed, please try again."), + + REMOTE_DEBUGGING_MODE(319036, "Device in remote debugging mode. "), + + ONSITE_DEBUGGING_MODE(319037, "Device in onsite debugging mode."), + + UPDATING(319038, "Updating device. Try again later."), + + RESUME_TASK_FAILED(319042, "Failed to resume flight."), + + CANCEL_TASK_FAILED_2(319043, "Failed to cancel RTH."), + + NO_BREAKPOINT(319044, "Task completed. Unable to resume."), + + EMERGENCY_STOP_STATUS(319045, "DRC - flight control failed. Aircraft paused."), + + NOT_IN_WAYLINE(319046, "Task completed or paused. Unable to pause."), + + DOCK_SYSTEM_ERROR_3(319999, "Dock system error. Restart dock and try again."), + + TASK_ERROR(321000, "Task error. Try again later or restart dock and try again."), + + PROCESS_KMZ_FILE_FAILED_2(321004, "Failed to process flight route file. Unable to perform task. Check file."), + + MISSING_BREAKPOINT(321005, "Missing breakpoint info in wayline."), + + TASK_IN_PROGRESS(321257, "Task in progress. Failed to start task again."), + + STATUS_NOT_SUPPORTED(321258, "Unable to stop task. Check aircraft status."), + + NOT_STARTED_CANNOT_STOP(321259, "Task not started. Unable to stop task."), + + NOT_STARTED_CANNOT_INTERRUPT(321260, "Task not started. Unable to pause task."), + + HEIGHT_LIMIT(321513, "Unable to perform task. Flight route altitude greater than aircraft max flight altitude."), + + DISTANCE_LIMIT(321514, "Failed to perform task. Flight route start or end point in buffer zone or exceeds distance limit."), + + GEO_ZONE(321515, "Unable to perform task. Aircraft will fly across GEO Zone."), + + HEIGHT_TOO_LOW(321516, "Flight altitude too low. Task stopped."), + + OBSTACLE_SENSED(321517, "Obstacle sensed. Task stopped."), + + APPROACHED_GEO_ZONE(321519, "Aircraft approached GEO Zone or reached max distance and automatically returned to home. Unable to complete task."), + + PROPELLER_CHECK_FAILED(321523, "Aircraft propeller check failed. Propeller may be damaged. Try again later. Contact DJI Support to replace propeller if issue persists."), + + PREPARATION_FAILED_2(321524, "The preparation before takeoff of the aircraft has failed, possibly due to the aircraft's inability to locate or gear error. Please check the status of the aircraft."), + + WEAK_GPS(321769, "Aircraft satellite positioning signal weak. Unable to perform task. Restart dock and try again."), + + WRONG_GEAR_MODE(321770, "Aircraft flight mode error. Unable to perform task. Restart dock and try again."), + + HOME_POINT_NOT_SET(321771, "Aircraft Home Point not set. Unable to perform task. Restart dock and try again."), + + LOW_POWER_PERFORM_TASK(321772, "Aircraft battery level low. Unable to perform task. Wait until aircraft is charged up to 50% and try again."), + + LOW_POWER_RTH(321773, "Aircraft battery level low and returned to home. Unable to complete task."), + + AIRCRAFT_SIGNAL_LOST(321775, "Aircraft signal lost when performing task."), + + RTK_NOT_READY(321776, "Failed to converge aircraft RTK data. Unable to perform task. Restart dock and try again."), + + NOT_HOVERING(321777, "Aircraft not hovering. Unable to start task."), + + B_CONTROL_PROPELLERS(321778, "Unable to perform task. Aircraft controlled by Controller B, and propellers started."), + + USER_CONTROL(322282, "Task stopped. Aircraft control obtained by cloud user or Controller B."), + + USER_SEND_RTH(322283, "RTH command sent by user. Aircraft unable to complete task."), + + WRONG_BREAKPOINT(322539, "Breakpoint info error. Dock unable to perform task"), + + EMPTY_ACTION_LAYER(322594, "The layer of action tree can not be empty."), + + WRONG_TASK(386535, "Task error. Try again later or restart dock and try again."), + + SET_MEDIA_PRIORITY_FAILED(324030, "Setting priority of media upload failed, the task does not exist in the upload queue."), + + MEDIA_PRIORITY_COMMAND_TOO_FAST(324031, "Setting priority of media upload failed, the action of issuing commands is too fast, and the response to the last command has not yet ended."), + + MEDIA_PRIORITY_WRONG_PARAMETER(324032, "Setting priority of media upload failed, incorrect parameter."), + + UNKNOWN(-1, "UNKNOWN"), + + ; + + + private final String msg; + + private final int code; + + WaylineErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String getMessage() { + return this.msg; + } + + @Override + public Integer getCode() { + return this.code; + } + + @Override + public String toString() { + return "{" + + "code='" + code + '\'' + + ", message=" + msg + + '}'; + } + + /** + * @param code error code + * @return enumeration object + */ + @JsonCreator + public static WaylineErrorCodeEnum find(int code) { + return Arrays.stream(values()).filter(codeEnum -> codeEnum.code == code).findAny().orElse(UNKNOWN); + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineMethodEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineMethodEnum.java new file mode 100755 index 0000000..019be1e --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineMethodEnum.java @@ -0,0 +1,39 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean + * @version 1.3 + * @date 2022/11/14 + */ +public enum WaylineMethodEnum { + + FLIGHTTASK_CREATE("flighttask_create"), + + FLIGHTTASK_PREPARE("flighttask_prepare"), + + FLIGHTTASK_EXECUTE("flighttask_execute"), + + FLIGHTTASK_UNDO("flighttask_undo"), + + FLIGHTTASK_PAUSE("flighttask_pause"), + + FLIGHTTASK_RECOVERY("flighttask_recovery"), + + RETURN_HOME("return_home"), + + RETURN_HOME_CANCEL("return_home_cancel"); + + private final String method; + + WaylineMethodEnum(String method) { + this.method = method; + } + + @JsonValue + public String getMethod() { + return this.method; + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineTypeEnum.java b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineTypeEnum.java new file mode 100644 index 0000000..874963b --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineTypeEnum.java @@ -0,0 +1,51 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/9/26 + */ +@Schema(enumAsRef = true, type = "int", allowableValues = {"0", "1", "2", "3"}, + description = "

0: waypoint

1: mapping2d

2: mapping3d

3: mappingStrip

") +public enum WaylineTypeEnum { + + WAYPOINT(0, "waypoint"), + + MAPPING_2D(1, "mapping2d"), + + MAPPING_3D(2, "mapping3d"), + + MAPPING_STRIP(3, "mappingStrip"); + + private final int value; + + private final String type; + + WaylineTypeEnum(int value, String type) { + this.value = value; + this.type = type; + } + + @JsonValue + public int getValue() { + return value; + } + + @JsonCreator + public static WaylineTypeEnum find(int value) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.value == value).findAny() + .orElseThrow(() -> new CloudSDKException(WaylineTypeEnum.class, value)); + } + + public static WaylineTypeEnum find(String type) { + return Arrays.stream(values()).filter(typeEnum -> typeEnum.type.equals(type)).findAny() + .orElseThrow(() -> new CloudSDKException(WaylineTypeEnum.class, type)); + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineUploadCallbackMetadata.java b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineUploadCallbackMetadata.java new file mode 100644 index 0000000..aa97993 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineUploadCallbackMetadata.java @@ -0,0 +1,83 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/12 + */ +@Schema(description = "Wayline file metadata") +public class WaylineUploadCallbackMetadata { + + /** + * drone device product enum + */ + @NotNull + @Schema(description = "drone device product enum", example = "0-67-0") + @JsonProperty("drone_model_key") + private DeviceEnum droneModelKey; + + /** + * payload device product enum + */ + @NotNull + @Size(min = 1) + @JsonProperty("payload_model_keys") + @Schema(description = "payload device product enum", example = "[\"1-53-0\"]") + private List payloadModelKeys; + + /** + * wayline template collection + */ + @NotNull + @Size(min = 1) + @Schema(description = "wayline template collection", example = "[0]") + @JsonProperty("template_types") + private List templateTypes; + + public WaylineUploadCallbackMetadata() { + } + + @Override + public String toString() { + return "WaylineUploadCallbackMetadata{" + + "droneModelKey='" + droneModelKey + '\'' + + ", payloadModelKeys=" + payloadModelKeys + + ", templateTypes=" + templateTypes + + '}'; + } + + public DeviceEnum getDroneModelKey() { + return droneModelKey; + } + + public WaylineUploadCallbackMetadata setDroneModelKey(DeviceEnum droneModelKey) { + this.droneModelKey = droneModelKey; + return this; + } + + public List getPayloadModelKeys() { + return payloadModelKeys; + } + + public WaylineUploadCallbackMetadata setPayloadModelKeys(List payloadModelKeys) { + this.payloadModelKeys = payloadModelKeys; + return this; + } + + public List getTemplateTypes() { + return templateTypes; + } + + public WaylineUploadCallbackMetadata setTemplateTypes(List templateTypes) { + this.templateTypes = templateTypes; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineUploadCallbackRequest.java b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineUploadCallbackRequest.java new file mode 100644 index 0000000..87f8214 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/WaylineUploadCallbackRequest.java @@ -0,0 +1,69 @@ +package com.dji.sdk.cloudapi.wayline; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author sean + * @version 0.3 + * @date 2021/12/23 + */ +@Schema(description = "The data class of the upload result callback") +public class WaylineUploadCallbackRequest { + + @NotNull + @Schema(description = "The key of the object in the bucket", example = "wayline/waylineFile.kmz") + @JsonProperty("object_key") + private String objectKey; + + @NotNull + @Schema(description = "wayline file name", example = "waylineFile") + private String name; + + @Valid + @NotNull + @Schema(description = "wayline file metadata") + private WaylineUploadCallbackMetadata metadata; + + public WaylineUploadCallbackRequest() { + } + + @Override + public String toString() { + return "WaylineUploadCallbackRequest{" + + "objectKey='" + objectKey + '\'' + + ", name='" + name + '\'' + + ", metadata=" + metadata + + '}'; + } + + public String getObjectKey() { + return objectKey; + } + + public WaylineUploadCallbackRequest setObjectKey(String objectKey) { + this.objectKey = objectKey; + return this; + } + + public String getName() { + return name; + } + + public WaylineUploadCallbackRequest setName(String name) { + this.name = name; + return this; + } + + public WaylineUploadCallbackMetadata getMetadata() { + return metadata; + } + + public WaylineUploadCallbackRequest setMetadata(WaylineUploadCallbackMetadata metadata) { + this.metadata = metadata; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/api/AbstractWaylineService.java b/src/main/java/com/dji/sdk/cloudapi/wayline/api/AbstractWaylineService.java new file mode 100644 index 0000000..b295d0f --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/api/AbstractWaylineService.java @@ -0,0 +1,195 @@ +package com.dji.sdk.cloudapi.wayline.api; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.cloudapi.wayline.*; +import com.dji.sdk.common.CloudSDKVersionEnum; +import com.dji.sdk.common.Common; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.GatewayTypeEnum; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttReply; +import com.dji.sdk.mqtt.events.EventsDataRequest; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; +import com.dji.sdk.mqtt.services.ServicesPublish; +import com.dji.sdk.mqtt.services.ServicesReplyData; +import com.dji.sdk.mqtt.services.TopicServicesResponse; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public abstract class AbstractWaylineService { + + @Resource + private ServicesPublish servicesPublish; + + /** + * Notification of device exits the Return to Home (RTH) state + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_DEVICE_EXIT_HOMING_NOTIFY, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse deviceExitHomingNotify(TopicEventsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("deviceExitHomingNotify not implemented"); + } + + /** + * Report wayline task progress + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHTTASK_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse flighttaskProgress(TopicEventsRequest> request, MessageHeaders headers) { + throw new UnsupportedOperationException("flighttaskProgress not implemented"); + } + + /** + * Notification of task readiness + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHTTASK_READY, outputChannel = ChannelName.OUTBOUND_EVENTS) + public TopicEventsResponse flighttaskReady(TopicEventsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("flighttaskReady not implemented"); + } + + /** + * Create wayline task (Deprecated) + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(deprecated = CloudSDKVersionEnum.V0_0_1, exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flighttaskCreate(GatewayManager gateway, FlighttaskCreateRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + WaylineMethodEnum.FLIGHTTASK_CREATE.getMethod(), + request); + } + + /** + * Issue wayline task + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flighttaskPrepare(GatewayManager gateway, FlighttaskPrepareRequest request) { + validPrepareParam(request); + return servicesPublish.publish( + gateway.getGatewaySn(), + WaylineMethodEnum.FLIGHTTASK_PREPARE.getMethod(), + request, + request.getFlightId()); + } + + /** + * Execute wayline task + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flighttaskExecute(GatewayManager gateway, FlighttaskExecuteRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + WaylineMethodEnum.FLIGHTTASK_EXECUTE.getMethod(), + request, + request.getFlightId()); + } + + /** + * Cancel wayline task + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flighttaskUndo(GatewayManager gateway, FlighttaskUndoRequest request) { + return servicesPublish.publish( + gateway.getGatewaySn(), + WaylineMethodEnum.FLIGHTTASK_UNDO.getMethod(), + request); + } + + /** + * Pause wayline task + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flighttaskPause(GatewayManager gateway) { + return servicesPublish.publish( + gateway.getGatewaySn(), + WaylineMethodEnum.FLIGHTTASK_PAUSE.getMethod()); + } + + /** + * Resume wayline task + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse flighttaskRecovery(GatewayManager gateway) { + return servicesPublish.publish( + gateway.getGatewaySn(), + WaylineMethodEnum.FLIGHTTASK_RECOVERY.getMethod()); + } + + /** + * Return to Home (RTH) + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse returnHome(GatewayManager gateway) { + return servicesPublish.publish( + gateway.getGatewaySn(), + WaylineMethodEnum.RETURN_HOME.getMethod()); + } + + /** + * Cancel return to home + * @param gateway + * @return services_reply + */ + @CloudSDKVersion(exclude = GatewayTypeEnum.RC) + public TopicServicesResponse returnHomeCancel(GatewayManager gateway) { + return servicesPublish.publish( + gateway.getGatewaySn(), + WaylineMethodEnum.RETURN_HOME_CANCEL.getMethod()); + } + + /** + * Get the wayline task resource + * @param request data + * @param headers The headers for a {@link Message}. + * @return events_reply + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_FLIGHTTASK_RESOURCE_GET, outputChannel = ChannelName.OUTBOUND_REQUESTS) + public TopicRequestsResponse> flighttaskResourceGet(TopicRequestsRequest request, MessageHeaders headers) { + throw new UnsupportedOperationException("flighttaskResourceGet not implemented"); + } + + + private void validPrepareParam(FlighttaskPrepareRequest request) { + if (null == request.getExecuteTime() + && (TaskTypeEnum.IMMEDIATE == request.getTaskType() || TaskTypeEnum.TIMED == request.getTaskType())) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER, "Execute time must not be null."); + } + if (TaskTypeEnum.CONDITIONAL == request.getTaskType()) { + Common.validateModel(request.getReadyConditions()); + } + } + +} diff --git a/src/main/java/com/dji/sdk/cloudapi/wayline/api/IHttpWaylineService.java b/src/main/java/com/dji/sdk/cloudapi/wayline/api/IHttpWaylineService.java new file mode 100644 index 0000000..17fae68 --- /dev/null +++ b/src/main/java/com/dji/sdk/cloudapi/wayline/api/IHttpWaylineService.java @@ -0,0 +1,151 @@ +package com.dji.sdk.cloudapi.wayline.api; + +import com.dji.sdk.cloudapi.wayline.GetWaylineListRequest; +import com.dji.sdk.cloudapi.wayline.GetWaylineListResponse; +import com.dji.sdk.cloudapi.wayline.WaylineUploadCallbackRequest; +import com.dji.sdk.common.HttpResultResponse; +import com.dji.sdk.common.PaginationData; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +/** + * @author sean + * @version 0.3 + * @date 2021/12/22 + */ +@Tag(name = "wayline interface") +public interface IHttpWaylineService { + + String PREFIX = "wayline/api/v1"; + + /** + * Query the basic data of the wayline file according to the query conditions. + * The query condition field in pilot is fixed. + * @param workspaceId workspace id + * @param request get waylines params + * @param req + * @param rsp + * @return wayline list + */ + @Operation(summary = "get wayline list", description = "Query the basic data of the wayline file according to " + + "the query conditions. The query condition field in pilot is fixed.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")) + }) + @GetMapping(PREFIX + "/workspaces/{workspace_id}/waylines") + HttpResultResponse> getWaylineList( + @Valid @ParameterObject GetWaylineListRequest request, + @PathVariable(name = "workspace_id") String workspaceId, + HttpServletRequest req, HttpServletResponse rsp); + + /** + * Query the download address of the file according to the wayline file id, + * and redirect to this address directly for download. + * @param workspaceId workspace id + * @param waylineId wayline file id + * @param req + * @param rsp + */ + @Operation(summary = "get wayline file download address", description = "Query the download address of the file " + + "according to the wayline file id, and redirect to this address directly for download.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", schema = @Schema(format = "uuid")), + @Parameter(name = "wayline_id", description = "wayline id", schema = @Schema(format = "uuid")) + }) + @GetMapping(PREFIX + "/workspaces/{workspace_id}/waylines/{wayline_id}/url") + void getWaylineFileDownloadAddress( + @PathVariable(name = "workspace_id") String workspaceId, + @PathVariable(name = "wayline_id") String waylineId, + HttpServletRequest req, HttpServletResponse rsp); + + /** + * Checking whether the name already exists according to the wayline name must ensure the uniqueness of the wayline name. + * This interface will be called when uploading waylines and must be available. + * @param workspaceId workspace id + * @param names wayline file name collection + * @param req + * @param rsp + * @return already existing wayline name + */ + @Operation(summary = "get duplicated wayline name", description = "Checking whether the name already exists " + + "according to the wayline name must ensure the uniqueness of the wayline name. " + + "This interface will be called when uploading waylines and must be available.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", required = true), + @Parameter(name = "name", description = "wayline file name", required = true) + }) + @GetMapping(PREFIX + "/workspaces/{workspace_id}/waylines/duplicate-names") + HttpResultResponse> getDuplicatedWaylineName( + @PathVariable(name = "workspace_id") String workspaceId, + @NotNull @Size(min = 1) @RequestParam(name = "name") List names, + HttpServletRequest req, HttpServletResponse rsp); + + /** + * When the wayline file is uploaded to the storage server by pilot, + * the basic information of the file is reported through this interface. + * @param workspaceId workspace id + * @param request upload callback params + * @param req + * @param rsp + * @return success + */ + @Operation(summary = "file upload result report", description = "When the wayline file is uploaded to the " + + "storage server by pilot, the basic information of the file is reported through this interface.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", required = true) + }) + @PostMapping(PREFIX + "/workspaces/{workspace_id}/upload-callback") + HttpResultResponse fileUploadResultReport( + @PathVariable(name = "workspace_id") String workspaceId, + @Valid @RequestBody WaylineUploadCallbackRequest request, + HttpServletRequest req, HttpServletResponse rsp); + + /** + * Favorite the wayline file according to the wayline file id. + * @param workspaceId workspace id + * @param ids wayline file id + * @param req + * @param rsp + * @return success + */ + @Operation(summary = "batch favorites wayline", description = "Favorite the wayline file according to the wayline file id.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", required = true), + @Parameter(name = "id", description = "wayline id", required = true) + }) + @PostMapping(PREFIX + "/workspaces/{workspace_id}/favorites") + HttpResultResponse batchFavoritesWayline( + @PathVariable(name = "workspace_id") String workspaceId, + @NotNull @Size(min = 1) @RequestParam(name = "id") List ids, + HttpServletRequest req, HttpServletResponse rsp); + + /** + * Delete the favorites of this wayline file based on the wayline file id. + * @param workspaceId workspace id + * @param ids wayline file id + * @param req + * @param rsp + * @return success + */ + @Operation(summary = "batch unfavorites wayline", description = "Delete the favorites of this wayline file based on the wayline file id.", + parameters = { + @Parameter(name = "workspace_id", description = "workspace id", required = true), + @Parameter(name = "id", description = "wayline id", required = true) + }) + @DeleteMapping(PREFIX + "/workspaces/{workspace_id}/favorites") + HttpResultResponse batchUnfavoritesWayline( + @PathVariable(name = "workspace_id") String workspaceId, + @NotNull @Size(min = 1) @RequestParam(name = "id") List ids, + HttpServletRequest req, HttpServletResponse rsp); +} diff --git a/src/main/java/com/dji/sdk/common/BaseModel.java b/src/main/java/com/dji/sdk/common/BaseModel.java new file mode 100644 index 0000000..00fde60 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/BaseModel.java @@ -0,0 +1,81 @@ +package com.dji.sdk.common; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import org.springframework.util.CollectionUtils; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class BaseModel { + + private final static Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); + + public void valid() { + this.valid(null); + } + + public void checkProperty(String fieldName, GatewayManager gateway) { + try { + Field field = this.getClass().getDeclaredField(fieldName); + CloudSDKVersion annotation = field.getDeclaredAnnotation(CloudSDKVersion.class); + if (!gateway.isTypeSupport(annotation) || !gateway.isVersionSupport(annotation)) { + throw new CloudSDKException(CloudSDKErrorEnum.DEVICE_PROPERTY_NOT_SUPPORT, fieldName); + } + } catch (NoSuchFieldException e) { + throw new CloudSDKException(e); + } + } + + public void valid(GatewayManager gateway) { + Set> violations = VALIDATOR.validate(this); + if (null != gateway) { + Set names = new HashSet<>(); + violations = violations.stream().filter(violation -> + filterProperty(gateway, violation.getRootBeanClass(), + violation.getPropertyPath().toString().split("\\."), 0, true, names)) + .collect(Collectors.toSet()); + } + + if (CollectionUtils.isEmpty(violations)) { + return; + } + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER, violations.stream() + .map(violation -> violation.getPropertyPath().toString() + violation.getMessage() + + ", Current value is: " + violation.getInvalidValue()) + .collect(Collectors.joining("; "))); + + } + + private boolean filterProperty(GatewayManager gateway, Class clazz, String[] fields, int index, boolean isValid, Set names) { + if (!isValid || index == fields.length) { + return isValid; + } + String name = String.join(".", Arrays.copyOf(fields, index + 1)); + if (names.contains(name)) { + return false; + } + try { + Field field = clazz.getDeclaredField(fields[index]); + isValid = gateway.isPropertyValid(field.getAnnotation(CloudSDKVersion.class)); + if (!isValid) { + names.add(name); + } + return filterProperty(gateway, field.getType(), fields, index + 1, isValid, names); + } catch (NoSuchFieldException e) { + throw new CloudSDKException(e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/common/CloudSDKVersionEnum.java b/src/main/java/com/dji/sdk/common/CloudSDKVersionEnum.java new file mode 100644 index 0000000..7ace3e1 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/CloudSDKVersionEnum.java @@ -0,0 +1,35 @@ +package com.dji.sdk.common; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/22 + */ +public enum CloudSDKVersionEnum { + + V0_0_1("0.0.1"), + + V1_0_0("1.0.0"), + + DEFAULT("1.0.0"), + + V99("99"); + + private final String version; + + CloudSDKVersionEnum(String version) { + this.version = version; + } + + public String getVersion() { + return version; + } + + public boolean isSupported(CloudSDKVersionEnum version) { + return this.version.compareTo(version.getVersion()) >= 0; + } + + public boolean isDeprecated(CloudSDKVersionEnum version) { + return this.version.compareTo(version.getVersion()) >= 0; + } +} diff --git a/src/main/java/com/dji/sdk/common/Common.java b/src/main/java/com/dji/sdk/common/Common.java new file mode 100644 index 0000000..25924fe --- /dev/null +++ b/src/main/java/com/dji/sdk/common/Common.java @@ -0,0 +1,60 @@ +package com.dji.sdk.common; + +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public class Common { + + private static final JsonMapper.Builder MAPPER_BUILDER = JsonMapper.builder(); + + static { + 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"))); + + MAPPER_BUILDER.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE) + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS) + .addModule(timeModule) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) + .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); + } + + public static void validateModel(BaseModel model) { + if (null == model) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER, "Param must not be null."); + } + model.valid(); + } + + public static void validateModel(BaseModel model, GatewayManager gateway) { + if (null == model) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER, "Param must not be null."); + } + model.valid(gateway); + } + + public static ObjectMapper getObjectMapper() { + return MAPPER_BUILDER.build(); + } +} diff --git a/src/main/java/com/dji/sdk/common/CommonErrorEnum.java b/src/main/java/com/dji/sdk/common/CommonErrorEnum.java new file mode 100644 index 0000000..dcf71b7 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/CommonErrorEnum.java @@ -0,0 +1,42 @@ + +package com.dji.sdk.common; + +import com.dji.sdk.mqtt.events.IEventsErrorCode; +import com.dji.sdk.mqtt.services.IServicesErrorCode; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public enum CommonErrorEnum implements IServicesErrorCode, IEventsErrorCode, IErrorInfo { + + STATUS_NOT_SUPPORTED(314000, "The device is either uploading logs or executing a flight mission. Please try again later."), + + WRONG_PARAMETER(325001, "Cloud command parameter error. Dock unable to execute command."), + + UNKNOWN(-1, "Unknown"); + + private final int code; + + private final String msg; + + CommonErrorEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public Integer getCode() { + return this.code; + } + + public String getMessage() { + return this.msg; + } + + public static CommonErrorEnum find(int code) { + return Arrays.stream(values()).filter(error -> error.code == code).findAny().orElse(UNKNOWN); + } +} diff --git a/src/main/java/com/dji/sdk/common/DockThingVersionEnum.java b/src/main/java/com/dji/sdk/common/DockThingVersionEnum.java new file mode 100644 index 0000000..3a8d4fb --- /dev/null +++ b/src/main/java/com/dji/sdk/common/DockThingVersionEnum.java @@ -0,0 +1,52 @@ +package com.dji.sdk.common; + +import com.dji.sdk.exception.CloudSDKVersionException; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; +import java.util.Optional; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public enum DockThingVersionEnum { + + V1_0_0("1.0.0", CloudSDKVersionEnum.V0_0_1), + + V1_1_0("1.1.0", CloudSDKVersionEnum.V1_0_0), + + V1_1_2("1.1.2", CloudSDKVersionEnum.V1_0_0), + + V1_2_0("1.2.0", CloudSDKVersionEnum.V1_0_0), + + ; + + private final String thingVersion; + + private final CloudSDKVersionEnum cloudSDKVersion; + + DockThingVersionEnum(String thingVersion, CloudSDKVersionEnum cloudSDKVersion) { + this.thingVersion = thingVersion; + this.cloudSDKVersion = cloudSDKVersion; + } + + @JsonValue + public String getThingVersion() { + return thingVersion; + } + + public CloudSDKVersionEnum getCloudSDKVersion() { + return cloudSDKVersion; + } + + public static DockThingVersionEnum find(String thingVersion) { + Optional opt = Arrays.stream(values()) + .filter(thingVersionEnum -> thingVersionEnum.thingVersion.equals(thingVersion)).findAny(); + if (opt.isPresent()) { + return opt.get(); + } + throw new CloudSDKVersionException(thingVersion); + } +} diff --git a/src/main/java/com/dji/sdk/common/DroneThingVersionEnum.java b/src/main/java/com/dji/sdk/common/DroneThingVersionEnum.java new file mode 100644 index 0000000..04f9701 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/DroneThingVersionEnum.java @@ -0,0 +1,56 @@ +package com.dji.sdk.common; + +import com.dji.sdk.exception.CloudSDKVersionException; +import com.fasterxml.jackson.annotation.JsonValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Optional; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public enum DroneThingVersionEnum { + + V1_0_0("1.0.0", CloudSDKVersionEnum.V0_0_1), + + V1_1_0("1.1.0", CloudSDKVersionEnum.V1_0_0), + + V1_1_2("1.1.2", CloudSDKVersionEnum.V1_0_0), + + V1_2_0("1.2.0", CloudSDKVersionEnum.V1_0_0), + + ; + + private static final Logger log = LoggerFactory.getLogger(DroneThingVersionEnum.class); + + private final String thingVersion; + + private final CloudSDKVersionEnum cloudSDKVersion; + + DroneThingVersionEnum(String thingVersion, CloudSDKVersionEnum cloudSDKVersion) { + this.thingVersion = thingVersion; + this.cloudSDKVersion = cloudSDKVersion; + } + + @JsonValue + public String getThingVersion() { + return thingVersion; + } + + public CloudSDKVersionEnum getCloudSDKVersion() { + return cloudSDKVersion; + } + + public static DroneThingVersionEnum find(String thingVersion) { + Optional opt = Arrays.stream(values()) + .filter(thingVersionEnum -> thingVersionEnum.thingVersion.equals(thingVersion)).findAny(); + if (opt.isPresent()) { + return opt.get(); + } + throw new CloudSDKVersionException(thingVersion); + } +} diff --git a/src/main/java/com/dji/sdk/common/ErrorCodeSourceEnum.java b/src/main/java/com/dji/sdk/common/ErrorCodeSourceEnum.java new file mode 100644 index 0000000..01c1ef2 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/ErrorCodeSourceEnum.java @@ -0,0 +1,39 @@ + +package com.dji.sdk.common; + +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public enum ErrorCodeSourceEnum { + + DEVICE(3), + + DOCK(5), + + PILOT(6); + + private final int source; + + ErrorCodeSourceEnum(int source) { + this.source = source; + } + + @JsonValue + public int getSource() { + return source; + } + + @JsonCreator + public static ErrorCodeSourceEnum find(int source) { + return Arrays.stream(values()).filter(error -> error.source == source).findAny() + .orElseThrow(() -> new CloudSDKException(ErrorCodeSourceEnum.class, source)); + } +} diff --git a/src/main/java/com/dji/sdk/common/GatewayManager.java b/src/main/java/com/dji/sdk/common/GatewayManager.java new file mode 100644 index 0000000..a8e3e6e --- /dev/null +++ b/src/main/java/com/dji/sdk/common/GatewayManager.java @@ -0,0 +1,88 @@ +package com.dji.sdk.common; + +import com.dji.sdk.annotations.CloudSDKVersion; + +import java.util.Arrays; +import java.util.Objects; + +/** + * SDK information corresponding to the gateway device + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public class GatewayManager { + + private String gatewaySn; + private GatewayThingVersion gatewayThingVersion; + private DroneThingVersionEnum droneThingVersion; + private GatewayTypeEnum type; + private CloudSDKVersionEnum sdkVersion; + private String droneSn; + + private GatewayManager(String gatewaySn, String droneSn, GatewayTypeEnum gatewayType) { + this.gatewaySn = gatewaySn; + this.type = gatewayType; + this.droneSn = droneSn; + } + + public GatewayManager(String gatewaySn, String droneSn, GatewayTypeEnum gatewayType, String gatewayThingVersion, String droneThingVersion) { + this(gatewaySn, droneSn, gatewayType); + this.gatewayThingVersion = new GatewayThingVersion(gatewayType, gatewayThingVersion); + if (GatewayTypeEnum.RC == gatewayType) { + this.sdkVersion = CloudSDKVersionEnum.DEFAULT; + return; + } + if (Objects.isNull(this.droneThingVersion)) { + this.sdkVersion = this.gatewayThingVersion.getCloudSDKVersion(); + return; + } + this.droneThingVersion = DroneThingVersionEnum.find(droneThingVersion); + this.sdkVersion = this.gatewayThingVersion.getCloudSDKVersion().isSupported(this.droneThingVersion.getCloudSDKVersion()) ? + this.droneThingVersion.getCloudSDKVersion() : this.gatewayThingVersion.getCloudSDKVersion(); + } + + public String getGatewaySn() { + return gatewaySn; + } + + public GatewayThingVersion getGatewayThingVersion() { + return gatewayThingVersion; + } + + public DroneThingVersionEnum getDroneThingVersion() { + return droneThingVersion; + } + + public GatewayTypeEnum getType() { + return type; + } + + public CloudSDKVersionEnum getSdkVersion() { + return sdkVersion; + } + + public String getDroneSn() { + return droneSn; + } + + public boolean isTypeSupport(CloudSDKVersion version) { + return null != version && Arrays.stream(version.exclude()).noneMatch(typeEnum -> typeEnum == this.getType()) + && (version.include().length == 0 + || Arrays.stream(version.include()).anyMatch(typeEnum -> typeEnum == this.getType())); + } + + public boolean isVersionSupport(CloudSDKVersion version) { + return null != version && this.getSdkVersion().isSupported(version.since()) && !isDeprecated(version); + } + + public boolean isDeprecated(CloudSDKVersion version) { + return null != version && this.getSdkVersion().isDeprecated(version.deprecated()); + } + + public boolean isPropertyValid(CloudSDKVersion version) { + return null == version || + (!this.getSdkVersion().isDeprecated(version.since()) + && this.getSdkVersion().isSupported(version.since())); + } +} diff --git a/src/main/java/com/dji/sdk/common/GatewayThingVersion.java b/src/main/java/com/dji/sdk/common/GatewayThingVersion.java new file mode 100644 index 0000000..b8c39fd --- /dev/null +++ b/src/main/java/com/dji/sdk/common/GatewayThingVersion.java @@ -0,0 +1,48 @@ +package com.dji.sdk.common; + +import java.util.Objects; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public class GatewayThingVersion { + + private DockThingVersionEnum dockThingVersion; + + private RcThingVersionEnum rcThingVersion; + + public GatewayThingVersion(DockThingVersionEnum dockThingVersion) { + this.dockThingVersion = dockThingVersion; + } + + public GatewayThingVersion(RcThingVersionEnum rcThingVersion) { + this.rcThingVersion = rcThingVersion; + } + + public GatewayThingVersion(GatewayTypeEnum type, String thingVersion) { + switch (type) { + case DOCK: + this.dockThingVersion = DockThingVersionEnum.find(thingVersion); + return; + case RC: + this.rcThingVersion = RcThingVersionEnum.find(thingVersion); + return; + } + } + + public String getThingVersion() { + if (Objects.nonNull(dockThingVersion)) { + return dockThingVersion.getThingVersion(); + } + return rcThingVersion.getThingVersion(); + } + + public CloudSDKVersionEnum getCloudSDKVersion() { + if (Objects.nonNull(dockThingVersion)) { + return dockThingVersion.getCloudSDKVersion(); + } + return rcThingVersion.getCloudSDKVersion(); + } +} diff --git a/src/main/java/com/dji/sdk/common/GatewayTypeEnum.java b/src/main/java/com/dji/sdk/common/GatewayTypeEnum.java new file mode 100644 index 0000000..05b4d9b --- /dev/null +++ b/src/main/java/com/dji/sdk/common/GatewayTypeEnum.java @@ -0,0 +1,33 @@ +package com.dji.sdk.common; + +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public enum GatewayTypeEnum { + + RC(DeviceEnum.RC, DeviceEnum.RC_PLUS, DeviceEnum.RC_PRO), + + DOCK(DeviceEnum.DOCK); + + private final DeviceEnum[] gateway; + + GatewayTypeEnum(DeviceEnum... gateway) { + this.gateway = gateway; + } + + public DeviceEnum[] getGateway() { + return gateway; + } + + public static GatewayTypeEnum find(DeviceEnum device) { + return Arrays.stream(values()).filter(gateway -> Arrays.stream(gateway.gateway).anyMatch(deviceEnum -> device == deviceEnum)) + .findAny().orElseThrow(() -> new CloudSDKException(GatewayTypeEnum.class, device)); + } +} diff --git a/src/main/java/com/dji/sdk/common/HttpResultResponse.java b/src/main/java/com/dji/sdk/common/HttpResultResponse.java new file mode 100644 index 0000000..9e3efb9 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/HttpResultResponse.java @@ -0,0 +1,95 @@ +package com.dji.sdk.common; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "The data format of the http response.") +public class HttpResultResponse { + + public static final int CODE_SUCCESS = 0; + public static final int CODE_FAILED = -1; + public static final String MESSAGE_SUCCESS = "success"; + public static final String MESSAGE_FAILED = "failed"; + + @Schema(description = "0 means success, non-zero means error.", example = "0") + private int code; + + @Schema(description = "The response message.", example = MESSAGE_SUCCESS) + private String message; + + @Schema(description = "The response data.") + private T data; + + public HttpResultResponse() { + } + + @Override + public String toString() { + return "HttpResultResponse{" + + "code=" + code + + ", message='" + message + '\'' + + ", data=" + data + + '}'; + } + + public int getCode() { + return code; + } + + public HttpResultResponse setCode(int code) { + this.code = code; + return this; + } + + public String getMessage() { + return message; + } + + public HttpResultResponse setMessage(String message) { + this.message = message;; + return this; + } + + public T getData() { + return data; + } + + public HttpResultResponse setData(T data) { + this.data = data; + return this; + } + + public static HttpResultResponse success() { + return new HttpResultResponse() + .setCode(CODE_SUCCESS) + .setMessage(MESSAGE_SUCCESS) + .setData(""); + } + + public static HttpResultResponse success(T data) { + return HttpResultResponse.success().setData(data); + } + + public static HttpResultResponse error() { + return new HttpResultResponse() + .setCode(CODE_FAILED) + .setMessage(MESSAGE_FAILED); + } + + public static HttpResultResponse error(String message) { + return new HttpResultResponse() + .setCode(CODE_FAILED) + .setMessage(message); + } + + public static HttpResultResponse error(int code, String message) { + return new HttpResultResponse() + .setCode(code) + .setMessage(message); + } + + public static HttpResultResponse error(IErrorInfo errorInfo) { + return new HttpResultResponse() + .setCode(errorInfo.getCode()) + .setMessage(errorInfo.getMessage()); + } +} diff --git a/src/main/java/com/dji/sample/common/error/IErrorInfo.java b/src/main/java/com/dji/sdk/common/IErrorInfo.java similarity index 72% rename from src/main/java/com/dji/sample/common/error/IErrorInfo.java rename to src/main/java/com/dji/sdk/common/IErrorInfo.java index 3332dd3..c4965f4 100644 --- a/src/main/java/com/dji/sample/common/error/IErrorInfo.java +++ b/src/main/java/com/dji/sdk/common/IErrorInfo.java @@ -1,4 +1,4 @@ -package com.dji.sample.common.error; +package com.dji.sdk.common; /** * @author sean.zhou @@ -11,12 +11,12 @@ public interface IErrorInfo { * Get error message. * @return error message */ - String getErrorMsg(); + String getMessage(); /** * Get error code. * @return error code */ - Integer getErrorCode(); + Integer getCode(); } diff --git a/src/main/java/com/dji/sdk/common/Pagination.java b/src/main/java/com/dji/sdk/common/Pagination.java new file mode 100644 index 0000000..7a025d3 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/Pagination.java @@ -0,0 +1,80 @@ +package com.dji.sdk.common; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Used for paging display. These field names cannot be changed. + * Because they need to be the same as the pilot. + * @author sean + * @version 0.3 + * @date 2021/12/22 + */ +@Schema(description = "Used for paging display") +public class Pagination { + + /** + * The current page number. + */ + @Schema(description = "The current page number.", example = "1") + private long page; + + /** + * The amount of data displayed per page. + */ + @Schema(description = "The amount of data displayed per page.", example = "10") + @JsonProperty("page_size") + private long pageSize; + + /** + * The total amount of all data. + */ + @Schema(description = "The total amount of all data.", example = "10") + private long total; + + public Pagination() { + } + + public Pagination(Page page) { + this.page = page.getCurrent(); + this.pageSize = page.getSize(); + this.total = page.getTotal(); + } + + @Override + public String toString() { + return "Pagination{" + + "page=" + page + + ", pageSize=" + pageSize + + ", total=" + total + + '}'; + } + + public long getPage() { + return page; + } + + public Pagination setPage(long page) { + this.page = page; + return this; + } + + public long getPageSize() { + return pageSize; + } + + public Pagination setPageSize(long pageSize) { + this.pageSize = pageSize; + return this; + } + + public long getTotal() { + return total; + } + + public Pagination setTotal(long total) { + this.total = total; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/common/PaginationData.java b/src/main/java/com/dji/sdk/common/PaginationData.java new file mode 100644 index 0000000..c690de9 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/PaginationData.java @@ -0,0 +1,58 @@ +package com.dji.sdk.common; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +/** + * The format of the data response when a paginated display is required. + * @author sean + * @version 0.3 + * @date 2021/12/22 + */ +@Schema(description = "Format of paged data") +public class PaginationData { + + /** + * The collection in which the data list is stored. + */ + @Schema(description = "The collection in which the data list is stored.") + private List list; + + @Schema(description = "Used for paging display. These field names cannot be changed. Because they need to be the same as the pilot.") + private Pagination pagination; + + public PaginationData() { + } + + public PaginationData(List list, Pagination pagination) { + this.list = list; + this.pagination = pagination; + } + + @Override + public String toString() { + return "PaginationData{" + + "list=" + list + + ", pagination=" + pagination + + '}'; + } + + public List getList() { + return list; + } + + public PaginationData setList(List list) { + this.list = list; + return this; + } + + public Pagination getPagination() { + return pagination; + } + + public PaginationData setPagination(Pagination pagination) { + this.pagination = pagination; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/common/RcThingVersionEnum.java b/src/main/java/com/dji/sdk/common/RcThingVersionEnum.java new file mode 100644 index 0000000..1a263d9 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/RcThingVersionEnum.java @@ -0,0 +1,35 @@ +package com.dji.sdk.common; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public enum RcThingVersionEnum { + + V1_0_0("1.0.0", CloudSDKVersionEnum.V0_0_1); + + private final String thingVersion; + + private final CloudSDKVersionEnum cloudSDKVersion; + + RcThingVersionEnum(String thingVersion, CloudSDKVersionEnum cloudSDKVersion) { + this.thingVersion = thingVersion; + this.cloudSDKVersion = cloudSDKVersion; + } + + @JsonValue + public String getThingVersion() { + return thingVersion; + } + + public CloudSDKVersionEnum getCloudSDKVersion() { + return cloudSDKVersion; + } + + public static RcThingVersionEnum find(String thingVersion) { + return V1_0_0; + } +} diff --git a/src/main/java/com/dji/sdk/common/SDKManager.java b/src/main/java/com/dji/sdk/common/SDKManager.java new file mode 100644 index 0000000..ce07689 --- /dev/null +++ b/src/main/java/com/dji/sdk/common/SDKManager.java @@ -0,0 +1,50 @@ +package com.dji.sdk.common; + +import com.dji.sdk.cloudapi.device.DeviceDomainEnum; +import com.dji.sdk.cloudapi.device.DeviceEnum; +import com.dji.sdk.cloudapi.device.DeviceSubTypeEnum; +import com.dji.sdk.cloudapi.device.DeviceTypeEnum; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public class SDKManager { + + private SDKManager() { + } + + private static final ConcurrentHashMap SDK_MAP = new ConcurrentHashMap<>(16); + + public static GatewayManager getDeviceSDK(String gatewaySn) { + if (SDK_MAP.containsKey(gatewaySn)) { + return SDK_MAP.get(gatewaySn); + } + throw new CloudSDKException(CloudSDKErrorEnum.NOT_REGISTERED, + "The device has not been registered, please call the 'SDKManager.registerDevice()' method to register the device first."); + } + + public static GatewayManager registerDevice(String gatewaySn, String droneSn, + DeviceDomainEnum domain, DeviceTypeEnum type, DeviceSubTypeEnum subType, String gatewayThingVersion, String droneThingVersion) { + return registerDevice(gatewaySn, droneSn, GatewayTypeEnum.find(DeviceEnum.find(domain, type, subType)), gatewayThingVersion, droneThingVersion); + } + + public static GatewayManager registerDevice(String gatewaySn, String droneSn, GatewayTypeEnum type, String gatewayThingVersion, String droneThingVersion) { + return registerDevice(new GatewayManager(Objects.requireNonNull(gatewaySn), droneSn, type, gatewayThingVersion, droneThingVersion)); + } + + public static GatewayManager registerDevice(GatewayManager gateway) { + SDK_MAP.put(gateway.getGatewaySn(), gateway); + return gateway; + } + + public static void logoutDevice(String gatewaySn) { + SDK_MAP.remove(gatewaySn); + } +} diff --git a/src/main/java/com/dji/sample/common/util/SpringBeanUtils.java b/src/main/java/com/dji/sdk/common/SpringBeanUtils.java similarity index 95% rename from src/main/java/com/dji/sample/common/util/SpringBeanUtils.java rename to src/main/java/com/dji/sdk/common/SpringBeanUtils.java index 23e8966..3aa93fa 100644 --- a/src/main/java/com/dji/sample/common/util/SpringBeanUtils.java +++ b/src/main/java/com/dji/sdk/common/SpringBeanUtils.java @@ -1,4 +1,4 @@ -package com.dji.sample.common.util; +package com.dji.sdk.common; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; diff --git a/src/main/java/com/dji/sdk/config/CloudSDKHandler.java b/src/main/java/com/dji/sdk/config/CloudSDKHandler.java new file mode 100644 index 0000000..b0ef220 --- /dev/null +++ b/src/main/java/com/dji/sdk/config/CloudSDKHandler.java @@ -0,0 +1,107 @@ +package com.dji.sdk.config; + +import com.dji.sdk.annotations.CloudSDKVersion; +import com.dji.sdk.common.*; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.*; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/22 + */ +@Aspect +@Component +public class CloudSDKHandler { + + @Before("execution(public * com.dji.sdk.cloudapi.*.api.*.*(com.dji.sdk.common.GatewayManager, ..))") + public void checkCloudSDK(JoinPoint point) { + GatewayManager deviceSDK = (GatewayManager) point.getArgs()[0]; + CloudSDKVersion since = ((MethodSignature) point.getSignature()).getMethod().getDeclaredAnnotation(CloudSDKVersion.class); + if (Objects.isNull(since)) { + return; + } + if (!deviceSDK.isTypeSupport(since)) { + throw new CloudSDKException(CloudSDKErrorEnum.DEVICE_TYPE_NOT_SUPPORT); + } + if (!deviceSDK.isVersionSupport(since)) { + throw new CloudSDKException(CloudSDKErrorEnum.DEVICE_VERSION_NOT_SUPPORT); + } + } + + @Before("execution(public * com.dji.sdk.cloudapi.*.api.*.*(com.dji.sdk.common.GatewayManager, com.dji.sdk.common.BaseModel+))") + public void checkRequest(JoinPoint point) { + Common.validateModel((BaseModel) point.getArgs()[1], (GatewayManager) point.getArgs()[0]); + } + + @AfterReturning(value = "execution(public com.dji.sdk.common.HttpResultResponse+ com.dji.sdk.cloudapi.*.api.*.*(..))", returning = "response") + public void checkResponse(JoinPoint point, HttpResultResponse response) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { + if (null == response) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER, "The return value cannot be null."); + } + Method method = ((MethodSignature) point.getSignature()).getMethod(); + if (method.getGenericReturnType() instanceof Class) { + if (null == response.getData()) { + response.setData(""); + } + return; + } + checkClassType((ParameterizedType) method.getGenericReturnType(), response); + validData(response.getData(), point.getArgs()[0]); + } + + private void checkClassType(ParameterizedType type, HttpResultResponse response) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + Type actualType = type.getActualTypeArguments()[0]; + Class typeClass = actualType instanceof Class ? (Class) actualType : (Class) ((ParameterizedType) actualType).getRawType(); + if (null == response.getData()) { + if (List.class.isAssignableFrom(typeClass)) { + response.setData(Collections.emptyList()); + return; + } + response.setData(typeClass.getDeclaredConstructor().newInstance()); + return; + } + boolean isAssignableFrom = typeClass.isAssignableFrom(response.getData().getClass()); + if (!isAssignableFrom) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER); + } + } + + private void validData(Object data, Object arg) { + if (data instanceof BaseModel) { + Common.validateModel((BaseModel) data); + return; + } + if (data instanceof PaginationData) { + List list = ((PaginationData) data).getList(); + if (null == list) { + ((PaginationData) data).setList(Collections.EMPTY_LIST); + try { + Field page = arg.getClass().getDeclaredField("page"); + Field pageSize = arg.getClass().getDeclaredField("pageSize"); + page.setAccessible(true); + pageSize.setAccessible(true); + ((PaginationData) data).setPagination( + new Pagination().setPage((int) page.get(arg)).setPageSize((int) pageSize.get(arg))); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER, e.getMessage()); + } + return; + } + for (BaseModel model : list) { + Common.validateModel(model); + } + } + } +} diff --git a/src/main/java/com/dji/sdk/config/CloudSDKMvcConfigurer.java b/src/main/java/com/dji/sdk/config/CloudSDKMvcConfigurer.java new file mode 100644 index 0000000..e5771b2 --- /dev/null +++ b/src/main/java/com/dji/sdk/config/CloudSDKMvcConfigurer.java @@ -0,0 +1,16 @@ +package com.dji.sdk.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class CloudSDKMvcConfigurer implements WebMvcConfigurer { + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new GetSnakeArgumentProcessor(true)); + } +} diff --git a/src/main/java/com/dji/sample/configuration/mvc/GetSnakeArgumentProcessor.java b/src/main/java/com/dji/sdk/config/GetSnakeArgumentProcessor.java similarity index 85% rename from src/main/java/com/dji/sample/configuration/mvc/GetSnakeArgumentProcessor.java rename to src/main/java/com/dji/sdk/config/GetSnakeArgumentProcessor.java index 11d7b48..514b486 100644 --- a/src/main/java/com/dji/sample/configuration/mvc/GetSnakeArgumentProcessor.java +++ b/src/main/java/com/dji/sdk/config/GetSnakeArgumentProcessor.java @@ -1,6 +1,5 @@ -package com.dji.sample.configuration.mvc; +package com.dji.sdk.config; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor; @@ -12,9 +11,6 @@ import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttribu */ public class GetSnakeArgumentProcessor extends ServletModelAttributeMethodProcessor { - @Autowired - private GetSnakeDataBinder snakeDataBinder; - /** * Class constructor. * diff --git a/src/main/java/com/dji/sdk/config/GetSnakeDataBinder.java b/src/main/java/com/dji/sdk/config/GetSnakeDataBinder.java new file mode 100644 index 0000000..b14ab6b --- /dev/null +++ b/src/main/java/com/dji/sdk/config/GetSnakeDataBinder.java @@ -0,0 +1,110 @@ +package com.dji.sdk.config; + +import com.dji.sdk.common.Common; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.PropertyValue; +import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder; + +import javax.servlet.ServletRequest; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/9 + */ +public class GetSnakeDataBinder extends ExtendedServletRequestDataBinder { + + public GetSnakeDataBinder(Object target, String objectName) { + super(target, objectName); + } + + @Override + protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) { + List propertyValueList = mpvs.getPropertyValueList(); + List values = new ArrayList<>(propertyValueList); + Field[] fields = this.getTarget().getClass().getDeclaredFields(); + Map fieldMap = Arrays.stream(fields).collect(Collectors.toMap(Field::getName, field -> field)); + fieldMap.putAll(Arrays.stream(fields).filter(field -> null != field.getAnnotation(JsonProperty.class)) + .collect(Collectors.toMap(field -> field.getAnnotation(JsonProperty.class).value(), field -> field))); + + for (PropertyValue property : values) { + if (!fieldMap.containsKey(property.getName())) { + continue; + } + + Field field = fieldMap.get(property.getName()); + List list = (List) Objects.requireNonNullElse(property.getConvertedValue(), new ArrayList<>()); + list.addAll((List) convertValue(field, this.getTarget(), property.getValue())); + if (!list.isEmpty()) { + property.setConvertedValue(list); + } + + String fieldName = field.getName(); + if (fieldName.equals(property.getName())) { + continue; + } + if (mpvs.contains(fieldName)) { + PropertyValue propertyValue = mpvs.getPropertyValue(fieldName); + if (null != propertyValue.getConvertedValue()) { + ((List) propertyValue.getConvertedValue()).addAll((List) property.getConvertedValue()); + } + } else { + mpvs.addPropertyValue(new PropertyValue(fieldName, Objects.requireNonNullElse(property.getConvertedValue(), property.getValue()))); + } + mpvs.removePropertyValue(property); + } + + super.addBindValues(mpvs, request); + } + + private Object convertValue(Field field, Object object, Object value) { + List convertedValue = new ArrayList(); + if (Enum.class.isAssignableFrom(field.getType())) { + convertedValue.add(getRealEnumValue(field.getType(), object, value)); + } + if (field.getType() == field.getGenericType()) { + return convertedValue; + } + if (List.class.isAssignableFrom(field.getType())) { + if (!value.getClass().isArray()) { + value = String.valueOf(value).split(","); + } + for (String v : (String[]) value) { + if ("".equals(v)) { + continue; + } + convertedValue.add(getRealEnumValue((Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0], object, v)); + } + } + + return convertedValue; + } + + private Object getRealEnumValue(Class type, Object object, Object... value) { + if (!type.isEnum()) { + return value; + } + Set methods = Arrays.stream(type.getDeclaredMethods()) + .filter(m -> null != m.getAnnotation(JsonCreator.class)) + .filter(m -> m.getParameterTypes().length == value.length).collect(Collectors.toSet()); + for (Method m : methods) { + try { + Class[] parameterTypes = m.getParameterTypes(); + for (int i = 0; i < value.length; i++) { + value[i] = Common.getObjectMapper().convertValue(value[i], parameterTypes[i]); + } + return m.invoke(object, value); + } catch (IllegalAccessException | InvocationTargetException ignored) { + } + } + return value; + } +} diff --git a/src/main/java/com/dji/sdk/exception/CloudSDKErrorEnum.java b/src/main/java/com/dji/sdk/exception/CloudSDKErrorEnum.java new file mode 100644 index 0000000..acdf986 --- /dev/null +++ b/src/main/java/com/dji/sdk/exception/CloudSDKErrorEnum.java @@ -0,0 +1,49 @@ +package com.dji.sdk.exception; + +import com.dji.sdk.common.IErrorInfo; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/23 + */ +public enum CloudSDKErrorEnum implements IErrorInfo { + + NOT_REGISTERED(210001, "Device is not registered."), + + INVALID_PARAMETER(210002, "Invalid parameter."), + + DEVICE_TYPE_NOT_SUPPORT(210003, "The current type of the device does not support this function."), + + DEVICE_VERSION_NOT_SUPPORT(210004, "The current version of the device does not support this function."), + + DEVICE_PROPERTY_NOT_SUPPORT(210005, "The current device does not support this feature."), + + MQTT_PUBLISH_ABNORMAL(211001, "The sending of mqtt message is abnormal."), + + WEBSOCKET_PUBLISH_ABNORMAL(212001, "The sending of webSocket message is abnormal."), + + WRONG_DATA(220001, "Data exceeds limit."), + + UNKNOWN(299999, "sdk unknown"), + ; + + private final int code; + + private final String message; + + CloudSDKErrorEnum(int code, String message) { + this.code = code; + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public Integer getCode() { + return code; + } +} diff --git a/src/main/java/com/dji/sdk/exception/CloudSDKException.java b/src/main/java/com/dji/sdk/exception/CloudSDKException.java new file mode 100644 index 0000000..66f1357 --- /dev/null +++ b/src/main/java/com/dji/sdk/exception/CloudSDKException.java @@ -0,0 +1,48 @@ +package com.dji.sdk.exception; + +import com.dji.sdk.common.IErrorInfo; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/19 + */ +public class CloudSDKException extends RuntimeException { + + private IErrorInfo errorInfo; + + public CloudSDKException(String message) { + super(message); + this.errorInfo = CloudSDKErrorEnum.UNKNOWN; + } + + public CloudSDKException(Throwable cause) { + super(cause); + this.errorInfo = CloudSDKErrorEnum.UNKNOWN; + } + + public CloudSDKException() { + this("SDK Exception"); + this.errorInfo = CloudSDKErrorEnum.UNKNOWN; + } + + public CloudSDKException(Class clazz, Object... code) { + this(clazz.getName() + " has unknown data: " + Arrays.toString(code)); + this.errorInfo = CloudSDKErrorEnum.WRONG_DATA; + } + + public CloudSDKException(IErrorInfo err) { + this(err, null); + } + + public CloudSDKException(IErrorInfo err, String msg) { + this(String.format("Error Code: %d, Error Msg: %s. %s", err.getCode(), err.getMessage(), msg)); + this.errorInfo = err; + } + + public IErrorInfo getErrorInfo() { + return errorInfo; + } +} diff --git a/src/main/java/com/dji/sdk/exception/CloudSDKVersionException.java b/src/main/java/com/dji/sdk/exception/CloudSDKVersionException.java new file mode 100644 index 0000000..5edb22d --- /dev/null +++ b/src/main/java/com/dji/sdk/exception/CloudSDKVersionException.java @@ -0,0 +1,17 @@ +package com.dji.sdk.exception; + +import com.dji.sdk.common.CloudSDKVersionEnum; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/7 + */ +public class CloudSDKVersionException extends CloudSDKException { + + public CloudSDKVersionException(String thingVersion) { + super(String.format("The current CloudSDK version(%s) does not support this thing version(%s), " + + "please replace the corresponding CloudSDK version.)", CloudSDKVersionEnum.DEFAULT.getVersion(), thingVersion)); + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/Chan.java b/src/main/java/com/dji/sdk/mqtt/Chan.java new file mode 100644 index 0000000..88f80b7 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/Chan.java @@ -0,0 +1,59 @@ +package com.dji.sdk.mqtt; + +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +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 ConcurrentHashMap CHANNEL = new ConcurrentHashMap<>(); + + private static final int UNIT = 1000_000; + + private volatile CommonTopicResponse data; + + private volatile Thread t; + + private Chan () { + + } + + public static Chan getInstance(String tid, boolean isNeedCreate) { + if (!isNeedCreate) { + return CHANNEL.get(tid); + } + Chan chan = new Chan(); + CHANNEL.put(tid, chan); + return chan; + } + + public CommonTopicResponse get(String tid, long timeout) { + Chan chan = CHANNEL.get(tid); + if (Objects.isNull(chan)) { + return null; + } + chan.t = Thread.currentThread(); + LockSupport.parkNanos(chan.t, timeout * UNIT); + chan.t = null; + CHANNEL.remove(tid); + return chan.data; + } + + public void put(CommonTopicResponse response) { + Chan chan = CHANNEL.get(response.getTid()); + if (Objects.isNull(chan)) { + return; + } + chan.data = response; + if (chan.t == null) { + return; + } + LockSupport.unpark(chan.t); + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/ChannelName.java b/src/main/java/com/dji/sdk/mqtt/ChannelName.java new file mode 100644 index 0000000..1fb7c82 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/ChannelName.java @@ -0,0 +1,137 @@ +package com.dji.sdk.mqtt; + +/** + * The name of all channels. + * + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +public class ChannelName { + + public static final String INBOUND = "inbound"; + public static final String DEFAULT = "default"; + public static final String OUTBOUND = "outbound"; + + // status + public static final String INBOUND_STATUS = "inboundStatus"; + + public static final String OUTBOUND_STATUS = "outboundStatus"; + + public static final String INBOUND_STATUS_ONLINE = "inboundStatusOnline"; + + public static final String INBOUND_STATUS_OFFLINE = "inboundStatusOffline"; + + + // state + public static final String INBOUND_STATE = "inboundState"; + + public static final String INBOUND_STATE_RC_CONTROL_SOURCE = "inboundStateRcControlSource"; + + public static final String INBOUND_STATE_DOCK_CONTROL_SOURCE = "inboundStateDockControlSource"; + + public static final String INBOUND_STATE_RC_LIVESTREAM_ABILITY_UPDATE = "inboundStateRcLiveCapacity"; + + public static final String INBOUND_STATE_DOCK_LIVESTREAM_ABILITY_UPDATE = "inboundStateDockLiveCapacity"; + + public static final String INBOUND_STATE_RC_LIVE_STATUS = "inboundStateRcLiveStatus"; + + public static final String INBOUND_STATE_DOCK_LIVE_STATUS = "inboundStateDockLiveStatus"; + + public static final String INBOUND_STATE_RC_FIRMWARE_VERSION = "inboundStateRcFirmwareVersion"; + + public static final String INBOUND_STATE_DOCK_FIRMWARE_VERSION = "inboundStateDockFirmwareVersion"; + + public static final String INBOUND_STATE_RC_PAYLOAD_FIRMWARE = "inboundStateRcPayloadFirmware"; + + public static final String INBOUND_STATE_DOCK_WPMZ_VERSION = "inboundStateDockWpmzVersion"; + + public static final String INBOUND_STATE_DOCK_PAYLOAD = "inboundStateDockPayload"; + + + // services_reply + public static final String INBOUND_SERVICES_REPLY = "inboundServicesReply"; + + + // osd + public static final String INBOUND_OSD = "inboundOsd"; + + public static final String INBOUND_OSD_RC = "inboundOsdRc"; + + public static final String INBOUND_OSD_DOCK = "inboundOsdDock"; + + public static final String INBOUND_OSD_RC_DRONE = "inboundOsdRcDrone"; + + public static final String INBOUND_OSD_DOCK_DRONE = "inboundOsdDockDrone"; + + + // requests + public static final String INBOUND_REQUESTS = "inboundRequests"; + + public static final String INBOUND_REQUESTS_STORAGE_CONFIG_GET = "inboundRequestsStorageConfigGet"; + + 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_REQUESTS_CONFIG = "inboundRequestsConfig"; + + public static final String INBOUND_REQUESTS_FLIGHTTASK_RESOURCE_GET = "inboundEventsFlightTaskResourceGet"; + + public static final String OUTBOUND_REQUESTS = "outboundRequests"; + + + // events + public static final String INBOUND_EVENTS = "inboundEvents"; + + public static final String OUTBOUND_EVENTS = "outboundEvents"; + + public static final String INBOUND_EVENTS_DEVICE_EXIT_HOMING_NOTIFY = "inboundEventsDeviceExitHomingNotify"; + + public static final String INBOUND_EVENTS_FLIGHTTASK_PROGRESS = "inboundEventsFlighttaskProgress"; + + public static final String INBOUND_EVENTS_FLIGHTTASK_READY = "inboundEventsFlighttaskReady"; + + public static final String INBOUND_EVENTS_FILE_UPLOAD_CALLBACK = "inboundEventsFileUploadCallback"; + + public static final String INBOUND_EVENTS_HMS = "inboundEventsHms"; + + public static final String INBOUND_EVENTS_CONTROL_PROGRESS = "inboundEventsControlProgress"; + + public static final String INBOUND_EVENTS_OTA_PROGRESS = "inboundEventsOtaProgress"; + + public static final String INBOUND_EVENTS_FILEUPLOAD_PROGRESS = "inboundEventsFileUploadProgress"; + + public static final String INBOUND_EVENTS_FLY_TO_POINT_PROGRESS = "inboundEventsFlyToPointProgress"; + + public static final String INBOUND_EVENTS_TAKEOFF_TO_POINT_PROGRESS = "inboundEventsTakeoffToPointProgress"; + + public static final String INBOUND_EVENTS_DRC_STATUS_NOTIFY = "inboundEventsDrcStatusNotify"; + + public static final String INBOUND_EVENTS_JOYSTICK_INVALID_NOTIFY = "inboundEventsJoystickInvalidNotify"; + + public static final String INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA = "inboundEventsHighestPriorityUploadFlightTaskMedia"; + + + // property + public static final String INBOUND_PROPERTY_SET_REPLY = "inboundPropertySetReply"; + + + // drc/up + public static final String INBOUND_DRC_UP = "inboundDrcUp"; + + public static final String INBOUND_DRC_UP_DRONE_CONTROL = "inboundDrcUpDroneControl"; + + public static final String INBOUND_DRC_UP_DRONE_EMERGENCY_STOP = "inboundDrcUpDroneEmergencyStop"; + + public static final String INBOUND_DRC_UP_HEART_BEAT = "inboundDrcUpHeartBeat"; + + public static final String INBOUND_DRC_UP_HSI_INFO_PUSH = "inboundDrcUpHsiInfoPush"; + + public static final String INBOUND_DRC_UP_DELAY_INFO_PUSH = "inboundDrcUpDelayInfoPush"; + + public static final String INBOUND_DRC_UP_OSD_INFO_PUSH = "inboundDrcUpOsdInfoPush"; + +} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java b/src/main/java/com/dji/sdk/mqtt/CloudApiTopicEnum.java similarity index 62% rename from src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java rename to src/main/java/com/dji/sdk/mqtt/CloudApiTopicEnum.java index 8598905..4ab6f4c 100644 --- a/src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java +++ b/src/main/java/com/dji/sdk/mqtt/CloudApiTopicEnum.java @@ -1,25 +1,22 @@ -package com.dji.sample.component.mqtt.model; - -import lombok.Getter; +package com.dji.sdk.mqtt; import java.util.Arrays; import java.util.regex.Pattern; -import static com.dji.sample.component.mqtt.model.TopicConst.*; +import static com.dji.sdk.mqtt.TopicConst.*; /** * @author sean * @version 1.3 * @date 2022/10/28 */ -@Getter -public enum DeviceTopicEnum { +public enum CloudApiTopicEnum { STATUS(Pattern.compile("^" + BASIC_PRE + PRODUCT + REGEX_SN + STATUS_SUF + "$"), ChannelName.INBOUND_STATUS), STATE(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + STATE_SUF + "$"), ChannelName.INBOUND_STATE), - SERVICE_REPLY(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + SERVICES_SUF + _REPLY_SUF + "$"), ChannelName.INBOUND_SERVICE_REPLY), + SERVICE_REPLY(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + SERVICES_SUF + _REPLY_SUF + "$"), ChannelName.INBOUND_SERVICES_REPLY), OSD(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + OSD_SUF + "$"), ChannelName.INBOUND_OSD), @@ -29,18 +26,28 @@ public enum DeviceTopicEnum { PROPERTY_SET_REPLY(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + PROPERTY_SUF + SET_SUF + _REPLY_SUF + "$"), ChannelName.INBOUND_PROPERTY_SET_REPLY), + DRC_UP(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + DRC + UP + "$"), ChannelName.INBOUND_DRC_UP), + UNKNOWN(Pattern.compile("^.*$"), ChannelName.DEFAULT); - Pattern pattern; + private final Pattern pattern; - String beanName; + private final String beanName; - DeviceTopicEnum(Pattern pattern, String beanName) { + CloudApiTopicEnum(Pattern pattern, String beanName) { this.pattern = pattern; this.beanName = beanName; } - public static DeviceTopicEnum find(String topic) { - return Arrays.stream(DeviceTopicEnum.values()).filter(topicEnum -> topicEnum.pattern.matcher(topic).matches()).findAny().orElse(UNKNOWN); + public Pattern getPattern() { + return pattern; + } + + public String getBeanName() { + return beanName; + } + + public static CloudApiTopicEnum find(String topic) { + return Arrays.stream(CloudApiTopicEnum.values()).filter(topicEnum -> topicEnum.pattern.matcher(topic).matches()).findAny().orElse(UNKNOWN); } } diff --git a/src/main/java/com/dji/sdk/mqtt/CommonTopicRequest.java b/src/main/java/com/dji/sdk/mqtt/CommonTopicRequest.java new file mode 100644 index 0000000..049394e --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/CommonTopicRequest.java @@ -0,0 +1,71 @@ +package com.dji.sdk.mqtt; + +/** + * Unified topic request format. + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +public class CommonTopicRequest { + + /** + * The command is sent and the response is matched by the tid and bid fields in the message, + * and the reply should keep the tid and bid the same. + */ + protected String tid; + + protected String bid; + + protected Long timestamp; + + protected T data; + + public CommonTopicRequest() { + } + + @Override + public String toString() { + return "CommonTopicRequest{" + + "tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + + public String getTid() { + return tid; + } + + public CommonTopicRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public CommonTopicRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public CommonTopicRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public CommonTopicRequest setData(T data) { + this.data = data; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/CommonTopicResponse.java b/src/main/java/com/dji/sdk/mqtt/CommonTopicResponse.java new file mode 100644 index 0000000..fcbafa0 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/CommonTopicResponse.java @@ -0,0 +1,71 @@ +package com.dji.sdk.mqtt; + +/** + * Unified Topic response format + * @author sean.zhou + * @date 2021/11/15 + * @version 0.1 + */ +public class CommonTopicResponse { + + /** + * The command is sent and the response is matched by the tid and bid fields in the message, + * and the reply should keep the tid and bid the same. + */ + protected String tid; + + protected String bid; + + protected T data; + + protected Long timestamp; + + @Override + public String toString() { + return "CommonTopicResponse{" + + "tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", data=" + data + + ", timestamp=" + timestamp + + '}'; + } + + public CommonTopicResponse() { + } + + public String getTid() { + return tid; + } + + public CommonTopicResponse setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public CommonTopicResponse setBid(String bid) { + this.bid = bid; + return this; + } + + public T getData() { + return data; + } + + public CommonTopicResponse setData(T data) { + this.data = data; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public CommonTopicResponse setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sample/component/mqtt/service/IMqttMessageGateway.java b/src/main/java/com/dji/sdk/mqtt/IMqttMessageGateway.java similarity index 89% rename from src/main/java/com/dji/sample/component/mqtt/service/IMqttMessageGateway.java rename to src/main/java/com/dji/sdk/mqtt/IMqttMessageGateway.java index cb50b12..fb3dbf2 100644 --- a/src/main/java/com/dji/sample/component/mqtt/service/IMqttMessageGateway.java +++ b/src/main/java/com/dji/sdk/mqtt/IMqttMessageGateway.java @@ -1,6 +1,5 @@ -package com.dji.sample.component.mqtt.service; +package com.dji.sdk.mqtt; -import com.dji.sample.component.mqtt.model.ChannelName; import org.springframework.integration.annotation.MessagingGateway; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.handler.annotation.Header; diff --git a/src/main/java/com/dji/sample/component/mqtt/service/IMqttTopicService.java b/src/main/java/com/dji/sdk/mqtt/IMqttTopicService.java similarity index 74% rename from src/main/java/com/dji/sample/component/mqtt/service/IMqttTopicService.java rename to src/main/java/com/dji/sdk/mqtt/IMqttTopicService.java index 6983109..d5d6027 100644 --- a/src/main/java/com/dji/sample/component/mqtt/service/IMqttTopicService.java +++ b/src/main/java/com/dji/sdk/mqtt/IMqttTopicService.java @@ -1,4 +1,4 @@ -package com.dji.sample.component.mqtt.service; +package com.dji.sdk.mqtt; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.handler.annotation.Header; @@ -13,9 +13,9 @@ public interface IMqttTopicService { /** * Subscribe to a specific topic. - * @param topic target + * @param topics target */ - void subscribe(@Header(MqttHeaders.TOPIC) String topic); + void subscribe(@Header(MqttHeaders.TOPIC) String... topics); /** * Subscribe to a specific topic using a specific qos. @@ -26,9 +26,9 @@ public interface IMqttTopicService { /** * Unsubscribe from a specific topic. - * @param topic target + * @param topics target */ - void unsubscribe(@Header(MqttHeaders.TOPIC) String topic); + void unsubscribe(@Header(MqttHeaders.TOPIC) String... topics); /** * Get all the subscribed topics. diff --git a/src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java b/src/main/java/com/dji/sdk/mqtt/InboundMessageRouter.java similarity index 75% rename from src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java rename to src/main/java/com/dji/sdk/mqtt/InboundMessageRouter.java index 8aa9112..4d2a417 100644 --- a/src/main/java/com/dji/sample/component/mqtt/handler/InboundMessageRouter.java +++ b/src/main/java/com/dji/sdk/mqtt/InboundMessageRouter.java @@ -1,9 +1,8 @@ -package com.dji.sample.component.mqtt.handler; +package com.dji.sdk.mqtt; -import com.dji.sample.common.util.SpringBeanUtils; -import com.dji.sample.component.mqtt.model.ChannelName; -import com.dji.sample.component.mqtt.model.DeviceTopicEnum; -import lombok.extern.slf4j.Slf4j; +import com.dji.sdk.common.SpringBeanUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.integration.annotation.Router; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.integration.router.AbstractMessageRouter; @@ -22,9 +21,10 @@ import java.util.Collections; * @version 0.1 */ @Component -@Slf4j public class InboundMessageRouter extends AbstractMessageRouter { + private static final Logger log = LoggerFactory.getLogger(InboundMessageRouter.class); + /** * All mqtt broker messages will arrive here before distributing them to different channels. * @param message message from mqtt broker @@ -37,9 +37,9 @@ public class InboundMessageRouter extends AbstractMessageRouter { String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); byte[] payload = (byte[])message.getPayload(); - log.debug("received topic :{} \t payload :{}", topic, new String(payload)); + log.debug("received topic: {} \t payload =>{}", topic, new String(payload)); - DeviceTopicEnum topicEnum = DeviceTopicEnum.find(topic); + CloudApiTopicEnum topicEnum = CloudApiTopicEnum.find(topic); MessageChannel bean = (MessageChannel) SpringBeanUtils.getBean(topicEnum.getBeanName()); return Collections.singleton(bean); diff --git a/src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java b/src/main/java/com/dji/sdk/mqtt/MqttConfiguration.java similarity index 58% rename from src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java rename to src/main/java/com/dji/sdk/mqtt/MqttConfiguration.java index 40dada5..5855113 100644 --- a/src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java +++ b/src/main/java/com/dji/sdk/mqtt/MqttConfiguration.java @@ -1,22 +1,22 @@ -package com.dji.sample.component.mqtt.config; +package com.dji.sdk.mqtt; -import com.dji.sample.component.mqtt.model.ChannelName; -import com.dji.sample.component.mqtt.model.MqttClientOptions; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.annotation.IntegrationComponentScan; import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.integration.endpoint.MessageProducerSupport; import org.springframework.integration.mqtt.core.MqttPahoClientFactory; import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import javax.annotation.Resource; +import java.util.UUID; /** * Client configuration for inbound messages. @@ -24,12 +24,16 @@ import javax.annotation.Resource; * @date 2021/11/10 * @version 0.1 */ -@Slf4j @Configuration @IntegrationComponentScan -public class MqttInboundConfiguration { +public class MqttConfiguration { - @Autowired + private static final Logger log = LoggerFactory.getLogger(MqttConfiguration.class); + + @Value("${cloud-sdk.mqtt.inbound-topic: }") + private String inboundTopic; + + @Resource private MqttPahoClientFactory mqttClientFactory; @Resource(name = ChannelName.INBOUND) @@ -39,12 +43,10 @@ public class MqttInboundConfiguration { * Clients of inbound message channels. * @return */ - @Bean(name = "adapter") - public MessageProducerSupport mqttInbound() { - MqttClientOptions options = MqttConfiguration.getBasicClientOptions(); + @Bean + public MqttPahoMessageDrivenChannelAdapter mqttInbound() { MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( - options.getClientId() + "_consumer_" + System.currentTimeMillis(), - mqttClientFactory, options.getInboundTopic().split(",")); + UUID.randomUUID().toString(), mqttClientFactory, inboundTopic.split(",")); DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter(); // use byte types uniformly converter.setPayloadAsBytes(true); @@ -54,6 +56,27 @@ public class MqttInboundConfiguration { return adapter; } + /** + * Clients of outbound message channels. + * @return + */ + @Bean + @ServiceActivator(inputChannel = ChannelName.OUTBOUND) + public MessageHandler mqttOutbound() { + MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler( + UUID.randomUUID().toString(), mqttClientFactory); + DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter(); + // use byte types uniformly + converter.setPayloadAsBytes(true); + + messageHandler.setAsync(true); + messageHandler.setDefaultQos(0); + messageHandler.setConverter(converter); + return messageHandler; + } + + + /** * Define a default channel to handle messages that have no effect. * @return @@ -64,8 +87,7 @@ public class MqttInboundConfiguration { return message -> { log.info("The default channel does not handle messages." + "\nTopic: " + message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC) + - "\nPayload: " + message.getPayload()); + "\nPayload: " + message.getPayload() + "\n"); }; } - } diff --git a/src/main/java/com/dji/sdk/mqtt/MqttGatewayPublish.java b/src/main/java/com/dji/sdk/mqtt/MqttGatewayPublish.java new file mode 100644 index 0000000..8d60425 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/MqttGatewayPublish.java @@ -0,0 +1,101 @@ +package com.dji.sdk.mqtt; + +import com.dji.sdk.common.Common; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.TypeMismatchException; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author sean.zhou + * @date 2021/11/16 + * @version 0.1 + */ +@Component +@Slf4j +public class MqttGatewayPublish { + + private static final int DEFAULT_QOS = 0; + public static final int DEFAULT_RETRY_COUNT = 2; + public static final int DEFAULT_RETRY_TIMEOUT = 3000; + + @Resource + private IMqttMessageGateway messageGateway; + + public void publish(String topic, int qos, CommonTopicRequest request) { + try { + log.debug("send topic: {}, payload: {}", topic, request.toString()); + byte[] payload = Common.getObjectMapper().writeValueAsBytes(request); + messageGateway.publish(topic, payload, qos); + } catch (JsonProcessingException e) { + log.error("Failed to publish the message. {}", request.toString()); + e.printStackTrace(); + } + } + + public void publish(String topic, int qos, CommonTopicResponse response) { + try { + log.debug("send topic: {}, payload: {}", topic, response.toString()); + byte[] payload = Common.getObjectMapper().writeValueAsBytes(response); + messageGateway.publish(topic, payload, qos); + } catch (JsonProcessingException e) { + log.error("Failed to publish the message. {}", response.toString()); + e.printStackTrace(); + } + } + + public void publish(String topic, CommonTopicRequest request, int publishCount) { + AtomicInteger time = new AtomicInteger(0); + while (time.getAndIncrement() < publishCount) { + this.publish(topic, DEFAULT_QOS, request); + } + } + + public void publish(String topic, CommonTopicRequest request) { + this.publish(topic, DEFAULT_QOS, request); + } + + public void publishReply(CommonTopicResponse response, MessageHeaders headers) { + this.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF, 2, response); + } + + public CommonTopicResponse publishWithReply(Class clazz, String topic, CommonTopicRequest request, int retryCount, long timeout) { + AtomicInteger time = new AtomicInteger(0); + boolean hasBid = StringUtils.hasText(request.getBid()); + request.setBid(hasBid ? request.getBid() : UUID.randomUUID().toString()); + // Retry + while (time.getAndIncrement() <= retryCount) { + this.publish(topic, request); + + // If the message is not received in 3 seconds then resend it again. + CommonTopicResponse receiver = Chan.getInstance(request.getTid(), true).get(request.getTid(), timeout); + // Need to match tid and bid. + if (Objects.nonNull(receiver) + && receiver.getTid().equals(request.getTid()) + && receiver.getBid().equals(request.getBid())) { + if (clazz.isAssignableFrom(receiver.getData().getClass())) { + return receiver; + } + throw new TypeMismatchException(receiver.getData(), clazz); + } + // It must be guaranteed that the tid and bid of each message are different. + if (!hasBid) { + request.setBid(UUID.randomUUID().toString()); + } + request.setTid(UUID.randomUUID().toString()); + } + throw new CloudSDKException(CloudSDKErrorEnum.MQTT_PUBLISH_ABNORMAL, "No message reply received."); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/MqttReply.java b/src/main/java/com/dji/sdk/mqtt/MqttReply.java new file mode 100644 index 0000000..bf97ac1 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/MqttReply.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt; + +import com.dji.sdk.common.IErrorInfo; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/13 + */ +public class MqttReply { + + public static final int CODE_ERROR = -1; + + public static final int CODE_SUCCESS = 0; + + private Integer result; + + private T output; + + private MqttReply() { + } + + @Override + public String toString() { + return "MqttReply{" + + "result=" + result + + ", output=" + output + + '}'; + } + + private MqttReply(T output) { + this.output = output; + } + + private MqttReply(Integer result, T output) { + this.result = result; + this.output = output; + } + + public static MqttReply error(IErrorInfo errorInfo) { + return new MqttReply(errorInfo.getCode(), errorInfo.getMessage()); + } + + public static MqttReply error(String message) { + return new MqttReply(CODE_ERROR, message); + } + + public static MqttReply success(T data) { + return new MqttReply(CODE_SUCCESS, data); + } + + public static MqttReply success() { + return new MqttReply().setResult(CODE_SUCCESS); + } + + public Integer getResult() { + return result; + } + + public MqttReply setResult(Integer result) { + this.result = result; + return this; + } + + public MqttReply setOutput(T output) { + this.output = output; + return this; + } + + public T getOutput() { + return output; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/MqttReplyHandler.java b/src/main/java/com/dji/sdk/mqtt/MqttReplyHandler.java new file mode 100644 index 0000000..f0e3a27 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/MqttReplyHandler.java @@ -0,0 +1,60 @@ +package com.dji.sdk.mqtt; + +import com.dji.sdk.common.BaseModel; +import com.dji.sdk.common.Common; +import com.dji.sdk.mqtt.events.TopicEventsRequest; +import com.dji.sdk.mqtt.events.TopicEventsResponse; +import com.dji.sdk.mqtt.requests.TopicRequestsRequest; +import com.dji.sdk.mqtt.requests.TopicRequestsResponse; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/22 + */ +@Aspect +@Component +public class MqttReplyHandler { + + @AfterReturning(value = "execution(public com.dji.sdk.mqtt.CommonTopicResponse+ com.dji.sdk.cloudapi.*.api.*.*(com.dji.sdk.mqtt.CommonTopicRequest+, org.springframework.messaging.MessageHeaders))", returning = "result") + public Object validateReturnValue(JoinPoint point, CommonTopicResponse result) { + if (Objects.isNull(result)) { + return null; + } + CommonTopicRequest request = (CommonTopicRequest) point.getArgs()[0]; + result.setBid(request.getBid()).setTid(request.getTid()).setTimestamp(System.currentTimeMillis()); + if (result instanceof TopicEventsResponse) { + fillEvents((TopicEventsResponse) result, (TopicEventsRequest) request); + } else if (result instanceof TopicRequestsResponse) { + validateRequests((TopicRequestsResponse) result, (TopicRequestsRequest) request); + } + return result; + } + + private void fillEvents(TopicEventsResponse response, TopicEventsRequest request) { + if (!request.isNeedReply()) { + response.setData(null); + return; + } + response.setMethod(request.getMethod()).setData(MqttReply.success()); + } + + private void validateRequests(TopicRequestsResponse response, TopicRequestsRequest request) { + response.setMethod(request.getMethod()); + Object data = response.getData(); + if (data instanceof MqttReply) { + MqttReply mqttData = (MqttReply) data; + if (MqttReply.CODE_SUCCESS != mqttData.getResult()) { + return; + } + data = mqttData.getOutput(); + } + Common.validateModel((BaseModel) data); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/MqttTopicServiceImpl.java b/src/main/java/com/dji/sdk/mqtt/MqttTopicServiceImpl.java new file mode 100644 index 0000000..158fa60 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/MqttTopicServiceImpl.java @@ -0,0 +1,57 @@ +package com.dji.sdk.mqtt; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +@Component +public class MqttTopicServiceImpl implements IMqttTopicService { + + private static final Logger log = LoggerFactory.getLogger(MqttTopicServiceImpl.class); + + @Resource + private MqttPahoMessageDrivenChannelAdapter adapter; + + @Override + public void subscribe(String... topics) { + Set topicSet = new HashSet<>(Arrays.asList(getSubscribedTopic())); + for (String topic : topics) { + if (topicSet.contains(topic)) { + return; + } + subscribe(topic, 1); + } + } + + @Override + public void subscribe(String topic, int qos) { + Set topicSet = new HashSet<>(Arrays.asList(getSubscribedTopic())); + if (topicSet.contains(topic)) { + return; + } + log.debug("subscribe topic: {}", topic); + adapter.addTopic(topic, qos); + } + + @Override + public void unsubscribe(String... topics) { + log.debug("unsubscribe topic: {}", Arrays.toString(topics)); + adapter.removeTopic(topics); + } + + public String[] getSubscribedTopic() { + return adapter.getTopic(); + } +} diff --git a/src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java b/src/main/java/com/dji/sdk/mqtt/TopicConst.java similarity index 95% rename from src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java rename to src/main/java/com/dji/sdk/mqtt/TopicConst.java index 9ee3f9a..8b957de 100644 --- a/src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java +++ b/src/main/java/com/dji/sdk/mqtt/TopicConst.java @@ -1,4 +1,4 @@ -package com.dji.sample.component.mqtt.model; +package com.dji.sdk.mqtt; /** * All the topics that need to be used in the project. diff --git a/src/main/java/com/dji/sdk/mqtt/drc/DrcDownPublish.java b/src/main/java/com/dji/sdk/mqtt/drc/DrcDownPublish.java new file mode 100644 index 0000000..25d643b --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/drc/DrcDownPublish.java @@ -0,0 +1,40 @@ +package com.dji.sdk.mqtt.drc; + +import com.dji.sdk.mqtt.MqttGatewayPublish; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Objects; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +@Component +public class DrcDownPublish { + + @Resource + private MqttGatewayPublish gatewayPublish; + + public static final int DEFAULT_PUBLISH_COUNT = 5; + + public void publish(String sn, String method) { + this.publish(sn, method, null); + } + + public void publish(String sn, String method, Object data) { + this.publish(sn, method, data, DEFAULT_PUBLISH_COUNT); + } + + public void publish(String sn, String method, Object data, int publishCount) { + String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + Objects.requireNonNull(sn) + TopicConst.DRC + TopicConst.DOWN; + gatewayPublish.publish(topic, + new TopicDrcRequest<>() + .setMethod(method) + .setData(Objects.requireNonNullElse(data, "")), + publishCount); + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/drc/DrcUpData.java b/src/main/java/com/dji/sdk/mqtt/drc/DrcUpData.java new file mode 100644 index 0000000..ac64f80 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/drc/DrcUpData.java @@ -0,0 +1,44 @@ +package com.dji.sdk.mqtt.drc; + +import com.dji.sdk.cloudapi.wayline.WaylineErrorCodeEnum; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/22 + */ +public class DrcUpData { + + private WaylineErrorCodeEnum result; + + private T output; + + public DrcUpData() { + } + + @Override + public String toString() { + return "DrcUpData{" + + "result=" + result + + ", output=" + output + + '}'; + } + + public WaylineErrorCodeEnum getResult() { + return result; + } + + public DrcUpData setResult(WaylineErrorCodeEnum result) { + this.result = result; + return this; + } + + public T getOutput() { + return output; + } + + public DrcUpData setOutput(T output) { + this.output = output; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/drc/DrcUpMethodEnum.java b/src/main/java/com/dji/sdk/mqtt/drc/DrcUpMethodEnum.java new file mode 100644 index 0000000..979f600 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/drc/DrcUpMethodEnum.java @@ -0,0 +1,59 @@ +package com.dji.sdk.mqtt.drc; + +import com.dji.sdk.cloudapi.control.*; +import com.dji.sdk.mqtt.ChannelName; +import com.fasterxml.jackson.core.type.TypeReference; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +public enum DrcUpMethodEnum { + + DRONE_CONTROL("drone_control", ChannelName.INBOUND_DRC_UP_DRONE_CONTROL, new TypeReference>() {}), + + DRONE_EMERGENCY_STOP("drone_emergency_stop", ChannelName.INBOUND_DRC_UP_DRONE_EMERGENCY_STOP, new TypeReference() {}), + + HEART_BEAT("heart_beat", ChannelName.INBOUND_DRC_UP_HEART_BEAT, new TypeReference() {}), + + HSI_INFO_PUSH("hsi_info_push", ChannelName.INBOUND_DRC_UP_HSI_INFO_PUSH, new TypeReference() {}), + + DELAY_INFO_PUSH("delay_info_push", ChannelName.INBOUND_DRC_UP_DELAY_INFO_PUSH, new TypeReference() {}), + + OSD_INFO_PUSH("osd_info_push", ChannelName.INBOUND_DRC_UP_OSD_INFO_PUSH, new TypeReference() {}), + + UNKNOWN("", ChannelName.DEFAULT, new TypeReference<>() {}); + + private final String method; + + private final String channelName; + + private final TypeReference classType; + + DrcUpMethodEnum(String method, String channelName, TypeReference classType) { + this.method = method; + this.channelName = channelName; + this.classType = classType; + } + + public String getMethod() { + return method; + } + + public String getChannelName() { + return channelName; + } + + public TypeReference getClassType() { + return classType; + } + + public static DrcUpMethodEnum find(String method) { + return Arrays.stream(DrcUpMethodEnum.values()) + .filter(methodEnum -> methodEnum.method.equals(method)) + .findAny().orElse(UNKNOWN); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/drc/DrcUpRouter.java b/src/main/java/com/dji/sdk/mqtt/drc/DrcUpRouter.java new file mode 100644 index 0000000..8648bf8 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/drc/DrcUpRouter.java @@ -0,0 +1,41 @@ +package com.dji.sdk.mqtt.drc; + +import com.dji.sdk.common.Common; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +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 org.springframework.messaging.Message; + +import java.io.IOException; +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Configuration +public class DrcUpRouter { + + @Bean + public IntegrationFlow drcUpRouterFlow() { + return IntegrationFlows + .from(ChannelName.INBOUND_DRC_UP) + .transform(Message.class, source -> { + try { + TopicDrcRequest data = Common.getObjectMapper().readValue((byte[]) source.getPayload(), TopicDrcRequest.class); + return data.setData(Common.getObjectMapper().convertValue(data.getData(), DrcUpMethodEnum.find(data.getMethod()).getClassType())); + } catch (IOException e) { + throw new CloudSDKException(e); + } + }, null) + .route( + response -> DrcUpMethodEnum.find(response.getMethod()), + mapping -> Arrays.stream(DrcUpMethodEnum.values()).forEach( + methodEnum -> mapping.channelMapping(methodEnum, methodEnum.getChannelName()))) + .get(); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/drc/DrcUpSubscribe.java b/src/main/java/com/dji/sdk/mqtt/drc/DrcUpSubscribe.java new file mode 100644 index 0000000..cbb85ba --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/drc/DrcUpSubscribe.java @@ -0,0 +1,26 @@ +package com.dji.sdk.mqtt.drc; + +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +@Component +public class DrcUpSubscribe { + + @Resource + private IMqttTopicService topicService; + + public void subscribe(GatewayManager gateway) { + String drc = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.DRC + TopicConst.UP; + topicService.subscribe(String.format(drc, gateway.getGatewaySn())); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/drc/TopicDrcRequest.java b/src/main/java/com/dji/sdk/mqtt/drc/TopicDrcRequest.java new file mode 100644 index 0000000..f6f092a --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/drc/TopicDrcRequest.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt.drc; + +import com.dji.sdk.mqtt.CommonTopicRequest; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +public class TopicDrcRequest extends CommonTopicRequest { + + private String method; + + public TopicDrcRequest() { + } + + @Override + public String toString() { + return "TopicDrcRequest{" + + "method='" + method + '\'' + + ", tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + + public String getMethod() { + return method; + } + + public TopicDrcRequest setMethod(String method) { + this.method = method; + return this; + } + + public String getTid() { + return tid; + } + + public TopicDrcRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicDrcRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicDrcRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public TopicDrcRequest setData(T data) { + this.data = data; + return this; + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/drc/TopicDrcResponse.java b/src/main/java/com/dji/sdk/mqtt/drc/TopicDrcResponse.java new file mode 100644 index 0000000..2728890 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/drc/TopicDrcResponse.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt.drc; + +import com.dji.sdk.mqtt.CommonTopicResponse; + +/** + * Unified Topic request format + * @author sean.zhou + * @date 2021/11/15 + * @version 0.1 + */ +public class TopicDrcResponse extends CommonTopicResponse { + + private String method; + + @Override + public String toString() { + return "TopicDrcResponse{" + + "tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", method='" + method + '\'' + + ", data=" + data + + ", timestamp=" + timestamp + + '}'; + } + + public TopicDrcResponse() { + } + + public String getTid() { + return tid; + } + + public TopicDrcResponse setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicDrcResponse setBid(String bid) { + this.bid = bid; + return this; + } + + public String getMethod() { + return method; + } + + public TopicDrcResponse setMethod(String method) { + this.method = method; + return this; + } + + public T getData() { + return data; + } + + public TopicDrcResponse setData(T data) { + this.data = data; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicDrcResponse setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/events/EventsDataRequest.java b/src/main/java/com/dji/sdk/mqtt/events/EventsDataRequest.java new file mode 100644 index 0000000..581270c --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/events/EventsDataRequest.java @@ -0,0 +1,42 @@ +package com.dji.sdk.mqtt.events; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/9 + */ +public class EventsDataRequest { + + private EventsErrorCode result; + + private T output; + + public EventsDataRequest() { + } + + @Override + public String toString() { + return "EventsDataRequest{" + + "result=" + result + + ", output=" + output + + '}'; + } + + public EventsErrorCode getResult() { + return result; + } + + public EventsDataRequest setResult(EventsErrorCode result) { + this.result = result; + return this; + } + + public T getOutput() { + return output; + } + + public EventsDataRequest setOutput(T output) { + this.output = output; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/events/EventsErrorCode.java b/src/main/java/com/dji/sdk/mqtt/events/EventsErrorCode.java new file mode 100644 index 0000000..a969ec0 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/events/EventsErrorCode.java @@ -0,0 +1,88 @@ +package com.dji.sdk.mqtt.events; + +import com.dji.sdk.cloudapi.control.ControlErrorCodeEnum; +import com.dji.sdk.cloudapi.debug.DebugErrorCodeEnum; +import com.dji.sdk.cloudapi.firmware.FirmwareErrorCodeEnum; +import com.dji.sdk.cloudapi.log.LogErrorCodeEnum; +import com.dji.sdk.cloudapi.wayline.WaylineErrorCodeEnum; +import com.dji.sdk.common.CommonErrorEnum; +import com.dji.sdk.common.ErrorCodeSourceEnum; +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.mqtt.MqttReply; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/14 + */ +public class EventsErrorCode implements IErrorInfo { + + private static final int MOD = 100_000; + + private ErrorCodeSourceEnum source; + + private IEventsErrorCode errorCode; + + private boolean success; + + private Integer sourceCode; + + @Override + public String toString() { + return "{" + + "errorCode=" + getCode() + + ", errorMsg=" + getMessage() + + '}'; + } + + @JsonCreator + public EventsErrorCode(int code) { + this.sourceCode = code; + if (MqttReply.CODE_SUCCESS == code) { + this.success = true; + return; + } + this.source = ErrorCodeSourceEnum.find(code / MOD); + this.errorCode = DebugErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = ControlErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = LogErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = FirmwareErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = WaylineErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = CommonErrorEnum.find(code); + } + + @Override + public String getMessage() { + return errorCode.getMessage(); + } + + @JsonValue + public Integer getCode() { + return sourceCode; + } + + public boolean isSuccess() { + return success; + } + + public ErrorCodeSourceEnum getSource() { + return source; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/events/EventsMethodEnum.java b/src/main/java/com/dji/sdk/mqtt/events/EventsMethodEnum.java new file mode 100644 index 0000000..92ca2d8 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/events/EventsMethodEnum.java @@ -0,0 +1,105 @@ +package com.dji.sdk.mqtt.events; + +import com.dji.sdk.cloudapi.control.DrcStatusNotify; +import com.dji.sdk.cloudapi.control.FlyToPointProgress; +import com.dji.sdk.cloudapi.control.JoystickInvalidNotify; +import com.dji.sdk.cloudapi.control.TakeoffToPointProgress; +import com.dji.sdk.cloudapi.debug.RemoteDebugProgress; +import com.dji.sdk.cloudapi.firmware.OtaProgress; +import com.dji.sdk.cloudapi.hms.Hms; +import com.dji.sdk.cloudapi.log.FileUploadProgress; +import com.dji.sdk.cloudapi.media.FileUploadCallback; +import com.dji.sdk.cloudapi.media.HighestPriorityUploadFlightTaskMedia; +import com.dji.sdk.cloudapi.wayline.DeviceExitHomingNotify; +import com.dji.sdk.cloudapi.wayline.FlighttaskProgress; +import com.dji.sdk.cloudapi.wayline.FlighttaskReady; +import com.dji.sdk.mqtt.ChannelName; +import com.fasterxml.jackson.core.type.TypeReference; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +public enum EventsMethodEnum { + + FLIGHTTASK_PROGRESS("flighttask_progress", ChannelName.INBOUND_EVENTS_FLIGHTTASK_PROGRESS, new TypeReference>() {}), + + DEVICE_EXIT_HOMING_NOTIFY("device_exit_homing_notify", ChannelName.INBOUND_EVENTS_DEVICE_EXIT_HOMING_NOTIFY, new TypeReference() {}), + + FILE_UPLOAD_CALLBACK("file_upload_callback", ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, new TypeReference() {}), + + HMS("hms", ChannelName.INBOUND_EVENTS_HMS, new TypeReference() {}), + + DEVICE_REBOOT("device_reboot", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + DRONE_OPEN("drone_open", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + DRONE_CLOSE("drone_close", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + DRONE_FORMAT("drone_format", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + DEVICE_FORMAT("device_format", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + COVER_OPEN("cover_open", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + COVER_CLOSE("cover_close", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + PUTTER_OPEN("putter_open", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + PUTTER_CLOSE("putter_close", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + CHARGE_OPEN("charge_open", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + CHARGE_CLOSE("charge_close", ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, new TypeReference>() {}), + + OTA_PROGRESS("ota_progress", ChannelName.INBOUND_EVENTS_OTA_PROGRESS, new TypeReference>() {}), + + FILE_UPLOAD_PROGRESS("fileupload_progress", ChannelName.INBOUND_EVENTS_FILEUPLOAD_PROGRESS, new TypeReference>() {}), + + HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA("highest_priority_upload_flighttask_media", ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA, new TypeReference() {}), + + FLIGHT_TASK_READY("flighttask_ready", ChannelName.INBOUND_EVENTS_FLIGHTTASK_READY, new TypeReference() {}), + + FLY_TO_POINT_PROGRESS("fly_to_point_progress", ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS, new TypeReference() {}), + + TAKE_OFF_TO_POINT_PROGRESS("takeoff_to_point_progress", ChannelName.INBOUND_EVENTS_TAKEOFF_TO_POINT_PROGRESS, new TypeReference() {}), + + DRC_STATUS_NOTIFY("drc_status_notify", ChannelName.INBOUND_EVENTS_DRC_STATUS_NOTIFY, new TypeReference() {}), + + JOYSTICK_INVALID_NOTIFY("joystick_invalid_notify", ChannelName.INBOUND_EVENTS_JOYSTICK_INVALID_NOTIFY, new TypeReference() {}), + + UNKNOWN("", ChannelName.DEFAULT, new TypeReference<>() {}); + + private final String method; + + private final String channelName; + + private final TypeReference classType; + + EventsMethodEnum(String method, String channelName, TypeReference classType) { + this.method = method; + this.channelName = channelName; + this.classType = classType; + } + + public String getMethod() { + return method; + } + + public String getChannelName() { + return channelName; + } + + public TypeReference getClassType() { + return classType; + } + + 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/sdk/mqtt/events/EventsRouter.java b/src/main/java/com/dji/sdk/mqtt/events/EventsRouter.java new file mode 100644 index 0000000..0a6d50d --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/events/EventsRouter.java @@ -0,0 +1,71 @@ +package com.dji.sdk.mqtt.events; + +import com.dji.sdk.common.Common; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttGatewayPublish; +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 org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; + +import static com.dji.sdk.mqtt.TopicConst.*; + +/** + * @author sean + * @version 1.1 + * @date 2022/6/1 + */ +@Configuration +public class EventsRouter { + + @Resource + private MqttGatewayPublish gatewayPublish; + + @Bean + public IntegrationFlow eventsMethodRouterFlow() { + return IntegrationFlows + .from(ChannelName.INBOUND_EVENTS) + .transform(Message.class, source -> { + try { + TopicEventsRequest data = Common.getObjectMapper().readValue((byte[]) source.getPayload(), TopicEventsRequest.class); + String topic = String.valueOf(source.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); + return data.setFrom(topic.substring((THING_MODEL_PRE + PRODUCT).length(), topic.indexOf(EVENTS_SUF))) + .setData(Common.getObjectMapper().convertValue(data.getData(), EventsMethodEnum.find(data.getMethod()).getClassType())); + } catch (IOException e) { + throw new CloudSDKException(e); + } + }, null) + .route( + response -> EventsMethodEnum.find(response.getMethod()), + mapping -> Arrays.stream(EventsMethodEnum.values()).forEach( + methodEnum -> mapping.channelMapping(methodEnum, methodEnum.getChannelName()))) + .get(); + } + + @Bean + public IntegrationFlow replySuccessEvents() { + return IntegrationFlows + .from(ChannelName.OUTBOUND_EVENTS) + .handle(this::publish) + .nullChannel(); + + } + + private TopicEventsResponse publish(TopicEventsResponse request, MessageHeaders headers) { + if (Objects.isNull(request) || Objects.isNull(request.getData())) { + return null; + } + gatewayPublish.publishReply(request, headers); + return request; + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/events/EventsSubscribe.java b/src/main/java/com/dji/sdk/mqtt/events/EventsSubscribe.java new file mode 100644 index 0000000..84cffff --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/events/EventsSubscribe.java @@ -0,0 +1,41 @@ +package com.dji.sdk.mqtt.events; + +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +@Component +public class EventsSubscribe { + + public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.EVENTS_SUF; + + @Resource + private IMqttTopicService topicService; + + public void subscribe(GatewayManager gateway, boolean unsubscribeSubDevice) { + topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); + if (unsubscribeSubDevice) { + topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); + return; + } + if (null != gateway.getDroneSn()) { + topicService.subscribe(String.format(TOPIC, gateway.getDroneSn())); + } + } + + public void unsubscribe(GatewayManager gateway) { + topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); + if (null != gateway.getDroneSn()) { + topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); + } + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/events/IEventsErrorCode.java b/src/main/java/com/dji/sdk/mqtt/events/IEventsErrorCode.java new file mode 100644 index 0000000..9377743 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/events/IEventsErrorCode.java @@ -0,0 +1,22 @@ +package com.dji.sdk.mqtt.events; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public interface IEventsErrorCode { + + /** + * Get error message. + * @return error message + */ + String getMessage(); + + /** + * Get error code. + * @return error code + */ + Integer getCode(); + +} diff --git a/src/main/java/com/dji/sdk/mqtt/events/TopicEventsRequest.java b/src/main/java/com/dji/sdk/mqtt/events/TopicEventsRequest.java new file mode 100644 index 0000000..d519527 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/events/TopicEventsRequest.java @@ -0,0 +1,108 @@ +package com.dji.sdk.mqtt.events; + +import com.dji.sdk.mqtt.CommonTopicRequest; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +public class TopicEventsRequest extends CommonTopicRequest { + + private String method; + + private String gateway; + + private String from; + + private boolean needReply; + + public TopicEventsRequest() { + } + + @Override + public String toString() { + return "TopicRequestsRequest{" + + "method='" + method + '\'' + + ", gateway='" + gateway + '\'' + + ", from='" + from + '\'' + + ", needReply=" + needReply + + ", tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + + public String getTid() { + return tid; + } + + public TopicEventsRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicEventsRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicEventsRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public TopicEventsRequest setData(T data) { + this.data = data; + return this; + } + + public String getGateway() { + return gateway; + } + + public TopicEventsRequest setGateway(String gateway) { + this.gateway = gateway; + return this; + } + + public String getFrom() { + return from; + } + + public TopicEventsRequest setFrom(String from) { + this.from = from; + return this; + } + + public boolean isNeedReply() { + return needReply; + } + + public TopicEventsRequest setNeedReply(boolean needReply) { + this.needReply = needReply; + return this; + } + + public String getMethod() { + return method; + } + + public TopicEventsRequest setMethod(String method) { + this.method = method; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/events/TopicEventsResponse.java b/src/main/java/com/dji/sdk/mqtt/events/TopicEventsResponse.java new file mode 100644 index 0000000..682d0cf --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/events/TopicEventsResponse.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt.events; + +import com.dji.sdk.mqtt.CommonTopicResponse; + +/** + * Unified Topic request format + * @author sean.zhou + * @date 2021/11/15 + * @version 0.1 + */ +public class TopicEventsResponse extends CommonTopicResponse { + + private String method; + + @Override + public String toString() { + return "TopicEventsResponse{" + + "tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", method='" + method + '\'' + + ", data=" + data + + ", timestamp=" + timestamp + + '}'; + } + + public TopicEventsResponse() { + } + + public String getTid() { + return tid; + } + + public TopicEventsResponse setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicEventsResponse setBid(String bid) { + this.bid = bid; + return this; + } + + public String getMethod() { + return method; + } + + public TopicEventsResponse setMethod(String method) { + this.method = method; + return this; + } + + public T getData() { + return data; + } + + public TopicEventsResponse setData(T data) { + this.data = data; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicEventsResponse setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/osd/OsdDeviceTypeEnum.java b/src/main/java/com/dji/sdk/mqtt/osd/OsdDeviceTypeEnum.java new file mode 100644 index 0000000..bc5c8cc --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/osd/OsdDeviceTypeEnum.java @@ -0,0 +1,68 @@ +package com.dji.sdk.mqtt.osd; + +import com.dji.sdk.cloudapi.device.OsdDock; +import com.dji.sdk.cloudapi.device.OsdDockDrone; +import com.dji.sdk.cloudapi.device.OsdRcDrone; +import com.dji.sdk.cloudapi.device.OsdRemoteControl; +import com.dji.sdk.common.GatewayTypeEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/29 + */ +public enum OsdDeviceTypeEnum { + + RC(true, GatewayTypeEnum.RC, OsdRemoteControl.class, ChannelName.INBOUND_OSD_RC), + + DOCK(true, GatewayTypeEnum.DOCK, OsdDock.class, ChannelName.INBOUND_OSD_DOCK), + + RC_DRONE(false, GatewayTypeEnum.RC, OsdRcDrone.class, ChannelName.INBOUND_OSD_RC_DRONE), + + DOCK_DRONE(false, GatewayTypeEnum.DOCK, OsdDockDrone.class, ChannelName.INBOUND_OSD_DOCK_DRONE); + + private final boolean gateway; + + private final GatewayTypeEnum gatewayType; + + private final Class classType; + + private final String channelName; + + OsdDeviceTypeEnum(boolean gateway, GatewayTypeEnum gatewayType, Class classType, String channelName) { + this.gateway = gateway; + this.gatewayType = gatewayType; + this.classType = classType; + this.channelName = channelName; + } + + public GatewayTypeEnum getGatewayType() { + return gatewayType; + } + + public boolean isGateway() { + return gateway; + } + + public Class getClassType() { + return classType; + } + + public String getChannelName() { + return channelName; + } + + public static OsdDeviceTypeEnum find(GatewayTypeEnum gatewayType, boolean isGateway) { + return Arrays.stream(values()).filter(osdEnum -> osdEnum.gatewayType == gatewayType && osdEnum.gateway == isGateway).findAny() + .orElseThrow(() -> new CloudSDKException(OsdDeviceTypeEnum.class, gatewayType, isGateway)); + } + + public static OsdDeviceTypeEnum find(Class classType) { + return Arrays.stream(values()).filter(type -> type.classType == classType).findAny() + .orElseThrow(() -> new CloudSDKException(OsdDeviceTypeEnum.class, classType)); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/osd/OsdRouter.java b/src/main/java/com/dji/sdk/mqtt/osd/OsdRouter.java new file mode 100644 index 0000000..87f5282 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/osd/OsdRouter.java @@ -0,0 +1,64 @@ +package com.dji.sdk.mqtt.osd; + +import com.dji.sdk.cloudapi.device.PayloadModelEnum; +import com.dji.sdk.common.Common; +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +import com.fasterxml.jackson.core.type.TypeReference; +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 org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.Message; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static com.dji.sdk.mqtt.TopicConst.*; + +/** + * + * @author sean.zhou + * @date 2021/11/17 + * @version 0.1 + */ +@Configuration +public class OsdRouter { + + @Bean + public IntegrationFlow osdRouterFlow() { + return IntegrationFlows + .from(ChannelName.INBOUND_OSD) + .transform(Message.class, source -> { + try { + TopicOsdRequest response = Common.getObjectMapper().readValue((byte[]) source.getPayload(), new TypeReference() {}); + String topic = String.valueOf(source.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); + return response.setFrom(topic.substring((THING_MODEL_PRE + PRODUCT).length(), topic.indexOf(OSD_SUF))); + } catch (IOException e) { + throw new CloudSDKException(e); + } + }, null) + .handle((response, headers) -> { + GatewayManager gateway = SDKManager.getDeviceSDK(response.getGateway()); + OsdDeviceTypeEnum typeEnum = OsdDeviceTypeEnum.find(gateway.getType(), response.getFrom().equals(response.getGateway())); + Map data = (Map) response.getData(); + if (!typeEnum.isGateway()) { + List payloadData = (List) data.getOrDefault(PayloadModelEnum.PAYLOAD_KEY, new ArrayList<>()); + PayloadModelEnum.getAllIndexWithPosition().stream().filter(data::containsKey) + .map(data::get).forEach(payloadData::add); + data.put(PayloadModelEnum.PAYLOAD_KEY, payloadData); + } + return response.setData(Common.getObjectMapper().convertValue(data, typeEnum.getClassType())); + }) + .route(response -> OsdDeviceTypeEnum.find(response.getData().getClass()), + mapping -> Arrays.stream(OsdDeviceTypeEnum.values()).forEach(key -> mapping.channelMapping(key, key.getChannelName()))) + .get(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/osd/OsdSubscribe.java b/src/main/java/com/dji/sdk/mqtt/osd/OsdSubscribe.java new file mode 100644 index 0000000..bc359b2 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/osd/OsdSubscribe.java @@ -0,0 +1,44 @@ +package com.dji.sdk.mqtt.osd; + +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +@Component +public class OsdSubscribe { + + public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.OSD_SUF; + + @Resource + private IMqttTopicService topicService; + + public void subscribe(GatewayManager gateway, boolean unsubscribeSubDevice) { + SDKManager.registerDevice(gateway); + topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); + if (unsubscribeSubDevice) { + topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); + return; + } + if (null != gateway.getDroneSn()) { + topicService.subscribe(String.format(TOPIC, gateway.getDroneSn())); + } + } + + public void unsubscribe(GatewayManager gateway) { + SDKManager.logoutDevice(gateway.getGatewaySn()); + topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); + if (null != gateway.getDroneSn()) { + topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); + } + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/osd/TopicOsdRequest.java b/src/main/java/com/dji/sdk/mqtt/osd/TopicOsdRequest.java new file mode 100644 index 0000000..be4df6e --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/osd/TopicOsdRequest.java @@ -0,0 +1,83 @@ +package com.dji.sdk.mqtt.osd; + +import com.dji.sdk.mqtt.CommonTopicRequest; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +public class TopicOsdRequest extends CommonTopicRequest { + + private String gateway; + + private String from; + + public TopicOsdRequest() { + } + + @Override + public String toString() { + return "TopicOsdRequest{" + + "gateway='" + gateway + '\'' + + ", from='" + from + '\'' + + ", tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + public String getTid() { + return tid; + } + + public TopicOsdRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicOsdRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicOsdRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public TopicOsdRequest setData(T data) { + this.data = data; + return this; + } + + public String getGateway() { + return gateway; + } + + public TopicOsdRequest setGateway(String gateway) { + this.gateway = gateway; + return this; + } + + public String getFrom() { + return from; + } + + public TopicOsdRequest setFrom(String from) { + this.from = from; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/property/PropertySetPublish.java b/src/main/java/com/dji/sdk/mqtt/property/PropertySetPublish.java new file mode 100644 index 0000000..3426a85 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/property/PropertySetPublish.java @@ -0,0 +1,40 @@ +package com.dji.sdk.mqtt.property; + +import com.dji.sdk.mqtt.MqttGatewayPublish; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Objects; +import java.util.UUID; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +@Component +public class PropertySetPublish { + + @Resource + private MqttGatewayPublish gatewayPublish; + + public PropertySetReplyResultEnum publish(String sn, Object data) { + return this.publish(sn, data, MqttGatewayPublish.DEFAULT_RETRY_COUNT); + } + + public PropertySetReplyResultEnum publish(String sn, Object data, int retryCount) { + return this.publish(sn, data, retryCount, MqttGatewayPublish.DEFAULT_RETRY_TIMEOUT); + } + + public PropertySetReplyResultEnum publish(String sn, Object data, int retryCount, long timeout) { + String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + Objects.requireNonNull(sn) + TopicConst.PROPERTY_SUF + TopicConst.SET_SUF; + return gatewayPublish.publishWithReply( + PropertySetReplyResultEnum.class, topic, new TopicPropertySetRequest<>() + .setTid(UUID.randomUUID().toString()) + .setBid(null) + .setTimestamp(System.currentTimeMillis()) + .setData(Objects.requireNonNull(data)), retryCount, timeout).getData(); + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyHandler.java b/src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyHandler.java new file mode 100644 index 0000000..1723e22 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyHandler.java @@ -0,0 +1,44 @@ +package com.dji.sdk.mqtt.property; + +import com.dji.sdk.common.Common; +import com.dji.sdk.mqtt.Chan; +import com.dji.sdk.mqtt.ChannelName; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Objects; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/9 + */ +@Component +public class PropertySetReplyHandler { + + private static final String RESULT_KEY = "result"; + + /** + * Handle the reply message from topic "/property/set_reply". + * @param message reply message + * @throws IOException + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_PROPERTY_SET_REPLY) + public void propertySetReply(Message message) throws IOException { + byte[] payload = (byte[])message.getPayload(); + + TopicPropertySetResponse receiver = Common.getObjectMapper().readValue(payload, new TypeReference() {}); + Chan chan = Chan.getInstance(receiver.getTid(), false); + if (Objects.isNull(chan)) { + return; + } + receiver.setData(PropertySetReplyResultEnum.find( + Common.getObjectMapper().convertValue(receiver.getData(), JsonNode.class).findValue(RESULT_KEY).intValue())); + // Put the message to the chan object. + chan.put(receiver); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyResultEnum.java b/src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyResultEnum.java new file mode 100644 index 0000000..8a17339 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyResultEnum.java @@ -0,0 +1,38 @@ +package com.dji.sdk.mqtt.property; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.3 + * @date 2022/10/28 + */ +public enum PropertySetReplyResultEnum { + + SUCCESS(0), + + FAILED(1), + + TIMEOUT(2), + + UNKNOWN(-1); + + private final int result; + + PropertySetReplyResultEnum(int result) { + this.result = result; + } + + @JsonValue + public int getResult() { + return result; + } + + @JsonCreator + public static PropertySetReplyResultEnum find(int result) { + return Arrays.stream(values()).filter(resultEnum -> resultEnum.result == result).findAny().orElse(UNKNOWN); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/property/PropertySetSubscribe.java b/src/main/java/com/dji/sdk/mqtt/property/PropertySetSubscribe.java new file mode 100644 index 0000000..6bcd266 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/property/PropertySetSubscribe.java @@ -0,0 +1,30 @@ +package com.dji.sdk.mqtt.property; + +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +@Component +public class PropertySetSubscribe { + + public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.PROPERTY_SUF + TopicConst.SET_SUF + TopicConst._REPLY_SUF; + + @Resource + private IMqttTopicService topicService; + + public void subscribe(GatewayManager gateway) { + topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); + } + + public void unsubscribe(GatewayManager gateway) { + topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/property/TopicPropertySetRequest.java b/src/main/java/com/dji/sdk/mqtt/property/TopicPropertySetRequest.java new file mode 100644 index 0000000..30f2950 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/property/TopicPropertySetRequest.java @@ -0,0 +1,61 @@ +package com.dji.sdk.mqtt.property; + +import com.dji.sdk.mqtt.CommonTopicRequest; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +public class TopicPropertySetRequest extends CommonTopicRequest { + + public TopicPropertySetRequest() { + } + + @Override + public String toString() { + return "TopicPropertySetRequest{" + + ", tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + + public String getTid() { + return tid; + } + + public TopicPropertySetRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicPropertySetRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicPropertySetRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public TopicPropertySetRequest setData(T data) { + this.data = data; + return this; + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/property/TopicPropertySetResponse.java b/src/main/java/com/dji/sdk/mqtt/property/TopicPropertySetResponse.java new file mode 100644 index 0000000..1844cf8 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/property/TopicPropertySetResponse.java @@ -0,0 +1,61 @@ +package com.dji.sdk.mqtt.property; + +import com.dji.sdk.mqtt.CommonTopicResponse; + +/** + * Unified Topic request format + * @author sean.zhou + * @date 2021/11/15 + * @version 0.1 + */ +public class TopicPropertySetResponse extends CommonTopicResponse { + + @Override + public String toString() { + return "TopicPropertySetResponse{" + + "tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", data=" + data + + ", timestamp=" + timestamp + + '}'; + } + + public TopicPropertySetResponse() { + } + + public String getTid() { + return tid; + } + + public TopicPropertySetResponse setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicPropertySetResponse setBid(String bid) { + this.bid = bid; + return this; + } + + public T getData() { + return data; + } + + public TopicPropertySetResponse setData(T data) { + this.data = data; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicPropertySetResponse setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/requests/RequestsMethodEnum.java b/src/main/java/com/dji/sdk/mqtt/requests/RequestsMethodEnum.java new file mode 100644 index 0000000..40e79a4 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/requests/RequestsMethodEnum.java @@ -0,0 +1,63 @@ +package com.dji.sdk.mqtt.requests; + +import com.dji.sdk.cloudapi.config.RequestsConfigRequest; +import com.dji.sdk.cloudapi.media.StorageConfigGet; +import com.dji.sdk.cloudapi.organization.AirportBindStatusRequest; +import com.dji.sdk.cloudapi.organization.AirportOrganizationBindRequest; +import com.dji.sdk.cloudapi.organization.AirportOrganizationGetRequest; +import com.dji.sdk.cloudapi.wayline.FlighttaskResourceGetRequest; +import com.dji.sdk.mqtt.ChannelName; + +import java.util.Arrays; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/25 + */ +public enum RequestsMethodEnum { + + STORAGE_CONFIG_GET("storage_config_get", ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET, StorageConfigGet.class), + + AIRPORT_BIND_STATUS("airport_bind_status", ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS, AirportBindStatusRequest.class), + + AIRPORT_ORGANIZATION_BIND("airport_organization_bind", ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND, AirportOrganizationBindRequest.class), + + AIRPORT_ORGANIZATION_GET("airport_organization_get", ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET, AirportOrganizationGetRequest.class), + + FLIGHT_TASK_RESOURCE_GET("flighttask_resource_get", ChannelName.INBOUND_REQUESTS_FLIGHTTASK_RESOURCE_GET, FlighttaskResourceGetRequest.class), + + CONFIG("config", ChannelName.INBOUND_REQUESTS_CONFIG, RequestsConfigRequest.class), + + UNKNOWN("", ChannelName.DEFAULT, Object.class); + + private final String method; + + private final String channelName; + + private final Class classType; + + RequestsMethodEnum(String method, String channelName, Class classType) { + this.method = method; + this.channelName = channelName; + this.classType = classType; + } + + public String getMethod() { + return method; + } + + public String getChannelName() { + return channelName; + } + + public Class getClassType() { + return classType; + } + + 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/sdk/mqtt/requests/RequestsRouter.java b/src/main/java/com/dji/sdk/mqtt/requests/RequestsRouter.java new file mode 100644 index 0000000..045afcd --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/requests/RequestsRouter.java @@ -0,0 +1,64 @@ +package com.dji.sdk.mqtt.requests; + +import com.dji.sdk.common.Common; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttGatewayPublish; +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 org.springframework.messaging.MessageHeaders; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; + +/** + * @author sean + * @version 1.0 + * @date 2022/5/25 + */ +@Configuration +public class RequestsRouter { + + @Resource + private MqttGatewayPublish gatewayPublish; + + @Bean + public IntegrationFlow requestsMethodRouterFlow() { + return IntegrationFlows + .from(ChannelName.INBOUND_REQUESTS) + .transform(payload -> { + try { + TopicRequestsRequest response = Common.getObjectMapper().readValue(payload, TopicRequestsRequest.class); + return response.setData(Common.getObjectMapper().convertValue(response.getData(), RequestsMethodEnum.find(response.getMethod()).getClassType())); + } catch (IOException e) { + throw new CloudSDKException(e); + } + }) + .route( + receiver -> RequestsMethodEnum.find(receiver.getMethod()), + mapping -> Arrays.stream(RequestsMethodEnum.values()).forEach( + methodEnum -> mapping.channelMapping(methodEnum, methodEnum.getChannelName()))) + .get(); + } + + @Bean + public IntegrationFlow replyRequestsMethod() { + return IntegrationFlows + .from(ChannelName.OUTBOUND_REQUESTS) + .handle(this::publish) + .nullChannel(); + } + + private TopicRequestsResponse publish(TopicRequestsResponse request, MessageHeaders headers) { + if (Objects.isNull(request)) { + throw new CloudSDKException(CloudSDKErrorEnum.INVALID_PARAMETER, "The return value cannot be null."); + } + gatewayPublish.publishReply(request, headers); + return request; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/requests/RequestsSubscribe.java b/src/main/java/com/dji/sdk/mqtt/requests/RequestsSubscribe.java new file mode 100644 index 0000000..6f520fc --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/requests/RequestsSubscribe.java @@ -0,0 +1,34 @@ +package com.dji.sdk.mqtt.requests; + +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +@Component +public class RequestsSubscribe { + + public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.REQUESTS_SUF; + + @Resource + private IMqttTopicService topicService; + + public void subscribe(GatewayManager gateway) { + topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); + } + + public void unsubscribe(GatewayManager gateway) { + topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); + } + + public void subscribeWildcardsRequests() { + topicService.subscribe(TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "+" + TopicConst.REQUESTS_SUF); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/requests/TopicRequestsRequest.java b/src/main/java/com/dji/sdk/mqtt/requests/TopicRequestsRequest.java new file mode 100644 index 0000000..1cd659e --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/requests/TopicRequestsRequest.java @@ -0,0 +1,84 @@ +package com.dji.sdk.mqtt.requests; + +import com.dji.sdk.mqtt.CommonTopicRequest; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +public class TopicRequestsRequest extends CommonTopicRequest { + + private String method; + + private String gateway; + + public TopicRequestsRequest() { + } + + @Override + public String toString() { + return "TopicRequestsRequest{" + + "method='" + method + '\'' + + ", gateway='" + gateway + '\'' + + ", tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + + public String getTid() { + return tid; + } + + public TopicRequestsRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicRequestsRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicRequestsRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public TopicRequestsRequest setData(T data) { + this.data = data; + return this; + } + + public String getGateway() { + return gateway; + } + + public TopicRequestsRequest setGateway(String gateway) { + this.gateway = gateway; + return this; + } + + public String getMethod() { + return method; + } + + public TopicRequestsRequest setMethod(String method) { + this.method = method; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/requests/TopicRequestsResponse.java b/src/main/java/com/dji/sdk/mqtt/requests/TopicRequestsResponse.java new file mode 100644 index 0000000..37fd816 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/requests/TopicRequestsResponse.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt.requests; + +import com.dji.sdk.mqtt.CommonTopicResponse; + +/** + * Unified Topic request format + * @author sean.zhou + * @date 2021/11/15 + * @version 0.1 + */ +public class TopicRequestsResponse extends CommonTopicResponse { + + private String method; + + @Override + public String toString() { + return "TopicRequestsResponse{" + + "tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", method='" + method + '\'' + + ", data=" + data + + ", timestamp=" + timestamp + + '}'; + } + + public TopicRequestsResponse() { + } + + public String getTid() { + return tid; + } + + public TopicRequestsResponse setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicRequestsResponse setBid(String bid) { + this.bid = bid; + return this; + } + + public String getMethod() { + return method; + } + + public TopicRequestsResponse setMethod(String method) { + this.method = method; + return this; + } + + public T getData() { + return data; + } + + public TopicRequestsResponse setData(T data) { + this.data = data; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicRequestsResponse setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/services/IServicesErrorCode.java b/src/main/java/com/dji/sdk/mqtt/services/IServicesErrorCode.java new file mode 100644 index 0000000..810c1b7 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/IServicesErrorCode.java @@ -0,0 +1,22 @@ +package com.dji.sdk.mqtt.services; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/25 + */ +public interface IServicesErrorCode { + + /** + * Get error message. + * @return error message + */ + String getMessage(); + + /** + * Get error code. + * @return error code + */ + Integer getCode(); + +} diff --git a/src/main/java/com/dji/sdk/mqtt/services/ServicesErrorCode.java b/src/main/java/com/dji/sdk/mqtt/services/ServicesErrorCode.java new file mode 100644 index 0000000..bea7e92 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/ServicesErrorCode.java @@ -0,0 +1,93 @@ +package com.dji.sdk.mqtt.services; + +import com.dji.sdk.cloudapi.control.ControlErrorCodeEnum; +import com.dji.sdk.cloudapi.debug.DebugErrorCodeEnum; +import com.dji.sdk.cloudapi.firmware.FirmwareErrorCodeEnum; +import com.dji.sdk.cloudapi.livestream.LiveErrorCodeEnum; +import com.dji.sdk.cloudapi.log.LogErrorCodeEnum; +import com.dji.sdk.cloudapi.wayline.WaylineErrorCodeEnum; +import com.dji.sdk.common.CommonErrorEnum; +import com.dji.sdk.common.ErrorCodeSourceEnum; +import com.dji.sdk.common.IErrorInfo; +import com.dji.sdk.mqtt.MqttReply; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @author sean + * @version 1.7 + * @date 2023/7/14 + */ +public class ServicesErrorCode implements IErrorInfo { + + private static final int MOD = 100_000; + + private ErrorCodeSourceEnum source; + + private IServicesErrorCode errorCode; + + private boolean success; + + private Integer sourceCode; + + @Override + public String toString() { + return "{" + + "errorCode=" + getCode() + + ", errorMsg=" + getMessage() + + '}'; + } + + @JsonCreator + public ServicesErrorCode(int code) { + this.sourceCode = code; + if (MqttReply.CODE_SUCCESS == code) { + this.success = true; + return; + } + this.source = ErrorCodeSourceEnum.find(code / MOD); + this.errorCode = LiveErrorCodeEnum.find(code % MOD); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = DebugErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = ControlErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = LogErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = FirmwareErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = WaylineErrorCodeEnum.find(code); + if (errorCode.getCode() != -1) { + return; + } + this.errorCode = CommonErrorEnum.find(code); + } + + @Override + public String getMessage() { + return errorCode.getMessage(); + } + + @JsonValue + public Integer getCode() { + return sourceCode; + } + + public boolean isSuccess() { + return success; + } + + public ErrorCodeSourceEnum getSource() { + return source; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/services/ServicesPublish.java b/src/main/java/com/dji/sdk/mqtt/services/ServicesPublish.java new file mode 100644 index 0000000..9ca29d2 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/ServicesPublish.java @@ -0,0 +1,89 @@ +package com.dji.sdk.mqtt.services; + +import com.dji.sdk.common.Common; +import com.dji.sdk.mqtt.MqttGatewayPublish; +import com.dji.sdk.mqtt.TopicConst; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Objects; +import java.util.UUID; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +@Component +public class ServicesPublish { + + @Resource + private MqttGatewayPublish gatewayPublish; + + public TopicServicesResponse> publish(TypeReference clazz, String sn, String method) { + return this.publish(clazz, sn, method, null); + } + + public TopicServicesResponse> publish(TypeReference clazz, String sn, String method, Object data) { + return this.publish(clazz, sn, method, data, MqttGatewayPublish.DEFAULT_RETRY_COUNT); + } + + public TopicServicesResponse> publish(TypeReference clazz, String sn, String method, Object data, int retryCount) { + return this.publish(clazz, sn, method, data, retryCount, MqttGatewayPublish.DEFAULT_RETRY_TIMEOUT); + } + + public TopicServicesResponse> publish(TypeReference clazz, String sn, String method, Object data, int retryCount, long timeout) { + return this.publish(clazz, sn, method, data, null, retryCount, timeout); + } + + public TopicServicesResponse publish(String sn, String method) { + return this.publish(sn, method, null, null); + } + + public TopicServicesResponse publish(String sn, String method, Object data) { + return this.publish(sn, method, data, null); + } + + public TopicServicesResponse publish(String sn, String method, Object data, String bid) { + return this.publish(sn, method, data, bid, MqttGatewayPublish.DEFAULT_RETRY_COUNT); + } + + public TopicServicesResponse publish(String sn, String method, Object data, String bid, int retryCount) { + return this.publish(sn, method, data, bid, retryCount, MqttGatewayPublish.DEFAULT_RETRY_TIMEOUT); + } + + public TopicServicesResponse publish(String sn, String method, Object data, String bid, int retryCount, long timeout) { + return (TopicServicesResponse) this.publish(null, sn, method, data, bid, retryCount, timeout); + } + + public TopicServicesResponse> publish( + TypeReference clazz, String sn, String method, Object data, String bid, int retryCount, long timeout) { + String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + Objects.requireNonNull(sn) + TopicConst.SERVICES_SUF; + TopicServicesResponse response = (TopicServicesResponse) gatewayPublish.publishWithReply( + ServicesReplyReceiver.class, topic, new TopicServicesRequest<>() + .setTid(UUID.randomUUID().toString()) + .setBid(bid) + .setTimestamp(System.currentTimeMillis()) + .setMethod(method) + .setData(Objects.requireNonNullElse(data, "")), retryCount, timeout); + ServicesReplyReceiver replyReceiver = (ServicesReplyReceiver) response.getData(); + ServicesReplyData reply = new ServicesReplyData().setResult(replyReceiver.getResult()); + if (Objects.isNull(clazz)) { + reply.setOutput((T) Objects.requireNonNullElse( + replyReceiver.getOutput(), Objects.requireNonNullElse(replyReceiver.getInfo(), ""))); + return response.setData(reply); + } + // put together in "output" + ObjectMapper mapper = Common.getObjectMapper(); + if (Objects.nonNull(replyReceiver.getInfo())) { + reply.setOutput(mapper.convertValue(replyReceiver.getInfo(), clazz)); + } + if (Objects.nonNull(replyReceiver.getOutput())) { + reply.setOutput(mapper.convertValue(replyReceiver.getOutput(), clazz)); + } + return response.setData(reply); + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyData.java b/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyData.java new file mode 100644 index 0000000..05445af --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyData.java @@ -0,0 +1,42 @@ +package com.dji.sdk.mqtt.services; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/22 + */ +public class ServicesReplyData { + + private ServicesErrorCode result; + + private T output; + + public ServicesReplyData() { + } + + @Override + public String toString() { + return "DrcUpData{" + + "result=" + result + + ", output=" + output + + '}'; + } + + public ServicesErrorCode getResult() { + return result; + } + + public ServicesReplyData setResult(ServicesErrorCode result) { + this.result = result; + return this; + } + + public T getOutput() { + return output; + } + + public ServicesReplyData setOutput(T output) { + this.output = output; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyHandler.java b/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyHandler.java new file mode 100644 index 0000000..01ee174 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyHandler.java @@ -0,0 +1,45 @@ +package com.dji.sdk.mqtt.services; + +import com.dji.sdk.cloudapi.log.FileUploadListResponse; +import com.dji.sdk.cloudapi.log.LogMethodEnum; +import com.dji.sdk.common.Common; +import com.dji.sdk.mqtt.Chan; +import com.dji.sdk.mqtt.ChannelName; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Objects; + +/** + * @author sean + * @version 1.2 + * @date 2022/9/9 + */ +@Component +public class ServicesReplyHandler { + + /** + * Handle the reply message from topic "/services_reply". + * @param message reply message + * @throws IOException + */ + @ServiceActivator(inputChannel = ChannelName.INBOUND_SERVICES_REPLY) + public void servicesReply(Message message) throws IOException { + byte[] payload = (byte[])message.getPayload(); + + TopicServicesResponse receiver = Common.getObjectMapper() + .readValue(payload, new TypeReference>() {}); + Chan chan = Chan.getInstance(receiver.getTid(), false); + if (Objects.isNull(chan)) { + return; + } + if (LogMethodEnum.FILE_UPLOAD_LIST.getMethod().equals(receiver.getMethod())) { + receiver.getData().setOutput(Common.getObjectMapper().convertValue(receiver.getData(), + new TypeReference() {})); + } + chan.put(receiver); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyReceiver.java b/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyReceiver.java new file mode 100644 index 0000000..74fdaca --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/ServicesReplyReceiver.java @@ -0,0 +1,66 @@ +package com.dji.sdk.mqtt.services; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/22 + */ +public class ServicesReplyReceiver { + + private ServicesErrorCode result; + + private T info; + + private T output; + + private T files; + + public ServicesReplyReceiver() { + } + + @Override + public String toString() { + return "ServicesReplyReceiver{" + + "result=" + result + + ", info=" + info + + ", output=" + output + + ", files=" + files + + '}'; + } + + public ServicesErrorCode getResult() { + return result; + } + + public ServicesReplyReceiver setResult(ServicesErrorCode result) { + this.result = result; + return this; + } + + public T getInfo() { + return info; + } + + public ServicesReplyReceiver setInfo(T info) { + this.info = info; + return this; + } + + public T getOutput() { + return output; + } + + public ServicesReplyReceiver setOutput(T output) { + this.output = output; + return this; + } + + public T getFiles() { + return files; + } + + public ServicesReplyReceiver setFiles(T files) { + this.files = files; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/services/ServicesSubscribe.java b/src/main/java/com/dji/sdk/mqtt/services/ServicesSubscribe.java new file mode 100644 index 0000000..a3b92a9 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/ServicesSubscribe.java @@ -0,0 +1,30 @@ +package com.dji.sdk.mqtt.services; + +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +@Component +public class ServicesSubscribe { + + public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.SERVICES_SUF + TopicConst._REPLY_SUF; + + @Resource + private IMqttTopicService topicService; + + public void subscribe(GatewayManager gateway) { + topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); + } + + public void unsubscribe(GatewayManager gateway) { + topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/services/TopicServicesRequest.java b/src/main/java/com/dji/sdk/mqtt/services/TopicServicesRequest.java new file mode 100644 index 0000000..405d004 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/TopicServicesRequest.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt.services; + +import com.dji.sdk.mqtt.CommonTopicRequest; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +public class TopicServicesRequest extends CommonTopicRequest { + + private String method; + + public TopicServicesRequest() { + } + + @Override + public String toString() { + return "TopicServicesRequest{" + + "method='" + method + '\'' + + ", tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + + public String getMethod() { + return method; + } + + public TopicServicesRequest setMethod(String method) { + this.method = method; + return this; + } + + public String getTid() { + return tid; + } + + public TopicServicesRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicServicesRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicServicesRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public TopicServicesRequest setData(T data) { + this.data = data; + return this; + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/services/TopicServicesResponse.java b/src/main/java/com/dji/sdk/mqtt/services/TopicServicesResponse.java new file mode 100644 index 0000000..2deae35 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/services/TopicServicesResponse.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt.services; + +import com.dji.sdk.mqtt.CommonTopicResponse; + +/** + * Unified Topic request format + * @author sean.zhou + * @date 2021/11/15 + * @version 0.1 + */ +public class TopicServicesResponse extends CommonTopicResponse { + + private String method; + + @Override + public String toString() { + return "TopicServicesResponse{" + + "tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", method='" + method + '\'' + + ", data=" + data + + ", timestamp=" + timestamp + + '}'; + } + + public TopicServicesResponse() { + } + + public String getTid() { + return tid; + } + + public TopicServicesResponse setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicServicesResponse setBid(String bid) { + this.bid = bid; + return this; + } + + public String getMethod() { + return method; + } + + public TopicServicesResponse setMethod(String method) { + this.method = method; + return this; + } + + public T getData() { + return data; + } + + public TopicServicesResponse setData(T data) { + this.data = data; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicServicesResponse setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/state/DockStateDataKeyEnum.java b/src/main/java/com/dji/sdk/mqtt/state/DockStateDataKeyEnum.java new file mode 100644 index 0000000..f657eb0 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/state/DockStateDataKeyEnum.java @@ -0,0 +1,55 @@ +package com.dji.sdk.mqtt.state; + +import com.dji.sdk.cloudapi.device.*; +import com.dji.sdk.cloudapi.livestream.DockLivestreamAbilityUpdate; +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; + +/** + * + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public enum DockStateDataKeyEnum { + + FIRMWARE_VERSION(Set.of("firmware_version"), DockFirmwareVersion.class), + + LIVE_CAPACITY(Set.of("live_capacity"), DockLivestreamAbilityUpdate.class), + + CONTROL_SOURCE(Set.of("control_source"), DockDroneControlSource.class), + + LIVE_STATUS(Set.of("live_status"), DockLiveStatus.class), + + WPMZ_VERSION(Set.of("wpmz_version"), DockWpmzVersion.class), + + PAYLOAD(PayloadModelEnum.getAllIndexWithPosition(), DockPayload.class) + ; + + private final Set keys; + + private final Class classType; + + + DockStateDataKeyEnum(Set keys, Class classType) { + this.keys = keys; + this.classType = classType; + } + + public Class getClassType() { + return classType; + } + + public Set getKeys() { + return keys; + } + + public static DockStateDataKeyEnum find(Set keys) { + return Arrays.stream(values()).filter(keyEnum -> !Collections.disjoint(keys, keyEnum.keys)).findAny() + .orElseThrow(() -> new CloudSDKException(DockStateDataKeyEnum.class, keys)); + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/state/RcStateDataKeyEnum.java b/src/main/java/com/dji/sdk/mqtt/state/RcStateDataKeyEnum.java new file mode 100644 index 0000000..b0dfb1d --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/state/RcStateDataKeyEnum.java @@ -0,0 +1,53 @@ +package com.dji.sdk.mqtt.state; + +import com.dji.sdk.cloudapi.device.*; +import com.dji.sdk.cloudapi.livestream.DockLivestreamAbilityUpdate; +import com.dji.sdk.exception.CloudSDKException; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; + +/** + * + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public enum RcStateDataKeyEnum { + + FIRMWARE_VERSION(Set.of("firmware_version"), RcFirmwareVersion.class), + + LIVE_CAPACITY(Set.of("live_capacity"), DockLivestreamAbilityUpdate.class), + + CONTROL_SOURCE(Set.of("control_source"), RcDroneControlSource.class), + + LIVE_STATUS(Set.of("live_status"), RcLiveStatus.class), + + PAYLOAD_FIRMWARE(PayloadModelEnum.getAllModelWithPosition(), PayloadFirmwareVersion.class), + ; + + private final Set keys; + + private final Class classType; + + + RcStateDataKeyEnum(Set keys, Class classType) { + this.keys = keys; + this.classType = classType; + } + + public Class getClassType() { + return classType; + } + + public Set getKeys() { + return keys; + } + + public static RcStateDataKeyEnum find(Set keys) { + return Arrays.stream(values()).filter(keyEnum -> !Collections.disjoint(keys, keyEnum.keys)).findAny() + .orElseThrow(() -> new CloudSDKException(RcStateDataKeyEnum.class, keys)); + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/state/StateDataKeyEnum.java b/src/main/java/com/dji/sdk/mqtt/state/StateDataKeyEnum.java new file mode 100644 index 0000000..12fcdff --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/state/StateDataKeyEnum.java @@ -0,0 +1,64 @@ +package com.dji.sdk.mqtt.state; + +import com.dji.sdk.cloudapi.device.*; +import com.dji.sdk.cloudapi.livestream.DockLivestreamAbilityUpdate; +import com.dji.sdk.cloudapi.livestream.RcLivestreamAbilityUpdate; +import com.dji.sdk.mqtt.ChannelName; + +import java.util.Arrays; + +/** + * + * @author sean.zhou + * @date 2021/11/18 + * @version 0.1 + */ +public enum StateDataKeyEnum { + + RC_FIRMWARE_VERSION(ChannelName.INBOUND_STATE_RC_FIRMWARE_VERSION, RcFirmwareVersion.class), + + RC_LIVE_CAPACITY(ChannelName.INBOUND_STATE_RC_LIVESTREAM_ABILITY_UPDATE, RcLivestreamAbilityUpdate.class), + + RC_CONTROL_SOURCE(ChannelName.INBOUND_STATE_RC_CONTROL_SOURCE, RcDroneControlSource.class), + + RC_LIVE_STATUS(ChannelName.INBOUND_STATE_RC_LIVE_STATUS, RcLiveStatus.class), + + RC_PAYLOAD_FIRMWARE(ChannelName.INBOUND_STATE_RC_PAYLOAD_FIRMWARE, PayloadFirmwareVersion.class), + + DOCK_FIRMWARE_VERSION(ChannelName.INBOUND_STATE_DOCK_FIRMWARE_VERSION, DockFirmwareVersion.class), + + DOCK_LIVE_CAPACITY(ChannelName.INBOUND_STATE_DOCK_LIVESTREAM_ABILITY_UPDATE, DockLivestreamAbilityUpdate.class), + + DOCK_CONTROL_SOURCE(ChannelName.INBOUND_STATE_DOCK_CONTROL_SOURCE, DockDroneControlSource.class), + + DOCK_LIVE_STATUS(ChannelName.INBOUND_STATE_DOCK_LIVE_STATUS, DockLiveStatus.class), + + WPMZ_VERSION(ChannelName.INBOUND_STATE_DOCK_WPMZ_VERSION, DockWpmzVersion.class), + + DOCK_PAYLOAD(ChannelName.INBOUND_STATE_DOCK_PAYLOAD, DockPayload.class), + + UNKNOWN(ChannelName.DEFAULT, Object.class); + + private final String channelName; + + private final Class classType; + + StateDataKeyEnum(String channelName, Class classType) { + this.channelName = channelName; + this.classType = classType; + } + + public Class getClassType() { + return classType; + } + + public String getChannelName() { + return channelName; + } + + public static StateDataKeyEnum find(Class clazz) { + return Arrays.stream(values()).filter(keyEnum -> keyEnum.classType == clazz).findAny() + .orElse(UNKNOWN); + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/state/StateRouter.java b/src/main/java/com/dji/sdk/mqtt/state/StateRouter.java new file mode 100644 index 0000000..b26c00e --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/state/StateRouter.java @@ -0,0 +1,63 @@ +package com.dji.sdk.mqtt.state; + +import com.dji.sdk.common.Common; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +import com.fasterxml.jackson.core.type.TypeReference; +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 org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.Message; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; + +import static com.dji.sdk.mqtt.TopicConst.*; + +/** + * + * @author sean.zhou + * @date 2021/11/17 + * @version 0.1 + */ +@Configuration +public class StateRouter { + + @Bean + public IntegrationFlow stateDataRouterFlow() { + return IntegrationFlows + .from(ChannelName.INBOUND_STATE) + .transform(Message.class, source -> { + try { + TopicStateRequest response = Common.getObjectMapper().readValue((byte[]) source.getPayload(), new TypeReference() {}); + String topic = String.valueOf(source.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); + String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), topic.indexOf(STATE_SUF)); + return response.setFrom(from) + .setData(Common.getObjectMapper().convertValue(response.getData(), getTypeReference(response.getGateway(), response.getData()))); + } catch (IOException e) { + throw new CloudSDKException(e); + } + }, null) + .route(response -> StateDataKeyEnum.find(response.getData().getClass()), + mapping -> Arrays.stream(StateDataKeyEnum.values()).forEach(key -> mapping.channelMapping(key, key.getChannelName()))) + .get(); + } + + private Class getTypeReference(String gatewaySn, Object data) { + Set keys = ((Map) data).keySet(); + switch (SDKManager.getDeviceSDK(gatewaySn).getType()) { + case RC: + return RcStateDataKeyEnum.find(keys).getClassType(); + case DOCK: + return DockStateDataKeyEnum.find(keys).getClassType(); + default: + throw new CloudSDKException(CloudSDKErrorEnum.WRONG_DATA, "Unexpected value: " + SDKManager.getDeviceSDK(gatewaySn).getType()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/state/StateSubscribe.java b/src/main/java/com/dji/sdk/mqtt/state/StateSubscribe.java new file mode 100644 index 0000000..7a1a746 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/state/StateSubscribe.java @@ -0,0 +1,44 @@ +package com.dji.sdk.mqtt.state; + +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +@Component +public class StateSubscribe { + + @Resource + private IMqttTopicService topicService; + + public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.STATE_SUF; + + public void subscribe(GatewayManager gateway, boolean unsubscribeSubDevice) { + SDKManager.registerDevice(gateway); + topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); + if (unsubscribeSubDevice) { + topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); + return; + } + if (null != gateway.getDroneSn()) { + topicService.subscribe(String.format(TOPIC, gateway.getDroneSn())); + } + } + + public void unsubscribe(GatewayManager gateway) { + SDKManager.logoutDevice(gateway.getGatewaySn()); + topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); + if (null != gateway.getDroneSn()) { + topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); + } + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/state/TopicStateRequest.java b/src/main/java/com/dji/sdk/mqtt/state/TopicStateRequest.java new file mode 100644 index 0000000..555772c --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/state/TopicStateRequest.java @@ -0,0 +1,83 @@ +package com.dji.sdk.mqtt.state; + +import com.dji.sdk.mqtt.CommonTopicRequest; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +public class TopicStateRequest extends CommonTopicRequest { + + private String gateway; + + private String from; + + public TopicStateRequest() { + } + + @Override + public String toString() { + return "TopicStateRequest{" + + "gateway='" + gateway + '\'' + + ", from='" + from + '\'' + + ", tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + public String getTid() { + return tid; + } + + public TopicStateRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicStateRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicStateRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public TopicStateRequest setData(T data) { + this.data = data; + return this; + } + + public String getGateway() { + return gateway; + } + + public TopicStateRequest setGateway(String gateway) { + this.gateway = gateway; + return this; + } + + public String getFrom() { + return from; + } + + public TopicStateRequest setFrom(String from) { + this.from = from; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/status/StatusRouter.java b/src/main/java/com/dji/sdk/mqtt/status/StatusRouter.java new file mode 100644 index 0000000..396bd2e --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/status/StatusRouter.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt.status; + +import com.dji.sdk.cloudapi.device.UpdateTopo; +import com.dji.sdk.common.Common; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.mqtt.ChannelName; +import com.dji.sdk.mqtt.MqttGatewayPublish; +import com.fasterxml.jackson.core.type.TypeReference; +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 org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.Objects; +import java.util.Optional; + +import static com.dji.sdk.mqtt.TopicConst.*; + +/** + * + * @author sean.zhou + * @date 2021/11/12 + * @version 0.1 + */ +@Configuration +public class StatusRouter { + + @Resource + private MqttGatewayPublish gatewayPublish; + + @Bean + public IntegrationFlow statusRouterFlow() { + return IntegrationFlows + .from(ChannelName.INBOUND_STATUS) + .transform(Message.class, source -> { + try { + TopicStatusRequest response = Common.getObjectMapper().readValue((byte[]) source.getPayload(), new TypeReference>() {}); + String topic = String.valueOf(source.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); + return response.setFrom(topic.substring((BASIC_PRE + PRODUCT).length(), topic.indexOf(STATUS_SUF))); + } catch (IOException e) { + throw new CloudSDKException(e); + } + }, null) + ., Boolean>route( + response -> Optional.ofNullable(response.getData()).map(UpdateTopo::getSubDevices).map(CollectionUtils::isEmpty).orElse(true), + mapping -> mapping.channelMapping(true, ChannelName.INBOUND_STATUS_OFFLINE) + .channelMapping(false, ChannelName.INBOUND_STATUS_ONLINE)) + .get(); + } + + @Bean + public IntegrationFlow replySuccessStatus() { + return IntegrationFlows + .from(ChannelName.OUTBOUND_STATUS) + .handle(this::publish) + .nullChannel(); + + } + + private TopicStatusResponse publish(TopicStatusResponse request, MessageHeaders headers) { + if (Objects.isNull(request)) { + return null; + } + gatewayPublish.publishReply(request, headers); + return request; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/mqtt/status/StatusSubscribe.java b/src/main/java/com/dji/sdk/mqtt/status/StatusSubscribe.java new file mode 100644 index 0000000..2412067 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/status/StatusSubscribe.java @@ -0,0 +1,39 @@ +package com.dji.sdk.mqtt.status; + +import com.dji.sdk.common.GatewayManager; +import com.dji.sdk.common.SDKManager; +import com.dji.sdk.mqtt.IMqttTopicService; +import com.dji.sdk.mqtt.TopicConst; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * + * @author sean.zhou + * @date 2021/11/10 + * @version 0.1 + */ +@Component +public class StatusSubscribe { + + public static final String TOPIC = TopicConst.BASIC_PRE + TopicConst.PRODUCT + "%s" + TopicConst.STATUS_SUF; + + @Resource + private IMqttTopicService topicService; + + public void subscribe(GatewayManager gateway) { + SDKManager.registerDevice(gateway); + topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); + } + + public void subscribeWildcardsStatus() { + topicService.subscribe(String.format(TOPIC, "+")); + } + + public void unsubscribe(GatewayManager gateway) { + SDKManager.logoutDevice(gateway.getGatewaySn()); + topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); + } + +} diff --git a/src/main/java/com/dji/sdk/mqtt/status/TopicStatusRequest.java b/src/main/java/com/dji/sdk/mqtt/status/TopicStatusRequest.java new file mode 100644 index 0000000..ff54553 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/status/TopicStatusRequest.java @@ -0,0 +1,84 @@ +package com.dji.sdk.mqtt.status; + +import com.dji.sdk.mqtt.CommonTopicRequest; + +/** + * @author sean + * @version 1.7 + * @date 2023/5/24 + */ +public class TopicStatusRequest extends CommonTopicRequest { + + private String method; + + private String from; + + public TopicStatusRequest() { + } + + @Override + public String toString() { + return "TopicStatusRequest{" + + "method='" + method + '\'' + + ", from='" + from + '\'' + + ", tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + + public String getTid() { + return tid; + } + + public TopicStatusRequest setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicStatusRequest setBid(String bid) { + this.bid = bid; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicStatusRequest setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public TopicStatusRequest setData(T data) { + this.data = data; + return this; + } + + public String getMethod() { + return method; + } + + public TopicStatusRequest setMethod(String method) { + this.method = method; + return this; + } + + public String getFrom() { + return from; + } + + public TopicStatusRequest setFrom(String from) { + this.from = from; + return this; + } +} diff --git a/src/main/java/com/dji/sdk/mqtt/status/TopicStatusResponse.java b/src/main/java/com/dji/sdk/mqtt/status/TopicStatusResponse.java new file mode 100644 index 0000000..943fdb9 --- /dev/null +++ b/src/main/java/com/dji/sdk/mqtt/status/TopicStatusResponse.java @@ -0,0 +1,73 @@ +package com.dji.sdk.mqtt.status; + +import com.dji.sdk.mqtt.CommonTopicResponse; + +/** + * Unified Topic request format + * @author sean.zhou + * @date 2021/11/15 + * @version 0.1 + */ +public class TopicStatusResponse extends CommonTopicResponse { + + private String method; + + @Override + public String toString() { + return "TopicStatusResponse{" + + "tid='" + tid + '\'' + + ", bid='" + bid + '\'' + + ", method='" + method + '\'' + + ", data=" + data + + ", timestamp=" + timestamp + + '}'; + } + + public TopicStatusResponse() { + } + + public String getTid() { + return tid; + } + + public TopicStatusResponse setTid(String tid) { + this.tid = tid; + return this; + } + + public String getBid() { + return bid; + } + + public TopicStatusResponse setBid(String bid) { + this.bid = bid; + return this; + } + + public String getMethod() { + return method; + } + + public TopicStatusResponse setMethod(String method) { + this.method = method; + return this; + } + + public T getData() { + return data; + } + + public TopicStatusResponse setData(T data) { + this.data = data; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public TopicStatusResponse setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/swagger/SwaggerConfig.java b/src/main/java/com/dji/sdk/swagger/SwaggerConfig.java new file mode 100644 index 0000000..46be817 --- /dev/null +++ b/src/main/java/com/dji/sdk/swagger/SwaggerConfig.java @@ -0,0 +1,56 @@ +package com.dji.sdk.swagger; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.SpringDocConfigProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author sean + * @version 1.7 + * @date 2023/6/14 + */ +@Configuration +@OpenAPIDefinition(security = {@SecurityRequirement(name = "default")}) +public class SwaggerConfig { + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(new Info().title("CloudSDK API").description("All HTTP interfaces encapsulated by CloudSDK.") + .license(new License().name("LICENSE").url("https://github.com/dji-sdk/DJI-Cloud-API-Demo/blob/main/LICENSE")) + .version("1.0.0")).components(components()); + } + + @Bean + public SecurityScheme securityScheme() { + return new SecurityScheme().type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER).name("x-auth-token"); + } + + @Bean + public Components components() { + return new Components() + .addSecuritySchemes("default", securityScheme()); + } + + @Bean + public GroupedOpenApi sdkOpenApi() { + return GroupedOpenApi.builder().group("CloudSDK") + .packagesToScan("com.dji").build(); + } + + @Bean + public SpringDocConfigProperties springDocConfigProperties(SpringDocConfigProperties properties) { + properties.setDefaultFlatParamObject(false); + properties.setDefaultSupportFormData(true); + properties.setDefaultProducesMediaType("application/json"); + return properties; + } +} diff --git a/src/main/java/com/dji/sdk/websocket/BizCodeEnum.java b/src/main/java/com/dji/sdk/websocket/BizCodeEnum.java new file mode 100644 index 0000000..bb68d39 --- /dev/null +++ b/src/main/java/com/dji/sdk/websocket/BizCodeEnum.java @@ -0,0 +1,42 @@ +package com.dji.sdk.websocket; + +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * @author sean + * @version 0.1 + * @date 2021/11/26 + */ +@Schema(enumAsRef = true, description = "Pilot2 will receive these bizCode, and then do corresponding processing according to the value.") +public enum BizCodeEnum { + + DEVICE_ONLINE("device_online"), + + DEVICE_OFFLINE("device_offline"), + + DEVICE_UPDATE_TOPO("device_update_topo"), + + DEVICE_OSD("device_osd"), + + MAP_ELEMENT_CREATE("map_element_create"), + + MAP_ELEMENT_UPDATE("map_element_update"), + + MAP_ELEMENT_DELETE("map_element_delete"), + + MAP_GROUP_REFRESH("map_group_refresh"); + + private final String code; + + BizCodeEnum(String code) { + this.code = code; + } + + @JsonValue + public String getCode() { + return code; + } + + +} diff --git a/src/main/java/com/dji/sample/component/websocket/config/ConcurrentWebSocketSession.java b/src/main/java/com/dji/sdk/websocket/ConcurrentWebSocketSession.java similarity index 93% rename from src/main/java/com/dji/sample/component/websocket/config/ConcurrentWebSocketSession.java rename to src/main/java/com/dji/sdk/websocket/ConcurrentWebSocketSession.java index cd13365..e4c3938 100644 --- a/src/main/java/com/dji/sample/component/websocket/config/ConcurrentWebSocketSession.java +++ b/src/main/java/com/dji/sdk/websocket/ConcurrentWebSocketSession.java @@ -1,4 +1,4 @@ -package com.dji.sample.component.websocket.config; +package com.dji.sdk.websocket; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator; diff --git a/src/main/java/com/dji/sample/component/websocket/config/WebSocketMessageConfiguration.java b/src/main/java/com/dji/sdk/websocket/WebSocketConfiguration.java similarity index 62% rename from src/main/java/com/dji/sample/component/websocket/config/WebSocketMessageConfiguration.java rename to src/main/java/com/dji/sdk/websocket/WebSocketConfiguration.java index 85a696c..52ceb9b 100644 --- a/src/main/java/com/dji/sample/component/websocket/config/WebSocketMessageConfiguration.java +++ b/src/main/java/com/dji/sdk/websocket/WebSocketConfiguration.java @@ -1,4 +1,4 @@ -package com.dji.sample.component.websocket.config; +package com.dji.sdk.websocket; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; @@ -6,6 +6,8 @@ import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBr import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration; +import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; +import org.springframework.web.socket.server.HandshakeHandler; /** * @@ -15,25 +17,24 @@ import org.springframework.web.socket.config.annotation.WebSocketTransportRegist */ @EnableWebSocketMessageBroker @Configuration -public class WebSocketMessageConfiguration implements WebSocketMessageBrokerConfigurer { +public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { - @Autowired - private AuthPrincipalHandler authPrincipalHandler; + @Autowired(required = false) + private HandshakeHandler handshakeHandler; @Autowired - private WebSocketDefaultFactory webSocketDefaultFactory; + private WebSocketHandlerDecoratorFactory webSocketHandlerDecoratorFactory; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { // Set the WebSocket connection address registry.addEndpoint("/api/v1/ws").setAllowedOriginPatterns("*") - .setHandshakeHandler(authPrincipalHandler); + .setHandshakeHandler(handshakeHandler); } @Override public void configureWebSocketTransport(WebSocketTransportRegistration registry) { - registry.addDecoratorFactory(webSocketDefaultFactory); - registry.setTimeToFirstMessage(60000 * 60 * 24 * 10); + registry.addDecoratorFactory(webSocketHandlerDecoratorFactory); } } \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/websocket/WebSocketDefaultFactory.java b/src/main/java/com/dji/sdk/websocket/WebSocketDefaultFactory.java new file mode 100644 index 0000000..1d03b1f --- /dev/null +++ b/src/main/java/com/dji/sdk/websocket/WebSocketDefaultFactory.java @@ -0,0 +1,20 @@ +package com.dji.sdk.websocket; + +import org.springframework.stereotype.Component; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; + +/** + * + * @author sean.zhou + * @date 2021/11/16 + * @version 0.1 + */ +@Component +public class WebSocketDefaultFactory implements WebSocketHandlerDecoratorFactory { + + @Override + public WebSocketHandler decorate(WebSocketHandler handler) { + return new WebSocketDefaultHandler(handler); + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/websocket/WebSocketDefaultHandler.java b/src/main/java/com/dji/sdk/websocket/WebSocketDefaultHandler.java new file mode 100644 index 0000000..01e72b3 --- /dev/null +++ b/src/main/java/com/dji/sdk/websocket/WebSocketDefaultHandler.java @@ -0,0 +1,40 @@ +package com.dji.sdk.websocket; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.WebSocketMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.WebSocketHandlerDecorator; + +/** + * + * @author sean.zhou + * @date 2021/11/16 + * @version 0.1 + */ +public class WebSocketDefaultHandler extends WebSocketHandlerDecorator { + + private static final Logger log = LoggerFactory.getLogger(WebSocketDefaultHandler.class); + + public WebSocketDefaultHandler(WebSocketHandler delegate) { + super(delegate); + } + + @Override + public void afterConnectionEstablished(WebSocketSession session) throws Exception { + log.debug("{} is connected.", session.getId()); + } + + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { + log.debug("{} is disconnected.", session.getId()); + } + + @Override + public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception { + log.info("received message: {}, from: {}", message.getPayload(), session.getId()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/websocket/WebSocketMessageResponse.java b/src/main/java/com/dji/sdk/websocket/WebSocketMessageResponse.java new file mode 100644 index 0000000..816d555 --- /dev/null +++ b/src/main/java/com/dji/sdk/websocket/WebSocketMessageResponse.java @@ -0,0 +1,83 @@ +package com.dji.sdk.websocket; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * The format of WebSocket messages that the pilot can receive. + * @author sean.zhou + * @date 2021/11/17 + * @version 0.1 + */ +@Schema(description = "The format of WebSocket messages that the pilot can receive.") +public class WebSocketMessageResponse { + + @JsonProperty("biz_code") + @NotNull + @Schema(description = "webSocket messages identity", implementation = BizCodeEnum.class) + private String bizCode; + + @Schema(description = "webSocket messages version") + private String version = "1.0"; + + @NotNull + @Min(123456789012L) + @Schema(description = "timestamp (milliseconds)") + private Long timestamp; + + @NotNull + @Schema(description = "Data corresponding to business functions") + private T data; + + public WebSocketMessageResponse() { + } + + @Override + public String toString() { + return "WebSocketMessageResponse{" + + "bizCode=" + bizCode + + ", version='" + version + '\'' + + ", timestamp=" + timestamp + + ", data=" + data + + '}'; + } + + public String getBizCode() { + return bizCode; + } + + public WebSocketMessageResponse setBizCode(String bizCode) { + this.bizCode = bizCode; + return this; + } + + public String getVersion() { + return version; + } + + public WebSocketMessageResponse setVersion(String version) { + this.version = version; + return this; + } + + public Long getTimestamp() { + return timestamp; + } + + public WebSocketMessageResponse setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public T getData() { + return data; + } + + public WebSocketMessageResponse setData(T data) { + this.data = data; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/dji/sdk/websocket/api/WebSocketMessageSend.java b/src/main/java/com/dji/sdk/websocket/api/WebSocketMessageSend.java new file mode 100644 index 0000000..9caee08 --- /dev/null +++ b/src/main/java/com/dji/sdk/websocket/api/WebSocketMessageSend.java @@ -0,0 +1,64 @@ +package com.dji.sdk.websocket.api; + +import com.dji.sdk.common.Common; +import com.dji.sdk.exception.CloudSDKErrorEnum; +import com.dji.sdk.exception.CloudSDKException; +import com.dji.sdk.websocket.ConcurrentWebSocketSession; +import com.dji.sdk.websocket.WebSocketMessageResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.socket.TextMessage; + +import java.io.IOException; +import java.util.Collection; + +/** + * @author sean.zhou + * @version 0.1 + * @date 2021/11/24 + */ +public class WebSocketMessageSend { + + private static final Logger log = LoggerFactory.getLogger(WebSocketMessageSend.class); + + public void sendMessage(ConcurrentWebSocketSession session, WebSocketMessageResponse message) { + if (session == null) { + return; + } + + try { + if (!session.isOpen()) { + session.close(); + log.info("This session is closed."); + return; + } + + session.sendMessage(new TextMessage(Common.getObjectMapper().writeValueAsBytes(message))); + } catch (IOException e) { + throw new CloudSDKException(CloudSDKErrorEnum.WEBSOCKET_PUBLISH_ABNORMAL, e.getLocalizedMessage()); + } + } + + public void sendBatch(Collection sessions, WebSocketMessageResponse message) { + if (sessions.isEmpty()) { + return; + } + + try { + + TextMessage data = new TextMessage(Common.getObjectMapper().writeValueAsBytes(message)); + + for (ConcurrentWebSocketSession session : sessions) { + if (!session.isOpen()) { + session.close(); + log.info("This session is closed."); + return; + } + session.sendMessage(data); + } + + } catch (IOException e) { + throw new CloudSDKException(CloudSDKErrorEnum.WEBSOCKET_PUBLISH_ABNORMAL, e.getLocalizedMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a5c937a..b5ebbe7 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,8 @@ server: port: 6789 spring: + main: + allow-bean-definition-overriding: true application: name: cloud-api-sample datasource: @@ -50,13 +52,18 @@ mqtt: client-id: 123456 # If the protocol is ws/wss, this value is required. path: - # Topics that need to be subscribed when initially connecting to mqtt, multiple topics are divided by ",". - inbound-topic: sys/product/+/status,thing/product/+/requests DRC: protocol: WS # @see com.dji.sample.component.mqtt.model.MqttProtocolEnum host: Please enter your ip. port: 8083 path: /mqtt + username: JavaServer + password: 123456 + +cloud-sdk: + mqtt: + # Topics that need to be subscribed when initially connecting to mqtt, multiple topics are divided by ",". + inbound-topic: sys/product/+/status,thing/product/+/requests url: manage: @@ -81,7 +88,7 @@ url: # Tutorial: https://www.alibabacloud.com/help/en/object-storage-service/latest/use-a-temporary-credential-provided-by-sts-to-access-oss oss: enable: false - provider: ali # @see com.dji.sample.component.OssConfiguration.model.enums.OssTypeEnum + provider: ALIYUN # @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. @@ -118,7 +125,7 @@ oss: logging: level: - com.dji.sample: debug + com.dji: debug file: name: logs/cloud-api-sample.log diff --git a/src/main/resources/image/1.png b/src/main/resources/image/1.png new file mode 100644 index 0000000000000000000000000000000000000000..8d57aa869801956442ea845bd3e71e441db3cf04 GIT binary patch literal 29224 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