Browse Source

Merge 5c8030732a into b390ebe579

pull/46/merge
Vincent Chan 1 year ago committed by GitHub
parent
commit
40a9a72741
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      src/main/java/com/dji/sample/component/ApplicationBootInitial.java
  2. 2
      src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java
  3. 3
      src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java
  4. 3
      src/main/java/com/dji/sample/control/service/impl/DrcServiceImpl.java
  5. 3
      src/main/java/com/dji/sample/manage/service/impl/DeviceLogsServiceImpl.java
  6. 3
      src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
  7. 3
      src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java
  8. 3
      src/main/java/com/dji/sample/manage/service/impl/SDKDeviceService.java
  9. 3
      src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java
  10. 2
      src/main/java/com/dji/sdk/cloudapi/control/FlyToPointRequest.java
  11. 2
      src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointRequest.java
  12. 7
      src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStepKeyEnum.java
  13. 2
      src/main/java/com/dji/sdk/cloudapi/device/CameraModeEnum.java
  14. 3
      src/main/java/com/dji/sdk/cloudapi/device/DockLiveErrorStatus.java
  15. 5
      src/main/java/com/dji/sdk/cloudapi/device/DroneModeCodeEnum.java
  16. 4
      src/main/java/com/dji/sdk/cloudapi/device/api/AbstractDeviceService.java
  17. 3
      src/main/java/com/dji/sdk/cloudapi/media/UploadFlighttaskMediaPrioritize.java
  18. 11
      src/main/java/com/dji/sdk/cloudapi/wayline/ExecutionStepEnum.java
  19. 20
      src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakPoint.java
  20. 5
      src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskExecuteRequest.java
  21. 3
      src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskPrepareRequest.java
  22. 5
      src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskUndoRequest.java
  23. 89
      src/main/java/com/dji/sdk/common/JDKLockBarrierImpl.java
  24. 65
      src/main/java/com/dji/sdk/common/LocalCacheSDKManager.java
  25. 26
      src/main/java/com/dji/sdk/common/PublishBarrier.java
  26. 41
      src/main/java/com/dji/sdk/common/PublishBarrierResult.java
  27. 88
      src/main/java/com/dji/sdk/common/PublishConfiguration.java
  28. 57
      src/main/java/com/dji/sdk/common/PublishOption.java
  29. 20
      src/main/java/com/dji/sdk/common/PublishRequest.java
  30. 17
      src/main/java/com/dji/sdk/common/PublishResult.java
  31. 17
      src/main/java/com/dji/sdk/common/ReadonlyPublishConfiguration.java
  32. 36
      src/main/java/com/dji/sdk/common/SDKManager.java
  33. 80
      src/main/java/com/dji/sdk/config/DefaultBeanConfiguration.java
  34. 53
      src/main/java/com/dji/sdk/mqtt/ChanBarrierAdapter.java
  35. 51
      src/main/java/com/dji/sdk/mqtt/FlowTransformWrapper.java
  36. 24
      src/main/java/com/dji/sdk/mqtt/GlobalPublishOption.java
  37. 5
      src/main/java/com/dji/sdk/mqtt/InboundMessageRouter.java
  38. 104
      src/main/java/com/dji/sdk/mqtt/MqttGatewayPublish.java
  39. 39
      src/main/java/com/dji/sdk/mqtt/osd/OsdRouter.java
  40. 7
      src/main/java/com/dji/sdk/mqtt/osd/OsdSubscribe.java
  41. 34
      src/main/java/com/dji/sdk/mqtt/property/PropertySetPublish.java
  42. 17
      src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyHandler.java
  43. 44
      src/main/java/com/dji/sdk/mqtt/services/ServicesPublish.java
  44. 17
      src/main/java/com/dji/sdk/mqtt/services/ServicesReplyHandler.java
  45. 8
      src/main/java/com/dji/sdk/mqtt/state/DockStateDataKeyEnum.java
  46. 7
      src/main/java/com/dji/sdk/mqtt/state/RcStateDataKeyEnum.java
  47. 70
      src/main/java/com/dji/sdk/mqtt/state/StateRouter.java
  48. 7
      src/main/java/com/dji/sdk/mqtt/state/StateSubscribe.java
  49. 7
      src/main/java/com/dji/sdk/mqtt/status/StatusSubscribe.java

6
src/main/java/com/dji/sample/component/ApplicationBootInitial.java

@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Optional; import java.util.Optional;
/** /**
@ -27,6 +28,9 @@ public class ApplicationBootInitial implements CommandLineRunner {
@Autowired @Autowired
private IDeviceRedisService deviceRedisService; private IDeviceRedisService deviceRedisService;
@Resource
SDKManager sdkManager;
/** /**
* Subscribe to the devices that exist in the redis when the program starts, * Subscribe to the devices that exist in the redis when the program starts,
* to prevent the data from being different from the pilot side due to program interruptions. * to prevent the data from being different from the pilot side due to program interruptions.
@ -44,7 +48,7 @@ public class ApplicationBootInitial implements CommandLineRunner {
.map(Optional::get) .map(Optional::get)
.filter(device -> DeviceDomainEnum.DRONE != device.getDomain()) .filter(device -> DeviceDomainEnum.DRONE != device.getDomain())
.forEach(device -> deviceService.subDeviceOnlineSubscribeTopic( .forEach(device -> deviceService.subDeviceOnlineSubscribeTopic(
SDKManager.registerDevice(device.getDeviceSn(), device.getChildDeviceSn(), device.getDomain(), sdkManager.registerDevice(device.getDeviceSn(), device.getChildDeviceSn(), device.getDomain(),
device.getType(), device.getSubType(), device.getThingVersion(), device.getType(), device.getSubType(), device.getThingVersion(),
deviceRedisService.getDeviceOnline(device.getChildDeviceSn()).map(DeviceDTO::getThingVersion).orElse(null)))); deviceRedisService.getDeviceOnline(device.getChildDeviceSn()).map(DeviceDTO::getThingVersion).orElse(null))));

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

@ -86,6 +86,7 @@ public class AliyunOssServiceImpl implements IOssService {
@Override @Override
public InputStream getObject(String bucket, String objectKey) { public InputStream getObject(String bucket, String objectKey) {
//bug: ossClient.getObject -> OSSObject need to close manually. otherwise will hang up main thread to wait available connection. by witcom @2023.12.08
return ossClient.getObject(bucket, objectKey).getObjectContent(); return ossClient.getObject(bucket, objectKey).getObjectContent();
} }
@ -94,6 +95,7 @@ public class AliyunOssServiceImpl implements IOssService {
if (ossClient.doesObjectExist(bucket, objectKey)) { if (ossClient.doesObjectExist(bucket, objectKey)) {
throw new RuntimeException("The filename already exists."); throw new RuntimeException("The filename already exists.");
} }
//bug: PutObjectResult need to close manually. otherwise will hang up main thread to wait available connection. by witcom @2023.12.08
PutObjectResult objectResult = ossClient.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata())); PutObjectResult objectResult = ossClient.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata()));
log.info("Upload FlighttaskCreateFile: {}", objectResult.getETag()); log.info("Upload FlighttaskCreateFile: {}", objectResult.getETag());
} }

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

@ -68,6 +68,9 @@ public class ControlServiceImpl implements IControlService {
@Qualifier("SDKWaylineService") @Qualifier("SDKWaylineService")
private AbstractWaylineService abstractWaylineService; private AbstractWaylineService abstractWaylineService;
@Autowired
SDKManager SDKManager;
private RemoteDebugHandler checkDebugCondition(String sn, RemoteDebugParam param, RemoteDebugMethodEnum controlMethodEnum) { private RemoteDebugHandler checkDebugCondition(String sn, RemoteDebugParam param, RemoteDebugMethodEnum controlMethodEnum) {
RemoteDebugHandler handler = Objects.nonNull(controlMethodEnum.getClazz()) ? RemoteDebugHandler handler = Objects.nonNull(controlMethodEnum.getClazz()) ?
mapper.convertValue(Objects.nonNull(param) ? param : new Object(), controlMethodEnum.getClazz()) mapper.convertValue(Objects.nonNull(param) ? param : new Object(), controlMethodEnum.getClazz())

3
src/main/java/com/dji/sample/control/service/impl/DrcServiceImpl.java

@ -84,6 +84,9 @@ public class DrcServiceImpl implements IDrcService {
@Autowired @Autowired
private AbstractControlService abstractControlService; private AbstractControlService abstractControlService;
@Autowired
SDKManager SDKManager;
@Override @Override
public void setDrcModeInRedis(String dockSn, String clientId) { public void setDrcModeInRedis(String dockSn, String clientId) {
RedisOpsUtils.setWithExpire(RedisConst.DRC_PREFIX + dockSn, clientId, RedisConst.DRC_MODE_ALIVE_SECOND); RedisOpsUtils.setWithExpire(RedisConst.DRC_PREFIX + dockSn, clientId, RedisConst.DRC_MODE_ALIVE_SECOND);

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

@ -85,6 +85,9 @@ public class DeviceLogsServiceImpl extends AbstractLogService implements IDevice
@Autowired @Autowired
private AbstractLogService abstractLogService; private AbstractLogService abstractLogService;
@Autowired
SDKManager SDKManager;
@Override @Override
public PaginationData<DeviceLogsDTO> getUploadedLogs(String deviceSn, DeviceLogsQueryParam param) { public PaginationData<DeviceLogsDTO> getUploadedLogs(String deviceSn, DeviceLogsQueryParam param) {
LambdaQueryWrapper<DeviceLogsEntity> queryWrapper = new LambdaQueryWrapper<DeviceLogsEntity>() LambdaQueryWrapper<DeviceLogsEntity> queryWrapper = new LambdaQueryWrapper<DeviceLogsEntity>()

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

@ -124,6 +124,9 @@ public class DeviceServiceImpl implements IDeviceService {
@Autowired @Autowired
private AbstractFirmwareService abstractFirmwareService; private AbstractFirmwareService abstractFirmwareService;
@Autowired
SDKManager SDKManager;
@Override @Override
public void subDeviceOffline(String deviceSn) { public void subDeviceOffline(String deviceSn) {
// If no information about this device exists in the cache, the drone is considered to be offline. // If no information about this device exists in the cache, the drone is considered to be offline.

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

@ -46,6 +46,9 @@ public class LiveStreamServiceImpl implements ILiveStreamService {
@Autowired @Autowired
private AbstractLivestreamService abstractLivestreamService; private AbstractLivestreamService abstractLivestreamService;
@Autowired
SDKManager SDKManager;
@Override @Override
public List<CapacityDeviceDTO> getLiveCapacity(String workspaceId) { public List<CapacityDeviceDTO> getLiveCapacity(String workspaceId) {

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

@ -57,6 +57,9 @@ public class SDKDeviceService extends AbstractDeviceService {
@Autowired @Autowired
private IDevicePayloadService devicePayloadService; private IDevicePayloadService devicePayloadService;
@Autowired
SDKManager SDKManager;
@Override @Override
public TopicStatusResponse<MqttReply> updateTopoOnline(TopicStatusRequest<UpdateTopo> request, MessageHeaders headers) { public TopicStatusResponse<MqttReply> updateTopoOnline(TopicStatusRequest<UpdateTopo> request, MessageHeaders headers) {
UpdateTopoSubDevice updateTopoSubDevice = request.getData().getSubDevices().get(0); UpdateTopoSubDevice updateTopoSubDevice = request.getData().getSubDevices().get(0);

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

@ -62,6 +62,9 @@ public class FlightTaskServiceImpl extends AbstractWaylineService implements IFl
@Autowired @Autowired
private ObjectMapper mapper; private ObjectMapper mapper;
@Autowired
SDKManager SDKManager;
@Autowired @Autowired
private IWebSocketMessageService websocketMessageService; private IWebSocketMessageService websocketMessageService;

2
src/main/java/com/dji/sdk/cloudapi/control/FlyToPointRequest.java

@ -13,7 +13,7 @@ import java.util.List;
*/ */
public class FlyToPointRequest extends BaseModel { 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}$") //@Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
@NotNull @NotNull
private String flyToId; private String flyToId;

2
src/main/java/com/dji/sdk/cloudapi/control/TakeoffToPointRequest.java

@ -19,7 +19,7 @@ import javax.validation.constraints.Pattern;
*/ */
public class TakeoffToPointRequest extends BaseModel { 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}$") //@Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
@NotNull @NotNull
private String flightId; private String flightId;

7
src/main/java/com/dji/sdk/cloudapi/debug/RemoteDebugStepKeyEnum.java

@ -59,7 +59,9 @@ public enum RemoteDebugStepKeyEnum {
FREE_PUTTER("free_putter", "Free Putter"), FREE_PUTTER("free_putter", "Free Putter"),
STOP_CHARGE("stop_charge", "Stop charging"); STOP_CHARGE("stop_charge", "Stop charging"),
UNKNOWN("unknown","Unknown");
private final String stepKey; private final String stepKey;
@ -79,10 +81,11 @@ public enum RemoteDebugStepKeyEnum {
return message; return message;
} }
//fix: 提供unknown取代异常 witcom@2023.10.30
@JsonCreator @JsonCreator
public static RemoteDebugStepKeyEnum find(String stepKey) { public static RemoteDebugStepKeyEnum find(String stepKey) {
return Arrays.stream(values()).filter(stepKeyEnum -> stepKeyEnum.stepKey.equals(stepKey)).findAny() return Arrays.stream(values()).filter(stepKeyEnum -> stepKeyEnum.stepKey.equals(stepKey)).findAny()
.orElseThrow(() -> new CloudSDKException(RemoteDebugStepKeyEnum.class,stepKey)); .orElse(UNKNOWN);
} }
} }

2
src/main/java/com/dji/sdk/cloudapi/device/CameraModeEnum.java

@ -13,6 +13,8 @@ import java.util.Arrays;
*/ */
public enum CameraModeEnum { public enum CameraModeEnum {
//fix: Cannot construct instance of `com.dji.sdk.cloudapi.device.CameraModeEnum`, problem: com.dji.sdk.cloudapi.device.CameraModeEnum has unknown data: [-1] vincent @ 2023.12.07
UNKNOWN(-1),
PHOTO(0), PHOTO(0),
VIDEO(1); VIDEO(1);

3
src/main/java/com/dji/sdk/cloudapi/device/DockLiveErrorStatus.java

@ -40,11 +40,14 @@ public class DockLiveErrorStatus {
} }
public String getMessage() { public String getMessage() {
if(success){ return "success";}
return errorCode.getMessage(); return errorCode.getMessage();
} }
@JsonValue @JsonValue
public Integer getCode() { public Integer getCode() {
//witcom: errorCode.getCode() will cause npe 2023.10.30
if(success){ return 0; }
return source.getSource() * MOD + errorCode.getCode(); return source.getSource() * MOD + errorCode.getCode();
} }

5
src/main/java/com/dji/sdk/cloudapi/device/DroneModeCodeEnum.java

@ -45,7 +45,10 @@ public enum DroneModeCodeEnum {
APAS(15), APAS(15),
VIRTUAL_JOYSTICK(16); VIRTUAL_JOYSTICK(16),
//witcom: 飞机报这个号上来报异常
UNKNOWN_1(17);
private final int code; private final int code;

4
src/main/java/com/dji/sdk/cloudapi/device/api/AbstractDeviceService.java

@ -169,7 +169,9 @@ public class AbstractDeviceService {
* @param request data * @param request data
* @param headers The headers for a {@link Message}. * @param headers The headers for a {@link Message}.
*/ */
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_WPMZ_VERSION) //witcom: wrong ChannelName. is INBOUND_STATE_DOCK_PAYLOAD??
// @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_WPMZ_VERSION)
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_DOCK_PAYLOAD)
public void dockPayload(TopicStateRequest<DockPayload> request, MessageHeaders headers) { public void dockPayload(TopicStateRequest<DockPayload> request, MessageHeaders headers) {
throw new UnsupportedOperationException("dockPayload not implemented"); throw new UnsupportedOperationException("dockPayload not implemented");
} }

3
src/main/java/com/dji/sdk/cloudapi/media/UploadFlighttaskMediaPrioritize.java

@ -12,8 +12,9 @@ import javax.validation.constraints.Pattern;
*/ */
public class UploadFlighttaskMediaPrioritize extends BaseModel { public class UploadFlighttaskMediaPrioritize extends BaseModel {
/* fix: flightId是应用层提供的,应用层有自己的id构建规则,如果不是设备原因必须指定格式,最好就不要控制flightId的格式了 by witcom@2023.09.22 */
@NotNull @NotNull
@Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") //@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; private String flightId;
public UploadFlighttaskMediaPrioritize() { public UploadFlighttaskMediaPrioritize() {

11
src/main/java/com/dji/sdk/cloudapi/wayline/ExecutionStepEnum.java

@ -52,15 +52,16 @@ public enum ExecutionStepEnum {
return msg; return msg;
} }
//witcom: 这个函数跑不进来,跑了下面的函数,导致FlightTaskProgress报不上来
@JsonCreator @JsonCreator
public static ExecutionStepEnum find(int step) { public static ExecutionStepEnum find(int step) {
return Arrays.stream(values()).filter(stepEnum -> stepEnum.min <= step && stepEnum.max >= step).findAny() return Arrays.stream(values()).filter(stepEnum -> stepEnum.min <= step && stepEnum.max >= step).findAny()
.orElseThrow(() -> new CloudSDKException(ExecutionStepEnum.class, step)); .orElseThrow(() -> new CloudSDKException(ExecutionStepEnum.class, step));
} }
@JsonCreator // @JsonCreator
public static ExecutionStepEnum find(String msg) { // public static ExecutionStepEnum find(String msg) {
return Arrays.stream(values()).filter(stepEnum -> stepEnum.msg.equals(msg)).findAny() // return Arrays.stream(values()).filter(stepEnum -> stepEnum.msg.equals(msg)).findAny()
.orElseThrow(() -> new CloudSDKException(ExecutionStepEnum.class, msg)); // .orElseThrow(() -> new CloudSDKException(ExecutionStepEnum.class, msg));
} // }
} }

20
src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskBreakPoint.java

@ -24,12 +24,10 @@ public class FlighttaskBreakPoint {
@NotNull @NotNull
private BreakpointStateEnum state; private BreakpointStateEnum state;
/**
* Current wayline segment process // @NotNull
*/ // @Min(0)
@NotNull // @Max(1)
@Min(0)
@Max(1)
private Float progress; private Float progress;
/** /**
@ -37,10 +35,14 @@ public class FlighttaskBreakPoint {
*/ */
private Integer waylineID; private Integer waylineID;
/**
* Current wayline segment process
* bug: Cannot deserialize value of type `com.dji.sdk.cloudapi.wayline.FlighttaskBreakReasonEnum` from number 1281: index value outside legal index range [0..-1] mark by witcom 2023.11.09
*/
/** /**
* Break reason * Break reason
*/ */
private FlighttaskBreakReasonEnum breakReason; private Integer breakReason;
/** /**
* Breakpoint latitude * Breakpoint latitude
@ -116,11 +118,11 @@ public class FlighttaskBreakPoint {
return this; return this;
} }
public FlighttaskBreakReasonEnum getBreakReason() { public Integer getBreakReason() {
return breakReason; return breakReason;
} }
public FlighttaskBreakPoint setBreakReason(FlighttaskBreakReasonEnum breakReason) { public FlighttaskBreakPoint setBreakReason(Integer breakReason) {
this.breakReason = breakReason; this.breakReason = breakReason;
return this; return this;
} }

5
src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskExecuteRequest.java

@ -12,8 +12,11 @@ import javax.validation.constraints.Pattern;
*/ */
public class FlighttaskExecuteRequest extends BaseModel { public class FlighttaskExecuteRequest extends BaseModel {
/**
* fix: flightId是应用层提供的,应用层有自己的id构建规则,如果不是设备原因必须指定格式,最好就不要控制flightId的格式了 by witcom@2023.09.22
*/
@NotNull @NotNull
@Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") //@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; private String flightId;
public FlighttaskExecuteRequest() { public FlighttaskExecuteRequest() {

3
src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskPrepareRequest.java

@ -20,9 +20,10 @@ public class FlighttaskPrepareRequest extends BaseModel {
/** /**
* Task ID * Task ID
* fix: flightId是应用层提供的,应用层有自己的id构建规则,如果不是设备原因必须指定格式,最好就不要控制flightId的格式了 by witcom@2023.09.22
*/ */
@NotNull @NotNull
@Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") //@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; private String flightId;
/** /**

5
src/main/java/com/dji/sdk/cloudapi/wayline/FlighttaskUndoRequest.java

@ -14,9 +14,12 @@ import java.util.List;
*/ */
public class FlighttaskUndoRequest extends BaseModel { public class FlighttaskUndoRequest extends BaseModel {
/**
* fix: flightId是应用层提供的,应用层有自己的id构建规则,如果不是设备原因必须指定格式,最好就不要控制flightId的格式了 by witcom@2023.09.22
*/
@NotNull @NotNull
@Size(min = 1) @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; 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() { public FlighttaskUndoRequest() {
} }

89
src/main/java/com/dji/sdk/common/JDKLockBarrierImpl.java

@ -0,0 +1,89 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月22日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.common;
import com.dji.sdk.mqtt.Chan;
import com.dji.sdk.mqtt.CommonTopicRequest;
import com.dji.sdk.mqtt.CommonTopicResponse;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class JDKLockBarrierImpl implements PublishBarrier{
/**
* 在我的实现中是采用一个定期清理的TimedCache储存请求
*/
private final ConcurrentHashMap<String, JDKHolder> container = new ConcurrentHashMap<>();
@Override
public String generateIdentity(CommonTopicRequest requestData) {
return requestData.getTid();
}
@Override
public String generateIdentity(CommonTopicResponse receiveData) {
return receiveData.getTid();
}
@Override
public void put(String identity, CommonTopicResponse receiveData) {
if(hasIdentity(identity)){
container.get(identity).setData(receiveData);
}
}
@Override
public void registerRequest(String identity, CommonTopicRequest requestData) {
container.put(identity,new JDKHolder());
}
@Override
public PublishBarrierResult await(String identity, long timeout) {
JDKHolder jdkHolder = container.get(identity);
if(Objects.isNull(jdkHolder)){
throw new RuntimeException("等待指令返回前未注册指令到栅栏");
}
jdkHolder.await(timeout);
return jdkHolder.getResult();
}
@Override
public boolean hasIdentity(String identity) {
return container.containsKey(identity);
}
public static class JDKHolder{
volatile Object locker = new Object();
CommonTopicResponse response = null;
public void await(long timeout) {
synchronized (locker){
try {
locker.wait(timeout);
}catch (InterruptedException e){}
}
}
public void setData(CommonTopicResponse receiveData) {
this.response = receiveData;
synchronized (locker){
locker.notify();
}
}
public PublishBarrierResult getResult() {
if(Objects.nonNull(response)){
return PublishBarrierResult.ok(response);
}else{
return PublishBarrierResult.EMPTY;
}
}
}
}

65
src/main/java/com/dji/sdk/common/LocalCacheSDKManager.java

@ -0,0 +1,65 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月25日
* @version: 1.0.0
* @description:
**************************************************/
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 org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public class LocalCacheSDKManager implements SDKManager{
final ConcurrentHashMap<String, GatewayManager> SDK_MAP = new ConcurrentHashMap<>(16);
@Override
public 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.");
}
@Override
public Optional<GatewayManager> findDeviceSDK(String gatewaySn) {
if(SDK_MAP.containsKey(gatewaySn)){
return Optional.of(SDK_MAP.get(gatewaySn));
}else {
return Optional.empty();
}
}
@Override
public 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);
}
@Override
public GatewayManager registerDevice(String gatewaySn, String droneSn, GatewayTypeEnum type, String gatewayThingVersion, String droneThingVersion) {
return registerDevice(new GatewayManager(Objects.requireNonNull(gatewaySn), droneSn, type, gatewayThingVersion, droneThingVersion));
}
@Override
public GatewayManager registerDevice(GatewayManager gateway) {
SDK_MAP.put(gateway.getGatewaySn(), gateway);
return gateway;
}
@Override
public void logoutDevice(String gatewaySn) {
SDK_MAP.remove(gatewaySn);
}
}

26
src/main/java/com/dji/sdk/common/PublishBarrier.java

@ -0,0 +1,26 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月22日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.common;
import com.dji.sdk.mqtt.CommonTopicRequest;
import com.dji.sdk.mqtt.CommonTopicResponse;
public interface PublishBarrier {
//构建栅栏标识方法
String generateIdentity(CommonTopicRequest requestData);
String generateIdentity(CommonTopicResponse receiveData);
void put(String identity, CommonTopicResponse receiveData);
void registerRequest(String identity, CommonTopicRequest requestData);
PublishBarrierResult await(String identity,long timeout);
boolean hasIdentity(String identity);
}

41
src/main/java/com/dji/sdk/common/PublishBarrierResult.java

@ -0,0 +1,41 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月22日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.common;
import com.dji.sdk.mqtt.CommonTopicResponse;
public class PublishBarrierResult implements PublishResult {
public static PublishBarrierResult EMPTY = new PublishBarrierResult();
public static PublishBarrierResult ok(CommonTopicResponse data){
return new PublishBarrierResult().setData(data);
}
boolean timeout = true;
CommonTopicResponse data;
private PublishBarrierResult() {
}
private PublishBarrierResult setData(CommonTopicResponse data) {
this.data = data;
this.timeout = false;
return this;
}
public boolean isTimeout(){
return timeout;
}
public CommonTopicResponse getData(){
return data;
}
}

88
src/main/java/com/dji/sdk/common/PublishConfiguration.java

@ -0,0 +1,88 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月22日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.common;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class PublishConfiguration implements ReadonlyPublishConfiguration {
String bid;
String tid;
//默认超时
int timeout = 3;
//请求发送前调用
Consumer<PublishRequest> beforePublishHook = null;
//收到请求回信后调用
BiConsumer<PublishRequest, PublishBarrierResult> afterPublishHook = null;
public String getBid() {
return bid;
}
public String getTid() {
return tid;
}
public long getTimeout() {
return timeout * 1000;
}
public void setBizId(String bid) {
this.bid = bid;
}
public void setTransactionId(String tid) {
this.tid = tid;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public void setBeforePublishHook(Consumer<PublishRequest> callback) {
beforePublishHook = callback;
}
public void setAfterPublishReplyHook(BiConsumer<PublishRequest, PublishBarrierResult> callback) {
afterPublishHook = callback;
}
public void invokeBeforePublishHook(PublishRequest req){
if(Objects.nonNull(beforePublishHook)){
try {
beforePublishHook.accept(req);
}catch (Throwable ex){
//do nothing
//业务层的异常不理会
}
}
}
public void invokeAfterPublishReplyHook(PublishRequest req, PublishBarrierResult result){
if(Objects.nonNull(afterPublishHook)){
try{
afterPublishHook.accept(req,result);
}catch (Throwable ex){
//do nothing
//业务层的异常不理会
}
}
}
public boolean noneBeforePublishHook() {
return Objects.isNull(beforePublishHook);
}
public boolean noneAfterPublishHook() {
return Objects.isNull(afterPublishHook);
}
}

57
src/main/java/com/dji/sdk/common/PublishOption.java

@ -0,0 +1,57 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月22日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.common;
import com.google.common.base.Strings;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class PublishOption {
public static Consumer<PublishOption> DEFAULT = (cfg)->{};
final PublishConfiguration configuration;
public PublishOption(PublishConfiguration configuration) {
this.configuration = configuration;
}
public PublishOption withBizId(String bid){
if(!Strings.isNullOrEmpty(bid)){
configuration.setBizId(bid);
}
return this;
}
public PublishOption withTransactionId(String tid){
if(!Strings.isNullOrEmpty(tid)){
configuration.setTransactionId(tid);
}
return this;
}
public PublishOption timeout(int second){
configuration.setTimeout(second);
return this;
}
public PublishOption beforePublish(Consumer<PublishRequest> callback){
if(Objects.nonNull(callback)){
configuration.setBeforePublishHook(callback);
}
return this;
}
public PublishOption afterPublishReply(BiConsumer<PublishRequest, PublishBarrierResult> callback){
if(Objects.nonNull(callback)){
configuration.setAfterPublishReplyHook(callback);
}
return this;
}
}

20
src/main/java/com/dji/sdk/common/PublishRequest.java

@ -0,0 +1,20 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月25日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.common;
import com.dji.sdk.common.ReadonlyPublishConfiguration;
import com.dji.sdk.mqtt.CommonTopicRequest;
public interface PublishRequest {
String getTopic();
CommonTopicRequest getOriginRequest();
ReadonlyPublishConfiguration getConfiguration();
}

17
src/main/java/com/dji/sdk/common/PublishResult.java

@ -0,0 +1,17 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月25日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.common;
import com.dji.sdk.mqtt.CommonTopicResponse;
public interface PublishResult {
boolean isTimeout();
CommonTopicResponse getData();
}

17
src/main/java/com/dji/sdk/common/ReadonlyPublishConfiguration.java

@ -0,0 +1,17 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月25日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.common;
public interface ReadonlyPublishConfiguration {
String getBid();
String getTid();
long getTimeout();
}

36
src/main/java/com/dji/sdk/common/SDKManager.java

@ -8,15 +8,39 @@ import com.dji.sdk.exception.CloudSDKErrorEnum;
import com.dji.sdk.exception.CloudSDKException; import com.dji.sdk.exception.CloudSDKException;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* @author sean * @author sean
* @version 1.7 * @version 1.7
* @date 2023/5/19 * @date 2023/5/19
*
* fix: 改接口,由客户决定Gateway管理策略 witcom@2023.09.25
*/ */
public class SDKManager { public interface SDKManager {
default GatewayManager getDeviceSDK(String gatewaySn){
return findDeviceSDK(gatewaySn)
.orElseThrow(()-> new CloudSDKException(CloudSDKErrorEnum.NOT_REGISTERED,
"The device has not been registered, please call the 'SDKManager.registerDevice()' method to register the device first."));
}
Optional<GatewayManager> findDeviceSDK(String gatewaySn);
default 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);
}
default GatewayManager registerDevice(String gatewaySn, String droneSn, GatewayTypeEnum type, String gatewayThingVersion, String droneThingVersion){
return registerDevice(new GatewayManager(Objects.requireNonNull(gatewaySn), droneSn, type, gatewayThingVersion, droneThingVersion));
}
GatewayManager registerDevice(GatewayManager gateway);
void logoutDevice(String gatewaySn);
/*
private SDKManager() { private SDKManager() {
} }
@ -30,6 +54,14 @@ public class SDKManager {
"The device has not been registered, please call the 'SDKManager.registerDevice()' method to register the device first."); "The device has not been registered, please call the 'SDKManager.registerDevice()' method to register the device first.");
} }
public static Optional<GatewayManager> findDeviceSDK(String gatewaySn) {
if(SDK_MAP.containsKey(gatewaySn)){
return Optional.of(SDK_MAP.get(gatewaySn));
}else {
return Optional.empty();
}
}
public static GatewayManager registerDevice(String gatewaySn, String droneSn, public static GatewayManager registerDevice(String gatewaySn, String droneSn,
DeviceDomainEnum domain, DeviceTypeEnum type, DeviceSubTypeEnum subType, String gatewayThingVersion, String droneThingVersion) { DeviceDomainEnum domain, DeviceTypeEnum type, DeviceSubTypeEnum subType, String gatewayThingVersion, String droneThingVersion) {
return registerDevice(gatewaySn, droneSn, GatewayTypeEnum.find(DeviceEnum.find(domain, type, subType)), gatewayThingVersion, droneThingVersion); return registerDevice(gatewaySn, droneSn, GatewayTypeEnum.find(DeviceEnum.find(domain, type, subType)), gatewayThingVersion, droneThingVersion);
@ -47,4 +79,6 @@ public class SDKManager {
public static void logoutDevice(String gatewaySn) { public static void logoutDevice(String gatewaySn) {
SDK_MAP.remove(gatewaySn); SDK_MAP.remove(gatewaySn);
} }
*/
} }

80
src/main/java/com/dji/sdk/config/DefaultBeanConfiguration.java

@ -0,0 +1,80 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月22日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.config;
import com.dji.sdk.common.*;
import com.dji.sdk.mqtt.ChanBarrierAdapter;
import com.dji.sdk.mqtt.GlobalPublishOption;
import com.dji.sdk.common.PublishRequest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
@Configuration
public class DefaultBeanConfiguration {
/**
* 全局发送默认设置
* @return
*/
@Bean
@ConditionalOnMissingBean(GlobalPublishOption.class)
public GlobalPublishOption defaultPublishOption(){
return new GlobalPublishOption() {
@Override
public Supplier<String> defaultTransactionId() {
return ()-> UUID.randomUUID().toString();
}
@Override
public Supplier<String> defaultBizId() {
return ()-> UUID.randomUUID().toString();
}
@Override
public Consumer<PublishRequest> defaultBeforePublishHook() {
return null;
}
@Override
public BiConsumer<PublishRequest, PublishBarrierResult> defaultAfterPublishHook() {
return null;
}
};
}
@Bean
@ConditionalOnMissingBean(SDKManager.class)
public SDKManager localCacheSDKManager(){
return new LocalCacheSDKManager();
}
/**
* 使用者可以自定义PublishBarrier的实现默认采用Chan实现
*/
@Bean
@ConditionalOnMissingBean(PublishBarrier.class)
public PublishBarrier chanBarrier(){
/** 原Chan实现 */
return new ChanBarrierAdapter();
}
/**
* PublishBarrier 另一个实现, 采用同步锁
*/
// @Bean
// @ConditionalOnMissingBean(PublishBarrier.class)
// public PublishBarrier jdkBarrier(){
// return new JDKLockBarrierImpl();
// }
}

53
src/main/java/com/dji/sdk/mqtt/ChanBarrierAdapter.java

@ -0,0 +1,53 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月22日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.mqtt;
import com.dji.sdk.common.PublishBarrier;
import com.dji.sdk.common.PublishBarrierResult;
import java.util.Objects;
public class ChanBarrierAdapter implements PublishBarrier {
@Override
public String generateIdentity(CommonTopicRequest requestData) {
return requestData.getTid();
}
@Override
public String generateIdentity(CommonTopicResponse receiveData) {
return receiveData.getTid();
}
@Override
public void put(String identity, CommonTopicResponse receiveData) {
Chan instance = Chan.getInstance(identity, false);
if(Objects.nonNull(instance)){
instance.put(receiveData);
}
}
@Override
public void registerRequest(String identity, CommonTopicRequest requestData) {
Chan.getInstance(identity, true);
}
@Override
public PublishBarrierResult await(String identity,long timeout) {
Chan instance = Chan.getInstance(identity, false);
CommonTopicResponse response = instance.get(identity, timeout);
return Objects.nonNull(response) ? PublishBarrierResult.ok(response) : PublishBarrierResult.EMPTY;
}
@Override
public boolean hasIdentity(String identity) {
Chan instance = Chan.getInstance(identity, false);
return Objects.nonNull(instance);
}
}

51
src/main/java/com/dji/sdk/mqtt/FlowTransformWrapper.java

@ -0,0 +1,51 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年10月09日
* @version: 1.0.0
* @description:
**************************************************/
package com.dji.sdk.mqtt;
public class FlowTransformWrapper {
public final static String DEFAULT_ERROR_MSG = "null";
public static FlowTransformWrapper error(){
return new FlowTransformWrapper(DEFAULT_ERROR_MSG);
}
public static FlowTransformWrapper ok(CommonTopicRequest request){
return new FlowTransformWrapper(request);
}
CommonTopicRequest request;
boolean bError;
String errorMessage;
private FlowTransformWrapper(CommonTopicRequest request){
this.request = request;
this.bError = false;
}
private FlowTransformWrapper(String errorMessage){
this.bError = true;
this.errorMessage = errorMessage;
}
public CommonTopicRequest getRequest() {
return request;
}
public boolean hasError() {
return bError;
}
public boolean continuee(){
return !hasError();
}
public String getErrorMessage() {
return errorMessage;
}
}

24
src/main/java/com/dji/sdk/mqtt/GlobalPublishOption.java

@ -0,0 +1,24 @@
/*************************************************
* @copyright 2017 Flision Corporation Inc.
* @author: Vincent Chan @ Canton
* @date: 2023年09月25日
* @version: 1.0.0
* @description: 全局发送默认配置
**************************************************/
package com.dji.sdk.mqtt;
import com.dji.sdk.common.PublishBarrierResult;
import com.dji.sdk.common.PublishRequest;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public interface GlobalPublishOption {
Supplier<String> defaultTransactionId();
Supplier<String> defaultBizId();
Consumer<PublishRequest> defaultBeforePublishHook();
BiConsumer<PublishRequest, PublishBarrierResult> defaultAfterPublishHook();
}

5
src/main/java/com/dji/sdk/mqtt/InboundMessageRouter.java

@ -37,7 +37,10 @@ public class InboundMessageRouter extends AbstractMessageRouter {
String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString();
byte[] payload = (byte[])message.getPayload(); byte[] payload = (byte[])message.getPayload();
log.debug("received topic: {} \t payload =>{}", topic, new String(payload)); //fix: 修复未启动debug时仍然需要构造debug参数的问题 by witcom@2023.09.22
if(log.isDebugEnabled()) {
log.debug("received topic: {} \t payload =>{}", topic, new String(payload));
}
CloudApiTopicEnum topicEnum = CloudApiTopicEnum.find(topic); CloudApiTopicEnum topicEnum = CloudApiTopicEnum.find(topic);
MessageChannel bean = (MessageChannel) SpringBeanUtils.getBean(topicEnum.getBeanName()); MessageChannel bean = (MessageChannel) SpringBeanUtils.getBean(topicEnum.getBeanName());

104
src/main/java/com/dji/sdk/mqtt/MqttGatewayPublish.java

@ -1,9 +1,10 @@
package com.dji.sdk.mqtt; package com.dji.sdk.mqtt;
import com.dji.sdk.common.Common; import com.dji.sdk.common.*;
import com.dji.sdk.exception.CloudSDKErrorEnum; import com.dji.sdk.exception.CloudSDKErrorEnum;
import com.dji.sdk.exception.CloudSDKException; import com.dji.sdk.exception.CloudSDKException;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException; import org.springframework.beans.TypeMismatchException;
import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.integration.mqtt.support.MqttHeaders;
@ -14,7 +15,9 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/** /**
* @author sean.zhou * @author sean.zhou
@ -32,9 +35,17 @@ public class MqttGatewayPublish {
@Resource @Resource
private IMqttMessageGateway messageGateway; private IMqttMessageGateway messageGateway;
@Resource
private PublishBarrier publishBarrier;
@Resource
private GlobalPublishOption globalOptions;
public void publish(String topic, int qos, CommonTopicRequest request) { public void publish(String topic, int qos, CommonTopicRequest request) {
try { try {
log.debug("send topic: {}, payload: {}", topic, request.toString()); if(log.isDebugEnabled()) {
log.debug("send topic: {}, payload: {}", topic, request.toString());
}
byte[] payload = Common.getObjectMapper().writeValueAsBytes(request); byte[] payload = Common.getObjectMapper().writeValueAsBytes(request);
messageGateway.publish(topic, payload, qos); messageGateway.publish(topic, payload, qos);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
@ -45,7 +56,9 @@ public class MqttGatewayPublish {
public void publish(String topic, int qos, CommonTopicResponse response) { public void publish(String topic, int qos, CommonTopicResponse response) {
try { try {
log.debug("send topic: {}, payload: {}", topic, response.toString()); if(log.isDebugEnabled()) {
log.debug("send topic: {}, payload: {}", topic, response.toString());
}
byte[] payload = Common.getObjectMapper().writeValueAsBytes(response); byte[] payload = Common.getObjectMapper().writeValueAsBytes(response);
messageGateway.publish(topic, payload, qos); messageGateway.publish(topic, payload, qos);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
@ -73,7 +86,9 @@ public class MqttGatewayPublish {
AtomicInteger time = new AtomicInteger(0); AtomicInteger time = new AtomicInteger(0);
boolean hasBid = StringUtils.hasText(request.getBid()); boolean hasBid = StringUtils.hasText(request.getBid());
request.setBid(hasBid ? request.getBid() : UUID.randomUUID().toString()); request.setBid(hasBid ? request.getBid() : UUID.randomUUID().toString());
// Retry // Retry
// Is Retry necessary? why not use Spring @Retryable instead?
while (time.getAndIncrement() <= retryCount) { while (time.getAndIncrement() <= retryCount) {
this.publish(topic, request); this.publish(topic, request);
@ -97,5 +112,88 @@ public class MqttGatewayPublish {
throw new CloudSDKException(CloudSDKErrorEnum.MQTT_PUBLISH_ABNORMAL, "No message reply received."); throw new CloudSDKException(CloudSDKErrorEnum.MQTT_PUBLISH_ABNORMAL, "No message reply received.");
} }
public <T> CompletableFuture<CommonTopicResponse<T>> publishWithReply(Class<T> clazz, String topic, CommonTopicRequest request, Consumer<PublishOption> options){
PublishConfiguration config = prepareConfiguration(options);
request.setBid(config.getBid());
request.setTid(config.getTid());
//use to log request data or the last chance to change some data
//CommonTopicRequest丢失了一些需要记录的内容,把这些内容封到PublishRequest交出去
PublishRequest wrapRequest = new CommonTopicRequestWrapper<T>(clazz,topic, request, config);
config.invokeBeforePublishHook(wrapRequest);
//注册barrier
String identity = publishBarrier.generateIdentity(request); //提供栅栏标识
publishBarrier.registerRequest(identity, request);
return CompletableFuture.supplyAsync(()->{
this.publish(topic, request);
if(log.isDebugEnabled()){ log.debug("等待{}指令返回",identity); };
PublishBarrierResult result = publishBarrier.await(identity,config.getTimeout());
config.invokeAfterPublishReplyHook(wrapRequest, result);
if(result.isTimeout()){
throw new CloudSDKException("Timeout"); //TODO: 换个更明确的异常更好
}
if(log.isDebugEnabled()){ log.debug("{}指令已返回",identity); }
return result.getData();
});
}
private PublishConfiguration prepareConfiguration(Consumer<PublishOption> options){
PublishConfiguration config = new PublishConfiguration();
PublishOption option = new PublishOption(config);
options.accept(option);
if(Strings.isNullOrEmpty(config.getBid())){
config.setBizId(globalOptions.defaultBizId().get());
}
if(Strings.isNullOrEmpty(config.getTid())){
config.setTransactionId(globalOptions.defaultTransactionId().get());
}
if(config.noneBeforePublishHook()){
config.setBeforePublishHook(globalOptions.defaultBeforePublishHook());
}
if(config.noneAfterPublishHook()){
config.setAfterPublishReplyHook(globalOptions.defaultAfterPublishHook());
}
return config;
}
static class CommonTopicRequestWrapper<T> implements PublishRequest{
final CommonTopicRequest request;
final String topic;
final Class<T> clazz;
final ReadonlyPublishConfiguration config;
public CommonTopicRequestWrapper(Class<T> clazz, String topic,CommonTopicRequest request, PublishConfiguration config) {
this.clazz = clazz;
this.request = request;
this.topic = topic;
this.config = config;
}
@Override
public String getTopic() {
return topic;
}
@Override
public CommonTopicRequest getOriginRequest() {
return request;
}
@Override
public ReadonlyPublishConfiguration getConfiguration() {
return config;
}
}
} }

39
src/main/java/com/dji/sdk/mqtt/osd/OsdRouter.java

@ -14,11 +14,9 @@ import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static com.dji.sdk.mqtt.TopicConst.*; import static com.dji.sdk.mqtt.TopicConst.*;
@ -32,7 +30,7 @@ import static com.dji.sdk.mqtt.TopicConst.*;
public class OsdRouter { public class OsdRouter {
@Bean @Bean
public IntegrationFlow osdRouterFlow() { public IntegrationFlow osdRouterFlow(SDKManager sdkManager) {
return IntegrationFlows return IntegrationFlows
.from(ChannelName.INBOUND_OSD) .from(ChannelName.INBOUND_OSD)
.transform(Message.class, source -> { .transform(Message.class, source -> {
@ -45,19 +43,28 @@ public class OsdRouter {
} }
}, null) }, null)
.<TopicOsdRequest>handle((response, headers) -> { .<TopicOsdRequest>handle((response, headers) -> {
GatewayManager gateway = SDKManager.getDeviceSDK(response.getGateway());
OsdDeviceTypeEnum typeEnum = OsdDeviceTypeEnum.find(gateway.getType(), response.getFrom().equals(response.getGateway())); // fix: getDeviceSDK抛出异常导致在设备未注册的情况下报osd时产生大量日志 witcom@2023.09.22
Map<String, Object> data = (Map<String, Object>) response.getData(); //GatewayManager gateway = SDKManager.getDeviceSDK(response.getGateway());
if (!typeEnum.isGateway()) { return sdkManager.findDeviceSDK(response.getGateway())
List payloadData = (List) data.getOrDefault(PayloadModelEnum.PAYLOAD_KEY, new ArrayList<>()); .map(gateway-> {
PayloadModelEnum.getAllIndexWithPosition().stream().filter(data::containsKey)
.map(data::get).forEach(payloadData::add); OsdDeviceTypeEnum typeEnum = OsdDeviceTypeEnum.find(gateway.getType(), response.getFrom().equals(response.getGateway()));
data.put(PayloadModelEnum.PAYLOAD_KEY, payloadData); Map<String, Object> data = (Map<String, Object>) response.getData();
} if (!typeEnum.isGateway()) {
return response.setData(Common.getObjectMapper().convertValue(data, typeEnum.getClassType())); 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()));
})
.orElse(null);
}) })
.<TopicOsdRequest>filter(Objects::nonNull)
.<TopicOsdRequest, OsdDeviceTypeEnum>route(response -> OsdDeviceTypeEnum.find(response.getData().getClass()), .<TopicOsdRequest, OsdDeviceTypeEnum>route(response -> OsdDeviceTypeEnum.find(response.getData().getClass()),
mapping -> Arrays.stream(OsdDeviceTypeEnum.values()).forEach(key -> mapping.channelMapping(key, key.getChannelName()))) mapping -> Arrays.stream(OsdDeviceTypeEnum.values())
.forEach(key -> mapping.channelMapping(key, key.getChannelName())))
.get(); .get();
} }

7
src/main/java/com/dji/sdk/mqtt/osd/OsdSubscribe.java

@ -22,8 +22,11 @@ public class OsdSubscribe {
@Resource @Resource
private IMqttTopicService topicService; private IMqttTopicService topicService;
@Resource
SDKManager sdkManager;
public void subscribe(GatewayManager gateway, boolean unsubscribeSubDevice) { public void subscribe(GatewayManager gateway, boolean unsubscribeSubDevice) {
SDKManager.registerDevice(gateway); sdkManager.registerDevice(gateway);
topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn()));
if (unsubscribeSubDevice) { if (unsubscribeSubDevice) {
topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn()));
@ -35,7 +38,7 @@ public class OsdSubscribe {
} }
public void unsubscribe(GatewayManager gateway) { public void unsubscribe(GatewayManager gateway) {
SDKManager.logoutDevice(gateway.getGatewaySn()); sdkManager.logoutDevice(gateway.getGatewaySn());
topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn()));
if (null != gateway.getDroneSn()) { if (null != gateway.getDroneSn()) {
topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn()));

34
src/main/java/com/dji/sdk/mqtt/property/PropertySetPublish.java

@ -1,5 +1,8 @@
package com.dji.sdk.mqtt.property; package com.dji.sdk.mqtt.property;
import com.dji.sdk.common.PublishOption;
import com.dji.sdk.mqtt.CommonTopicRequest;
import com.dji.sdk.mqtt.CommonTopicResponse;
import com.dji.sdk.mqtt.MqttGatewayPublish; import com.dji.sdk.mqtt.MqttGatewayPublish;
import com.dji.sdk.mqtt.TopicConst; import com.dji.sdk.mqtt.TopicConst;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -7,6 +10,8 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
/** /**
* @author sean * @author sean
@ -29,12 +34,33 @@ public class PropertySetPublish {
public PropertySetReplyResultEnum publish(String sn, Object data, int retryCount, long 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; String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + Objects.requireNonNull(sn) + TopicConst.PROPERTY_SUF + TopicConst.SET_SUF;
return gatewayPublish.publishWithReply(PropertySetReplyResultEnum.class,topic,
new CommonTopicRequest<>()
.setTimestamp(System.currentTimeMillis())
.setData(Objects.requireNonNull(data)),
ops->ops.timeout((int)(timeout/1000)))
.join()
.getData();
// return gatewayPublish.publishWithReply(
// PropertySetReplyResultEnum.class, topic, new TopicPropertySetRequest<>()
// .setTid(UUID.randomUUID().toString())
// .setBid(null)
// .setTimestamp(System.currentTimeMillis())
// .setData(Objects.requireNonNull(data)), retryCount, timeout).getData();
}
public CompletableFuture<CommonTopicResponse<PropertySetReplyResultEnum>>
publish(String sn, Object data, Consumer<PublishOption> options){
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + Objects.requireNonNull(sn) + TopicConst.PROPERTY_SUF + TopicConst.SET_SUF;
return gatewayPublish.publishWithReply( return gatewayPublish.publishWithReply(
PropertySetReplyResultEnum.class, topic, new TopicPropertySetRequest<>() PropertySetReplyResultEnum.class,
.setTid(UUID.randomUUID().toString()) topic,
.setBid(null) new CommonTopicRequest<>()
.setTimestamp(System.currentTimeMillis()) .setTimestamp(System.currentTimeMillis())
.setData(Objects.requireNonNull(data)), retryCount, timeout).getData(); .setData(Objects.requireNonNull(data)),
options);
} }
} }

17
src/main/java/com/dji/sdk/mqtt/property/PropertySetReplyHandler.java

@ -1,6 +1,7 @@
package com.dji.sdk.mqtt.property; package com.dji.sdk.mqtt.property;
import com.dji.sdk.common.Common; import com.dji.sdk.common.Common;
import com.dji.sdk.common.PublishBarrier;
import com.dji.sdk.mqtt.Chan; import com.dji.sdk.mqtt.Chan;
import com.dji.sdk.mqtt.ChannelName; import com.dji.sdk.mqtt.ChannelName;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
@ -9,6 +10,7 @@ import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
@ -20,6 +22,9 @@ import java.util.Objects;
@Component @Component
public class PropertySetReplyHandler { public class PropertySetReplyHandler {
@Resource
PublishBarrier barrier;
private static final String RESULT_KEY = "result"; private static final String RESULT_KEY = "result";
/** /**
@ -32,13 +37,19 @@ public class PropertySetReplyHandler {
byte[] payload = (byte[])message.getPayload(); byte[] payload = (byte[])message.getPayload();
TopicPropertySetResponse receiver = Common.getObjectMapper().readValue(payload, new TypeReference<TopicPropertySetResponse>() {}); TopicPropertySetResponse receiver = Common.getObjectMapper().readValue(payload, new TypeReference<TopicPropertySetResponse>() {});
Chan chan = Chan.getInstance(receiver.getTid(), false); //fix: use Barrier instead witcom@2023.09.22
if (Objects.isNull(chan)) { //Chan chan = Chan.getInstance(receiver.getTid(), false);
// if (Objects.isNull(chan)) {
// return;
// }
String identity = barrier.generateIdentity(receiver);
if(!barrier.hasIdentity(identity)) {
return; return;
} }
receiver.setData(PropertySetReplyResultEnum.find( receiver.setData(PropertySetReplyResultEnum.find(
Common.getObjectMapper().convertValue(receiver.getData(), JsonNode.class).findValue(RESULT_KEY).intValue())); Common.getObjectMapper().convertValue(receiver.getData(), JsonNode.class).findValue(RESULT_KEY).intValue()));
// Put the message to the chan object. // Put the message to the chan object.
chan.put(receiver); //chan.put(receiver);
barrier.put(identity, receiver);
} }
} }

44
src/main/java/com/dji/sdk/mqtt/services/ServicesPublish.java

@ -1,6 +1,8 @@
package com.dji.sdk.mqtt.services; package com.dji.sdk.mqtt.services;
import com.dji.sdk.common.Common; import com.dji.sdk.common.Common;
import com.dji.sdk.common.PublishOption;
import com.dji.sdk.mqtt.CommonTopicResponse;
import com.dji.sdk.mqtt.MqttGatewayPublish; import com.dji.sdk.mqtt.MqttGatewayPublish;
import com.dji.sdk.mqtt.TopicConst; import com.dji.sdk.mqtt.TopicConst;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
@ -10,6 +12,8 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
/** /**
* @author sean * @author sean
@ -60,6 +64,8 @@ public class ServicesPublish {
public <T> TopicServicesResponse<ServicesReplyData<T>> publish( public <T> TopicServicesResponse<ServicesReplyData<T>> publish(
TypeReference<T> clazz, String sn, String method, Object data, String bid, int retryCount, long timeout) { TypeReference<T> clazz, String sn, String method, Object data, String bid, int retryCount, long timeout) {
return this.publish(clazz, sn, method,data,ops-> ops.withBizId(bid).timeout((int)(timeout / 1000))).join();
/*
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + Objects.requireNonNull(sn) + TopicConst.SERVICES_SUF; String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + Objects.requireNonNull(sn) + TopicConst.SERVICES_SUF;
TopicServicesResponse response = (TopicServicesResponse) gatewayPublish.publishWithReply( TopicServicesResponse response = (TopicServicesResponse) gatewayPublish.publishWithReply(
ServicesReplyReceiver.class, topic, new TopicServicesRequest<>() ServicesReplyReceiver.class, topic, new TopicServicesRequest<>()
@ -84,6 +90,44 @@ public class ServicesPublish {
reply.setOutput(mapper.convertValue(replyReceiver.getOutput(), clazz)); reply.setOutput(mapper.convertValue(replyReceiver.getOutput(), clazz));
} }
return response.setData(reply); return response.setData(reply);
*/
} }
/**
* Remark by witcom@2023.09.22
* TODO: 在贡献的版本到这里就不再往下修改了,需要修改所有AbstractXXXService和AOP改动量很大
* 主要思想是
* 1.提供异步支持
* 2.Chan的实现可由用户自定义
* 3.提供发送选项参数
* 4.提供发送前接收后钩子用于记录请求和返回接近最原始的记录
*/
public <T> CompletableFuture<TopicServicesResponse<ServicesReplyData<T>>> publish(TypeReference<T> clazz, String sn, String method, Object data, Consumer<PublishOption> options){
String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + Objects.requireNonNull(sn) + TopicConst.SERVICES_SUF;
return gatewayPublish.publishWithReply(ServicesReplyReceiver.class, topic, new TopicServicesRequest<>()
.setTimestamp(System.currentTimeMillis())
.setMethod(method)
.setData(Objects.requireNonNullElse(data, "")), options)
.thenApply(response->(TopicServicesResponse)response)
.thenApply(response->{
ServicesReplyReceiver replyReceiver = (ServicesReplyReceiver) response.getData();
ServicesReplyData<T> reply = new ServicesReplyData<T>().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);
});
}
} }

17
src/main/java/com/dji/sdk/mqtt/services/ServicesReplyHandler.java

@ -3,6 +3,7 @@ package com.dji.sdk.mqtt.services;
import com.dji.sdk.cloudapi.log.FileUploadListResponse; import com.dji.sdk.cloudapi.log.FileUploadListResponse;
import com.dji.sdk.cloudapi.log.LogMethodEnum; import com.dji.sdk.cloudapi.log.LogMethodEnum;
import com.dji.sdk.common.Common; import com.dji.sdk.common.Common;
import com.dji.sdk.common.PublishBarrier;
import com.dji.sdk.mqtt.Chan; import com.dji.sdk.mqtt.Chan;
import com.dji.sdk.mqtt.ChannelName; import com.dji.sdk.mqtt.ChannelName;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
@ -10,6 +11,7 @@ import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
@ -21,6 +23,9 @@ import java.util.Objects;
@Component @Component
public class ServicesReplyHandler { public class ServicesReplyHandler {
@Resource
PublishBarrier barrier;
/** /**
* Handle the reply message from topic "/services_reply". * Handle the reply message from topic "/services_reply".
* @param message reply message * @param message reply message
@ -32,14 +37,20 @@ public class ServicesReplyHandler {
TopicServicesResponse<ServicesReplyReceiver> receiver = Common.getObjectMapper() TopicServicesResponse<ServicesReplyReceiver> receiver = Common.getObjectMapper()
.readValue(payload, new TypeReference<TopicServicesResponse<ServicesReplyReceiver>>() {}); .readValue(payload, new TypeReference<TopicServicesResponse<ServicesReplyReceiver>>() {});
Chan chan = Chan.getInstance(receiver.getTid(), false); //fix: use Barrier instead witcom@2023.09.22
if (Objects.isNull(chan)) { // Chan chan = Chan.getInstance(receiver.getTid(), false);
// if (Objects.isNull(chan)) {
// return;
// }
String identity = barrier.generateIdentity(receiver);
if(!barrier.hasIdentity(identity)){
return; return;
} }
if (LogMethodEnum.FILE_UPLOAD_LIST.getMethod().equals(receiver.getMethod())) { if (LogMethodEnum.FILE_UPLOAD_LIST.getMethod().equals(receiver.getMethod())) {
receiver.getData().setOutput(Common.getObjectMapper().convertValue(receiver.getData(), receiver.getData().setOutput(Common.getObjectMapper().convertValue(receiver.getData(),
new TypeReference<FileUploadListResponse>() {})); new TypeReference<FileUploadListResponse>() {}));
} }
chan.put(receiver); barrier.put(identity, receiver);
//chan.put(receiver);
} }
} }

8
src/main/java/com/dji/sdk/mqtt/state/DockStateDataKeyEnum.java

@ -6,6 +6,7 @@ import com.dji.sdk.exception.CloudSDKException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Optional;
import java.util.Set; import java.util.Set;
/** /**
@ -47,9 +48,10 @@ public enum DockStateDataKeyEnum {
return keys; return keys;
} }
public static DockStateDataKeyEnum find(Set<String> keys) { public static Optional<DockStateDataKeyEnum> find(Set<String> keys) {
return Arrays.stream(values()).filter(keyEnum -> !Collections.disjoint(keys, keyEnum.keys)).findAny() // com.dji.sdk.mqtt.state.DockStateDataKeyEnum has unknown data: [[mode_code_reason]]
.orElseThrow(() -> new CloudSDKException(DockStateDataKeyEnum.class, keys)); return Arrays.stream(values()).filter(keyEnum -> !Collections.disjoint(keys, keyEnum.keys)).findAny();
// .orElseThrow(() -> new CloudSDKException(DockStateDataKeyEnum.class, keys));
} }
} }

7
src/main/java/com/dji/sdk/mqtt/state/RcStateDataKeyEnum.java

@ -6,6 +6,7 @@ import com.dji.sdk.exception.CloudSDKException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Optional;
import java.util.Set; import java.util.Set;
/** /**
@ -45,9 +46,9 @@ public enum RcStateDataKeyEnum {
return keys; return keys;
} }
public static RcStateDataKeyEnum find(Set<String> keys) { public static Optional<RcStateDataKeyEnum> find(Set<String> keys) {
return Arrays.stream(values()).filter(keyEnum -> !Collections.disjoint(keys, keyEnum.keys)).findAny() return Arrays.stream(values()).filter(keyEnum -> !Collections.disjoint(keys, keyEnum.keys)).findAny();
.orElseThrow(() -> new CloudSDKException(RcStateDataKeyEnum.class, keys)); //.orElseThrow(() -> new CloudSDKException(RcStateDataKeyEnum.class, keys));
} }
} }

70
src/main/java/com/dji/sdk/mqtt/state/StateRouter.java

@ -1,11 +1,15 @@
package com.dji.sdk.mqtt.state; package com.dji.sdk.mqtt.state;
import com.dji.sdk.common.Common; import com.dji.sdk.common.Common;
import com.dji.sdk.common.GatewayTypeEnum;
import com.dji.sdk.common.SDKManager; import com.dji.sdk.common.SDKManager;
import com.dji.sdk.exception.CloudSDKErrorEnum; import com.dji.sdk.exception.CloudSDKErrorEnum;
import com.dji.sdk.exception.CloudSDKException; import com.dji.sdk.exception.CloudSDKException;
import com.dji.sdk.mqtt.ChannelName; import com.dji.sdk.mqtt.ChannelName;
import com.dji.sdk.mqtt.FlowTransformWrapper;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.integration.dsl.IntegrationFlow; import org.springframework.integration.dsl.IntegrationFlow;
@ -13,51 +17,79 @@ import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.*;
import java.util.Map;
import java.util.Set;
import static com.dji.sdk.mqtt.TopicConst.*; import static com.dji.sdk.mqtt.TopicConst.*;
/** /**
*
* @author sean.zhou * @author sean.zhou
* @date 2021/11/17
* @version 0.1 * @version 0.1
* @date 2021/11/17
*/ */
@Slf4j
@Configuration @Configuration
public class StateRouter { public class StateRouter {
@Resource
SDKManager sdkManager;
@Bean @Bean
public IntegrationFlow stateDataRouterFlow() { public IntegrationFlow stateDataRouterFlow() {
ObjectMapper objectMapper = Common.getObjectMapper();
return IntegrationFlows return IntegrationFlows
.from(ChannelName.INBOUND_STATE) .from(ChannelName.INBOUND_STATE)
.transform(Message.class, source -> { .transform(Message.class, source -> {
try { try {
TopicStateRequest response = Common.getObjectMapper().readValue((byte[]) source.getPayload(), new TypeReference<TopicStateRequest>() {}); TopicStateRequest response = objectMapper.readValue(
(byte[]) source.getPayload(),
new TypeReference<TopicStateRequest>() {});
String topic = String.valueOf(source.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); String topic = String.valueOf(source.getHeaders().get(MqttHeaders.RECEIVED_TOPIC));
String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), topic.indexOf(STATE_SUF)); 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()))); return FlowTransformWrapper.ok(response.setFrom(from));
} catch (IOException e) { } catch (Exception ex) {
throw new CloudSDKException(e); log.warn("[StateRouter]"+ex.getMessage());
return FlowTransformWrapper.error();
} }
}, null) }, null)
.filter(FlowTransformWrapper::continuee)
.<FlowTransformWrapper>handle((wrapper, headers) -> {
TopicStateRequest response = (TopicStateRequest)wrapper.getRequest();
//fix: 修复设备未注册前设备推送state导致产生大量日志的问题 witcom@2023.10.08
try {
return getTypeReference(response.getGateway(), response.getData())
.map(clazz -> response.setData(objectMapper.convertValue(response.getData(), clazz)))
.orElse(null);
}catch (CloudSDKException ex){
log.warn("[StateRouter]"+ex.getMessage());
return null;
}
})
.filter(Objects::nonNull)
.<TopicStateRequest, StateDataKeyEnum>route(response -> StateDataKeyEnum.find(response.getData().getClass()), .<TopicStateRequest, StateDataKeyEnum>route(response -> StateDataKeyEnum.find(response.getData().getClass()),
mapping -> Arrays.stream(StateDataKeyEnum.values()).forEach(key -> mapping.channelMapping(key, key.getChannelName()))) mapping -> Arrays.stream(StateDataKeyEnum.values()).forEach(key -> mapping.channelMapping(key, key.getChannelName())))
.get(); .get();
} }
private Class getTypeReference(String gatewaySn, Object data) { private Optional<Class> getTypeReference(String gatewaySn, Object data) {
Set<String> keys = ((Map<String, Object>) data).keySet(); Set<String> keys = ((Map<String, Object>) data).keySet();
switch (SDKManager.getDeviceSDK(gatewaySn).getType()) { //fix: 捕捉数据流发现在注册前设备可能会推送state主题导致产生大量日志 witcom@2023.10.08
case RC: //GatewayTypeEnum type = sdkManager.getDeviceSDK(gatewaySn).getType();
return RcStateDataKeyEnum.find(keys).getClassType(); return sdkManager.findDeviceSDK(gatewaySn)
case DOCK: .flatMap(gw -> {
return DockStateDataKeyEnum.find(keys).getClassType(); GatewayTypeEnum type = gw.getType();
default: switch (type) {
throw new CloudSDKException(CloudSDKErrorEnum.WRONG_DATA, "Unexpected value: " + SDKManager.getDeviceSDK(gatewaySn).getType()); case RC:
} return RcStateDataKeyEnum.find(keys).map(v->v.getClassType());
case DOCK:
return DockStateDataKeyEnum.find(keys).map(v->v.getClassType());
default:
throw new CloudSDKException(CloudSDKErrorEnum.WRONG_DATA, "Unexpected value: " + type);
}
});
} }
} }

7
src/main/java/com/dji/sdk/mqtt/state/StateSubscribe.java

@ -20,10 +20,13 @@ public class StateSubscribe {
@Resource @Resource
private IMqttTopicService topicService; private IMqttTopicService topicService;
@Resource
SDKManager sdkManager;
public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.STATE_SUF; public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + "%s" + TopicConst.STATE_SUF;
public void subscribe(GatewayManager gateway, boolean unsubscribeSubDevice) { public void subscribe(GatewayManager gateway, boolean unsubscribeSubDevice) {
SDKManager.registerDevice(gateway); sdkManager.registerDevice(gateway);
topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn()));
if (unsubscribeSubDevice) { if (unsubscribeSubDevice) {
topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn()));
@ -35,7 +38,7 @@ public class StateSubscribe {
} }
public void unsubscribe(GatewayManager gateway) { public void unsubscribe(GatewayManager gateway) {
SDKManager.logoutDevice(gateway.getGatewaySn()); sdkManager.logoutDevice(gateway.getGatewaySn());
topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn()));
if (null != gateway.getDroneSn()) { if (null != gateway.getDroneSn()) {
topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn())); topicService.unsubscribe(String.format(TOPIC, gateway.getDroneSn()));

7
src/main/java/com/dji/sdk/mqtt/status/StatusSubscribe.java

@ -22,8 +22,11 @@ public class StatusSubscribe {
@Resource @Resource
private IMqttTopicService topicService; private IMqttTopicService topicService;
@Resource
SDKManager sdkManager;
public void subscribe(GatewayManager gateway) { public void subscribe(GatewayManager gateway) {
SDKManager.registerDevice(gateway); sdkManager.registerDevice(gateway);
topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn())); topicService.subscribe(String.format(TOPIC, gateway.getGatewaySn()));
} }
@ -32,7 +35,7 @@ public class StatusSubscribe {
} }
public void unsubscribe(GatewayManager gateway) { public void unsubscribe(GatewayManager gateway) {
SDKManager.logoutDevice(gateway.getGatewaySn()); sdkManager.logoutDevice(gateway.getGatewaySn());
topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn())); topicService.unsubscribe(String.format(TOPIC, gateway.getGatewaySn()));
} }

Loading…
Cancel
Save