What's new?
1. Add license for dock.
2. Modify the logic corresponding to the firmware file and device type.
3. Add multiple mqtt clients options.
4. Modify the structure of the interface for obtaining the device list.
5. Fixed some issues.
44 files modified
5 files renamed
2 files deleted
10 files added
| | |
| | | `file_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The file_id in the logs_file table.', |
| | | `start_time` bigint NOT NULL COMMENT 'The file start time reported by the dock.', |
| | | `end_time` bigint NOT NULL COMMENT 'The file end time reported by the dock.', |
| | | `size` int NOT NULL COMMENT 'The file size reported by the dock.', |
| | | `size` bigint NOT NULL COMMENT 'The file size reported by the dock.', |
| | | `device_sn` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The sn of the device.', |
| | | `domain` int NOT NULL COMMENT 'This parameter corresponds to the domain in the device dictionary table.', |
| | | `create_time` bigint NOT NULL, |
| | |
| | | (20,0,77,1,'Mavic 3T',NULL), |
| | | (21,1,66,0,'Mavic 3E Camera',NULL), |
| | | (22,1,67,0,'Mavic 3T Camera',NULL), |
| | | (23,2,144,0,'DJI RC Pro','Remote control for Mavic 3E/T'); |
| | | (23,2,144,0,'DJI RC Pro','Remote control for Mavic 3E/T and Mavic 3M'), |
| | | (24,0,77,2,'Mavic 3M',NULL), |
| | | (25,1,68,0,'Mavic 3M Camera',NULL); |
| | | |
| | | /*!40000 ALTER TABLE `manage_device_dictionary` ENABLE KEYS */; |
| | | UNLOCK TABLES; |
| | |
| | | `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_md5` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The md5 of the firmware package.', |
| | | `device_name` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'model of the device. This parameter corresponds to the device name in the device dictionary table.', |
| | | `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.', |
| | | `release_date` bigint NOT NULL COMMENT 'The release date of the firmware package.', |
| | |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `payload_sn_UNIQUE` (`payload_sn`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='The payload information of the device.'; |
| | | |
| | | |
| | | |
| | | # manage_firmware_model |
| | | # ------------------------------------------------------------ |
| | | |
| | | DROP TABLE IF EXISTS `manage_firmware_model`; |
| | | |
| | | CREATE TABLE `manage_firmware_model` ( |
| | | `id` bigint unsigned NOT NULL AUTO_INCREMENT, |
| | | `firmware_id` varchar(64) NOT NULL, |
| | | `device_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'model of the device. This parameter corresponds to the device name in the device dictionary table.', |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | PRIMARY KEY (`id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; |
| | | |
| | | |
| | | |
| | |
| | | `workspace_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Which workspace the current job belongs to.', |
| | | `task_type` int NOT NULL, |
| | | `wayline_type` int NOT NULL COMMENT 'The template type of the wayline.', |
| | | `execute_time` bigint NOT NULL, |
| | | `execute_time` bigint DEFAULT NULL COMMENT 'actual begin time', |
| | | `completed_time` bigint DEFAULT NULL COMMENT 'actual end time', |
| | | `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The name of the creator.', |
| | | `end_time` bigint DEFAULT NULL COMMENT 'end time of the job.', |
| | | `begin_time` bigint NOT NULL COMMENT 'planned begin time', |
| | | `end_time` bigint NOT NULL COMMENT 'planned end time', |
| | | `error_code` int DEFAULT NULL, |
| | | `status` int NOT NULL COMMENT '1: pending; 2: in progress; 3: success; 4: cancel; 5: failed', |
| | | `rth_altitude` int NOT NULL COMMENT 'return to home altitude. min: 20m; max: 500m', |
| | |
| | | `media_count` int NOT NULL DEFAULT '0', |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | `parent_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `job_id_UNIQUE` (`job_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Wayline mission information of the dock.'; |
| | |
| | | |
| | | ILLEGAL_ARGUMENT(200001, "illegal argument"), |
| | | |
| | | REDIS_DATA_NOT_FOUND(201404, "Redis data does not exist."), |
| | | |
| | | DEVICE_OFFLINE(212015, "Device is offline."), |
| | | |
| | | GET_ORGANIZATION_FAILED(210230, "Failed to get organization."), |
| | | |
| | | DEVICE_BINDING_FAILED(210231, "Failed to bind device."), |
| | |
| | | |
| | | SECRET_INVALID(600100, "secret invalid"), |
| | | |
| | | NO_TOKEN(600101, "accss_token is null"), |
| | | NO_TOKEN(600101, "token is null"), |
| | | |
| | | TOKEN_EXPIRED(600102, "token is expired"), |
| | | |
| | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.Date; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | import java.util.*; |
| | | |
| | | @Slf4j |
| | | @Component |
| | |
| | | |
| | | private static String secret; |
| | | |
| | | private static Algorithm algorithm; |
| | | public static Algorithm algorithm; |
| | | |
| | | @Value("${jwt.issuer: DJI}") |
| | | private void setIssuer(String issuer) { |
| | |
| | | * @param claims custom information |
| | | * @return token |
| | | */ |
| | | public static String createToken(Map<String, String> claims) { |
| | | public static String createToken(Map<String, ?> claims) { |
| | | return JwtUtil.createToken(claims, age, algorithm, subject, issuer); |
| | | } |
| | | |
| | | /** |
| | | * |
| | | * @param claims |
| | | * @param age unit: s |
| | | * @param algorithm |
| | | * @param subject |
| | | * @param issuer |
| | | * @return |
| | | */ |
| | | public static String createToken(Map<String, ?> claims, Long age, Algorithm algorithm, String subject, String issuer) { |
| | | if (Objects.isNull(algorithm)) { |
| | | throw new IllegalArgumentException(); |
| | | } |
| | | |
| | | Date now = new Date(); |
| | | JWTCreator.Builder builder = JWT.create(); |
| | | // Add custom information to the token's payload segment. |
| | | claims.forEach(builder::withClaim); |
| | | String token = builder.withIssuer(issuer) |
| | | .withSubject(subject) |
| | | claims.forEach((k, v) -> { |
| | | if (Objects.nonNull(v.getClass().getClassLoader())) { |
| | | log.error("claim can't be set to a custom object."); |
| | | return; |
| | | } |
| | | if (v instanceof Map) { |
| | | builder.withClaim(k, (Map) v); |
| | | } else if (v instanceof List) { |
| | | builder.withClaim(k, (List) v); |
| | | } else { |
| | | builder.withClaim(k, String.valueOf(v)); |
| | | } |
| | | }); |
| | | |
| | | if (StringUtils.hasText(subject)) { |
| | | builder.withSubject(subject); |
| | | } |
| | | |
| | | if (StringUtils.hasText(issuer)) { |
| | | builder.withIssuer(issuer); |
| | | } |
| | | |
| | | if (Objects.nonNull(age)) { |
| | | builder.withExpiresAt(new Date(now.getTime() + age * 1000)); |
| | | } |
| | | |
| | | String token = builder |
| | | .withIssuedAt(now) |
| | | .withExpiresAt(new Date(now.getTime() + age)) |
| | | .withNotBefore(now) |
| | | .sign(algorithm); |
| | | log.debug("token created. " + token); |
| | |
| | | long expire = RedisOpsUtils.getExpire(key); |
| | | if (expire <= 30) { |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(key); |
| | | if (device.getDomain().equals(DeviceDomainEnum.SUB_DEVICE.getDesc())) { |
| | | if (DeviceDomainEnum.SUB_DEVICE.getVal() == 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[]{key}); |
| | | RedisOpsUtils.del(RedisConst.HMS_PREFIX + key); |
| | | } |
| | | RedisOpsUtils.del(key); |
| | | } |
| | |
| | | package com.dji.sample.component.mqtt.config; |
| | | |
| | | import com.auth0.jwt.algorithms.Algorithm; |
| | | 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 lombok.Data; |
| | | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; |
| | | import org.springframework.integration.mqtt.core.MqttPahoClientFactory; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * |
| | |
| | | */ |
| | | @Configuration |
| | | @Data |
| | | @ConfigurationProperties(prefix = "mqtt") |
| | | @ConfigurationProperties |
| | | public class MqttConfiguration { |
| | | |
| | | private String protocol; |
| | | private static Map<MqttUseEnum, MqttClientOptions> mqtt; |
| | | |
| | | private String host; |
| | | |
| | | private Integer port; |
| | | |
| | | private String username; |
| | | |
| | | private String password; |
| | | |
| | | private String clientId; |
| | | public void setMqtt(Map<MqttUseEnum, MqttClientOptions> mqtt) { |
| | | MqttConfiguration.mqtt = mqtt; |
| | | } |
| | | |
| | | /** |
| | | * The topic to subscribe to immediately when client connects. |
| | | * Get the configuration options of the basic link of the mqtt client. |
| | | * @return |
| | | */ |
| | | private String inboundTopic; |
| | | static MqttClientOptions getBasicClientOptions() { |
| | | if (!mqtt.containsKey(MqttUseEnum.BASIC)) { |
| | | throw new Error("Please configure the basic mqtt connection parameters first, otherwise application cannot be started."); |
| | | } |
| | | return mqtt.get(MqttUseEnum.BASIC); |
| | | } |
| | | |
| | | /** |
| | | * Get the mqtt address of the basic link. |
| | | * @return |
| | | */ |
| | | public static String getBasicMqttAddress() { |
| | | return getMqttAddress(getBasicClientOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Splice the mqtt address according to the parameters of different clients. |
| | | * @param options |
| | | * @return |
| | | */ |
| | | private static String getMqttAddress(MqttClientOptions options) { |
| | | StringBuilder addr = new StringBuilder() |
| | | .append(options.getProtocol().getProtocolAddr()) |
| | | .append(options.getHost().trim()) |
| | | .append(":") |
| | | .append(options.getPort()); |
| | | if ((options.getProtocol() == MqttProtocolEnum.WS || options.getProtocol() == MqttProtocolEnum.WSS) |
| | | && StringUtils.hasText(options.getPath())) { |
| | | addr.append(options.getPath()); |
| | | } |
| | | return addr.toString(); |
| | | } |
| | | |
| | | @Bean |
| | | public MqttConnectOptions mqttConnectOptions() { |
| | | MqttClientOptions customizeOptions = getBasicClientOptions(); |
| | | MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); |
| | | mqttConnectOptions.setServerURIs(new String[]{ |
| | | new StringBuilder() |
| | | .append(protocol.trim()) |
| | | .append("://") |
| | | .append(host.trim()) |
| | | .append(":") |
| | | .append(port) |
| | | .toString()}); |
| | | mqttConnectOptions.setUserName(username); |
| | | mqttConnectOptions.setPassword(password.toCharArray()); |
| | | mqttConnectOptions.setServerURIs(new String[]{ getBasicMqttAddress() }); |
| | | mqttConnectOptions.setUserName(customizeOptions.getUsername()); |
| | | mqttConnectOptions.setPassword(StringUtils.hasText(customizeOptions.getPassword()) ? |
| | | customizeOptions.getPassword().toCharArray() : new char[0]); |
| | | mqttConnectOptions.setAutomaticReconnect(true); |
| | | mqttConnectOptions.setKeepAliveInterval(10); |
| | | return mqttConnectOptions; |
| | |
| | | package com.dji.sample.component.mqtt.config; |
| | | |
| | | 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.springframework.context.annotation.Bean; |
| | |
| | | public class MqttInboundConfiguration { |
| | | |
| | | @Autowired |
| | | private MqttConfiguration mqttConfiguration; |
| | | |
| | | @Autowired |
| | | private MqttPahoClientFactory mqttClientFactory; |
| | | |
| | | @Resource(name = ChannelName.INBOUND) |
| | |
| | | */ |
| | | @Bean(name = "adapter") |
| | | public MessageProducerSupport mqttInbound() { |
| | | MqttClientOptions options = MqttConfiguration.getBasicClientOptions(); |
| | | MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( |
| | | mqttConfiguration.getClientId() + "_consumer_" + System.currentTimeMillis(), |
| | | mqttClientFactory, mqttConfiguration.getInboundTopic().split(",")); |
| | | options.getClientId() + "_consumer_" + System.currentTimeMillis(), |
| | | mqttClientFactory, options.getInboundTopic().split(",")); |
| | | DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter(); |
| | | // use byte types uniformly |
| | | converter.setPayloadAsBytes(true); |
| | |
| | | @ServiceActivator(inputChannel = ChannelName.OUTBOUND) |
| | | public MessageHandler mqttOutbound() { |
| | | MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler( |
| | | mqttConfiguration.getClientId() + "_producer_" + System.currentTimeMillis(), |
| | | MqttConfiguration.getBasicClientOptions().getClientId() + "_producer_" + System.currentTimeMillis(), |
| | | mqttClientFactory); |
| | | DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter(); |
| | | // use byte types uniformly |
| | |
| | | |
| | | public static final String FLIGHT_IDS = "flight_ids"; |
| | | |
| | | public static final String ACL = "acl"; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/18 |
| | | */ |
| | | @Data |
| | | public class MqttClientOptions { |
| | | |
| | | private MqttProtocolEnum protocol; |
| | | |
| | | private String host; |
| | | |
| | | private Integer port; |
| | | |
| | | private String username; |
| | | |
| | | private String password; |
| | | |
| | | private String clientId; |
| | | |
| | | private String path; |
| | | |
| | | /** |
| | | * The topic to subscribe to immediately when client connects. Only required for basic link. |
| | | */ |
| | | private String inboundTopic; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/18 |
| | | */ |
| | | @Getter |
| | | public enum MqttProtocolEnum { |
| | | |
| | | MQTT("tcp"), |
| | | |
| | | MQTTS("tcp"), |
| | | |
| | | WS("ws"), |
| | | |
| | | WSS("wss"); |
| | | |
| | | String protocol; |
| | | |
| | | MqttProtocolEnum(String protocol) { |
| | | this.protocol = protocol; |
| | | } |
| | | |
| | | public String getProtocolAddr() { |
| | | return protocol + "://"; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.component.mqtt.model; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/18 |
| | | */ |
| | | public enum MqttUseEnum { |
| | | |
| | | /** |
| | | * The broker is used for basic link. |
| | | */ |
| | | BASIC, |
| | | |
| | | /** |
| | | * This broker is used for the drc link. |
| | | */ |
| | | DRC |
| | | } |
| | |
| | | |
| | | public void publish(String topic, CommonTopicResponse response) { |
| | | try { |
| | | |
| | | log.info("send topic: {}, payload: {}", topic, response.toString()); |
| | | messageGateway.publish(topic, mapper.writeValueAsBytes(response)); |
| | | } catch (JsonProcessingException e) { |
| | | log.info("Failed to publish the message. {}", response.toString()); |
| | |
| | | } |
| | | |
| | | public <T> T publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response, int retryTime) { |
| | | log.info("send topic: {}, payload: {}", topic, response.toString()); |
| | | AtomicInteger time = new AtomicInteger(0); |
| | | // Retry three times |
| | | while (time.getAndIncrement() <= retryTime) { |
| | |
| | | */ |
| | | public final class RedisConst { |
| | | |
| | | public static final int WAYLINE_JOB_BLOCK_TIME = 600; |
| | | |
| | | private RedisConst() { |
| | | |
| | | } |
| | |
| | | |
| | | public static final String LOGS_FILE_PREFIX = "logs_file" + DELIMITER; |
| | | |
| | | public static final String WAYLINE_JOB = "wayline_job"; |
| | | public static final String WAYLINE_JOB_TIMED_EXECUTE = "wayline_job_timed_execute"; |
| | | |
| | | public static final String WAYLINE_JOB_BLOCK_PREFIX = "wayline_job_block" + DELIMITER; |
| | | |
| | | public static final String WAYLINE_JOB_RUNNING_PREFIX = "wayline_job_running" + DELIMITER; |
| | | |
| | | public static final String WAYLINE_JOB_PAUSED_PREFIX = "wayline_job_paused" + DELIMITER; |
| | | |
| | | public static final String OSD_PREFIX = "osd" + DELIMITER; |
| | | |
| | | public static final String MEDIA_FILE_PREFIX = "media_file" + DELIMITER; |
| | | |
| | | public static final String MEDIA_HIGHEST_PRIORITY_PREFIX = "media_highest_priority" + DELIMITER; |
| | | |
| | | public static final String LIVE_CAPACITY = "live_capacity"; |
| | | |
| | | public static final String MQTT_ACL_PREFIX = "mqtt_acl" + DELIMITER; |
| | | |
| | | public static final String FILE_UPLOADING_PREFIX = "file_uploading" + DELIMITER; |
| | | } |
| | |
| | | return redisTemplate.opsForZSet().score(key, value); |
| | | } |
| | | |
| | | /** |
| | | * ZINCRBY |
| | | * @param key |
| | | * @param value |
| | | * @param delta |
| | | */ |
| | | public static Double zIncrement(String key, Object value, double delta) { |
| | | return redisTemplate.opsForZSet().incrementScore(key, value, delta); |
| | | } |
| | | } |
| | |
| | | serviceReplyOpt, new TypeReference<ServiceReply<EventsOutputReceiver>>() {}); |
| | | if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { |
| | | return ResponseResult.error(serviceReply.getResult(), |
| | | Objects.nonNull(serviceReply.getOutput()) ? serviceReply.getOutput().getStatus() : "error: " + serviceIdentifier); |
| | | Objects.nonNull(serviceReply.getOutput()) ? serviceReply.getOutput().getStatus() |
| | | : "error: " + serviceIdentifier + serviceReply.getResult()); |
| | | } |
| | | if (controlMethodEnum.getProgress()) { |
| | | RedisOpsUtils.setWithExpire(serviceIdentifier + RedisConst.DELIMITER + bid, sn, |
| | |
| | | */ |
| | | @GetMapping("/{workspace_id}/devices/bound") |
| | | public ResponseResult<PaginationData<DeviceDTO>> getBoundDevicesWithDomain( |
| | | @PathVariable("workspace_id") String workspaceId, String domain, |
| | | @PathVariable("workspace_id") String workspaceId, Integer domain, |
| | | @RequestParam(defaultValue = "1") Long page, |
| | | @RequestParam(value = "page_size", defaultValue = "50") Long pageSize) { |
| | | PaginationData<DeviceDTO> devices = deviceService.getBoundDevicesWithDomain(workspaceId, page, pageSize, domain); |
| | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.validation.Valid; |
| | | import javax.validation.constraints.NotNull; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | |
| | | */ |
| | | @GetMapping("/firmware-release-notes/latest") |
| | | public ResponseResult<List<DeviceFirmwareNoteDTO>> getLatestFirmwareNote(@RequestParam("device_name") List<String> deviceNames) { |
| | | List<DeviceFirmwareNoteDTO> releaseNotes = new ArrayList<>(); |
| | | deviceNames.forEach(deviceName -> { |
| | | Optional<DeviceFirmwareNoteDTO> latestFirmware = service.getLatestFirmwareReleaseNote(deviceName); |
| | | latestFirmware.ifPresent(releaseNotes::add); |
| | | }); |
| | | |
| | | List<DeviceFirmwareNoteDTO> releaseNotes = deviceNames.stream() |
| | | .map(deviceName -> service.getLatestFirmwareReleaseNote(deviceName)) |
| | | .filter(Optional::isPresent) |
| | | .map(Optional::get) |
| | | .collect(Collectors.toList()); |
| | | |
| | | return ResponseResult.success(releaseNotes); |
| | | } |
| | | |
| | |
| | | * @return |
| | | */ |
| | | @PutMapping("/{workspace_id}/firmwares/{firmware_id}") |
| | | public ResponseResult importFirmwareFile(@PathVariable("workspace_id") String workspaceId, |
| | | public ResponseResult changeFirmwareStatus(@PathVariable("workspace_id") String workspaceId, |
| | | @PathVariable("firmware_id") String firmwareId, |
| | | @Valid @RequestBody DeviceFirmwareUpdateParam param) { |
| | | |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.Wrapper; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Constants; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.dji.sample.manage.model.entity.DeviceFirmwareEntity; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | * @date 2022/8/16 |
| | | */ |
| | | public interface IDeviceFirmwareMapper extends BaseMapper<DeviceFirmwareEntity> { |
| | | String sql = "<script> \n" + |
| | | "SELECT \n" + |
| | | " * \n" + |
| | | "from \n" + |
| | | " (\n" + |
| | | " select \n" + |
| | | " a.*, \n" + |
| | | " group_concat(b.device_name) device_name \n" + |
| | | " from \n" + |
| | | " manage_device_firmware a \n" + |
| | | " join manage_firmware_model b on a.firmware_id = b.firmware_id \n" + |
| | | " <if test='device_name != null and device_name != \"\"'> \n" + |
| | | " and b.device_name = #{device_name} \n" + |
| | | " </if> \n" + |
| | | " group by firmware_id \n" + |
| | | " ) c ${ew.customSqlSegment} \n"; |
| | | |
| | | @Select(sql + "</script>") |
| | | Page<DeviceFirmwareEntity> selectPage(Page page, @Param(Constants.WRAPPER)Wrapper<DeviceFirmwareEntity> wrapper, @Param("device_name") String deviceName); |
| | | |
| | | @Select(sql + " limit 1 </script>") |
| | | DeviceFirmwareEntity selectOne(@Param(Constants.WRAPPER)Wrapper<DeviceFirmwareEntity> wrapper, @Param("device_name") String deviceName); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dji.sample.manage.model.entity.FirmwareModelEntity; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/12/21 |
| | | */ |
| | | public interface IFirmwareModelMapper extends BaseMapper<FirmwareModelEntity> { |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.common; |
| | | |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3.1 |
| | | * @date 2023/1/5 |
| | | */ |
| | | @Component |
| | | @ConfigurationProperties("cloud-api.app") |
| | | public class AppLicenseProperties { |
| | | |
| | | public static String id; |
| | | |
| | | public static String key; |
| | | |
| | | public static String license; |
| | | |
| | | public void setId(String id) { |
| | | AppLicenseProperties.id = id; |
| | | } |
| | | |
| | | public void setKey(String key) { |
| | | AppLicenseProperties.key = key; |
| | | } |
| | | |
| | | public void setLicense(String license) { |
| | | AppLicenseProperties.license = license; |
| | | } |
| | | } |
| | |
| | | |
| | | private String childDeviceSn; |
| | | |
| | | private String domain; |
| | | private Integer domain; |
| | | |
| | | private Integer type; |
| | | |
| | | private Integer subType; |
| | | |
| | | private List<DeviceDTO> gatewaysList; |
| | | |
| | | private List<DevicePayloadDTO> payloadsList; |
| | | |
| | |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | |
| | | private String fileMd5; |
| | | |
| | | private String deviceName; |
| | | private List<String> deviceName; |
| | | |
| | | private String releaseNote; |
| | | |
| New file |
| | |
| | | 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 1.3 |
| | | * @date 2022/12/21 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class FirmwareModelDTO { |
| | | |
| | | private String firmwareId; |
| | | |
| | | private List<String> deviceNames; |
| | | } |
| | |
| | | |
| | | private LogsFileUploadList params; |
| | | |
| | | private String region; |
| | | |
| | | public LogsUploadCredentialsDTO(StsCredentialsDTO sts) { |
| | | this.bucket = sts.getBucket(); |
| | | Long expire = sts.getCredentials().getExpire(); |
| | | long expire = sts.getCredentials().getExpire(); |
| | | sts.getCredentials().setExpire(System.currentTimeMillis() + (expire - 60) * 1000); |
| | | this.credentials = sts.getCredentials(); |
| | | this.endpoint = sts.getEndpoint(); |
| | | this.objectKeyPrefix = sts.getObjectKeyPrefix(); |
| | | this.provider = sts.getProvider(); |
| | | this.region = sts.getRegion(); |
| | | } |
| | | } |
| File was renamed from src/main/java/com/dji/sample/manage/model/dto/NtpServerDTO.java |
| | |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class NtpServerDTO { |
| | | public class ProductConfigDTO { |
| | | |
| | | private String ntpServerHost; |
| | | |
| | | private String appId; |
| | | |
| | | private String appKey; |
| | | |
| | | private String appLicense; |
| | | } |
| | |
| | | |
| | | private String gatewaySn; |
| | | |
| | | private String domain; |
| | | private Integer domain; |
| | | } |
| | |
| | | @TableField("file_md5") |
| | | private String fileMd5; |
| | | |
| | | @TableField("device_name") |
| | | @TableField(exist = false) |
| | | private String deviceName; |
| | | |
| | | @TableField("release_note") |
| New file |
| | |
| | | package com.dji.sample.manage.model.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/12/21 |
| | | */ |
| | | @Data |
| | | @TableName("manage_firmware_model") |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class FirmwareModelEntity implements Serializable { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @TableField("firmware_id") |
| | | private String firmwareId; |
| | | |
| | | @TableField("device_name") |
| | | private String deviceName; |
| | | |
| | | @TableField(value = "create_time", fill = FieldFill.INSERT) |
| | | private Long createTime; |
| | | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | } |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | | * @date 2021/11/15 |
| | | * @version 0.1 |
| | | */ |
| | | @Getter |
| | | public enum DeviceDomainEnum { |
| | | |
| | | SUB_DEVICE(0, "sub-device"), |
| | | SUB_DEVICE(0), |
| | | |
| | | GATEWAY(2, "gateway"), |
| | | GATEWAY(2), |
| | | |
| | | PAYLOAD(1, "payload"), |
| | | PAYLOAD(1), |
| | | |
| | | DOCK (3, "dock"), |
| | | DOCK (3); |
| | | |
| | | UNKNOWN(-1, "unknown"); |
| | | int val; |
| | | |
| | | private int val; |
| | | |
| | | private String desc; |
| | | |
| | | DeviceDomainEnum(int val, String desc) { |
| | | DeviceDomainEnum(int val) { |
| | | this.val = val; |
| | | this.desc = desc; |
| | | } |
| | | |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | public String getDesc() { |
| | | return desc; |
| | | } |
| | | |
| | | public static String getDesc(int val) { |
| | | if (SUB_DEVICE.val == val) { |
| | | return SUB_DEVICE.desc; |
| | | } |
| | | |
| | | if (GATEWAY.val == val) { |
| | | return GATEWAY.desc; |
| | | } |
| | | |
| | | if (PAYLOAD.val == val) { |
| | | return PAYLOAD.desc; |
| | | } |
| | | |
| | | if (DOCK.val == val) { |
| | | return DOCK.desc; |
| | | } |
| | | return UNKNOWN.desc; |
| | | } |
| | | |
| | | public static int getVal(String desc) { |
| | | if (SUB_DEVICE.desc.equals(desc)) { |
| | | return SUB_DEVICE.val; |
| | | } |
| | | |
| | | if (GATEWAY.desc.equals(desc)) { |
| | | return GATEWAY.val; |
| | | } |
| | | |
| | | if (PAYLOAD.desc.equals(desc)) { |
| | | return PAYLOAD.val; |
| | | } |
| | | |
| | | if (DOCK.desc.equals(desc)) { |
| | | return DOCK.val; |
| | | } |
| | | return UNKNOWN.val; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | import lombok.Data; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | private Boolean status; |
| | | |
| | | @NotNull |
| | | private String deviceName; |
| | | private List<String> deviceName; |
| | | } |
| | |
| | | valid = StateSwitchEnum.find(state).isPresent(); |
| | | } |
| | | if (Objects.nonNull(distanceLimit)) { |
| | | valid &= StateSwitchEnum.find(distanceLimit).isPresent(); |
| | | valid &= distanceLimit >= DISTANCE_MIN && distanceLimit <= DISTANCE_MAX; |
| | | } |
| | | return valid; |
| | | } |
| | |
| | | |
| | | /** |
| | | * Query specific firmware information based on the device model and firmware version. |
| | | * |
| | | * @param workspaceId |
| | | * @param deviceName |
| | | * @param version |
| | | * @return |
| | | */ |
| | | Optional<DeviceFirmwareDTO> getFirmware(String deviceName, String version); |
| | | Optional<DeviceFirmwareDTO> getFirmware(String workspaceId, String deviceName, String version); |
| | | |
| | | /** |
| | | * Get the latest firmware release note for this device model. |
| | |
| | | |
| | | /** |
| | | * Get the firmware information that the device needs to update. |
| | | * |
| | | * @param workspaceId |
| | | * @param upgradeDTOS |
| | | * @return |
| | | */ |
| | | List<DeviceOtaCreateParam> getDeviceOtaFirmware(List<DeviceFirmwareUpgradeDTO> upgradeDTOS); |
| | | List<DeviceOtaCreateParam> getDeviceOtaFirmware(String workspaceId, List<DeviceFirmwareUpgradeDTO> upgradeDTOS); |
| | | |
| | | /** |
| | | * Interface to handle device firmware update progress. |
| | |
| | | /** |
| | | * Save the file information of the firmware. |
| | | * @param firmware |
| | | * @param deviceNames |
| | | */ |
| | | void saveFirmwareInfo(DeviceFirmwareDTO firmware); |
| | | void saveFirmwareInfo(DeviceFirmwareDTO firmware, List<String> deviceNames); |
| | | |
| | | /** |
| | | * Update the file information of the firmware. |
| | |
| | | * @param domain |
| | | * @return |
| | | */ |
| | | PaginationData<DeviceDTO> getBoundDevicesWithDomain(String workspaceId, Long page, Long pageSize, String domain); |
| | | PaginationData<DeviceDTO> getBoundDevicesWithDomain(String workspaceId, Long page, Long pageSize, Integer domain); |
| | | |
| | | /** |
| | | * Unbind device base on device's sn. |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.manage.model.dto.FirmwareModelDTO; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/12/21 |
| | | */ |
| | | public interface IFirmwareModelService { |
| | | |
| | | /** |
| | | * Save the relationship between firmware files and device models. |
| | | * @param firmwareModel |
| | | */ |
| | | void saveFirmwareDeviceName(FirmwareModelDTO firmwareModel); |
| | | |
| | | } |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | |
| | | |
| | | @Autowired |
| | | protected ObjectMapper mapper; |
| | | |
| | | @Autowired |
| | | protected RedisOpsUtils redisOps; |
| | | |
| | | @Autowired |
| | | private IWebSocketManageService webSocketManageService; |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.manage.model.common.AppLicenseProperties; |
| | | import com.dji.sample.manage.model.common.NtpServerProperties; |
| | | import com.dji.sample.manage.model.dto.NtpServerDTO; |
| | | import com.dji.sample.manage.model.dto.ProductConfigDTO; |
| | | import com.dji.sample.manage.service.IRequestsConfigService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | |
| | | |
| | | @Override |
| | | public Object getConfig() { |
| | | return new NtpServerDTO(NtpServerProperties.host); |
| | | return new ProductConfigDTO(NtpServerProperties.host, AppLicenseProperties.id, AppLicenseProperties.key, AppLicenseProperties.license); |
| | | } |
| | | } |
| | |
| | | |
| | | 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.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.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.component.websocket.service.impl.SendMessageServiceImpl; |
| | |
| | | import com.dji.sample.manage.model.param.DeviceOtaCreateParam; |
| | | import com.dji.sample.manage.service.IDeviceFirmwareService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.manage.service.IFirmwareModelService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | @Autowired |
| | | private OssServiceContext ossServiceContext; |
| | | |
| | | @Autowired |
| | | private IFirmwareModelService firmwareModelService; |
| | | |
| | | @Override |
| | | public Optional<DeviceFirmwareDTO> getFirmware(String deviceName, String version) { |
| | | public Optional<DeviceFirmwareDTO> getFirmware(String workspaceId, String deviceName, String version) { |
| | | return Optional.ofNullable(entity2Dto(mapper.selectOne( |
| | | new LambdaQueryWrapper<DeviceFirmwareEntity>() |
| | | .eq(DeviceFirmwareEntity::getDeviceName, deviceName) |
| | | .eq(DeviceFirmwareEntity::getFirmwareVersion, version)))); |
| | | .eq(DeviceFirmwareEntity::getWorkspaceId, workspaceId) |
| | | .eq(DeviceFirmwareEntity::getFirmwareVersion, version) |
| | | .eq(DeviceFirmwareEntity::getStatus, true), |
| | | deviceName))); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<DeviceFirmwareNoteDTO> getLatestFirmwareReleaseNote(String deviceName) { |
| | | return Optional.ofNullable(entity2NoteDto(mapper.selectOne( |
| | | new LambdaQueryWrapper<DeviceFirmwareEntity>() |
| | | .eq(DeviceFirmwareEntity::getDeviceName, deviceName) |
| | | Wrappers.lambdaQuery(DeviceFirmwareEntity.class) |
| | | .eq(DeviceFirmwareEntity::getStatus, true) |
| | | .orderByDesc(DeviceFirmwareEntity::getReleaseDate, DeviceFirmwareEntity::getFirmwareVersion) |
| | | .last(" limit 1 ")))); |
| | | .orderByDesc(DeviceFirmwareEntity::getReleaseDate, DeviceFirmwareEntity::getFirmwareVersion), |
| | | deviceName))); |
| | | } |
| | | |
| | | @Override |
| | | public List<DeviceOtaCreateParam> getDeviceOtaFirmware(List<DeviceFirmwareUpgradeDTO> upgradeDTOS) { |
| | | public List<DeviceOtaCreateParam> getDeviceOtaFirmware(String workspaceId, List<DeviceFirmwareUpgradeDTO> upgradeDTOS) { |
| | | List<DeviceOtaCreateParam> deviceOtaList = new ArrayList<>(); |
| | | upgradeDTOS.forEach(upgradeDevice -> { |
| | | boolean exist = deviceService.checkDeviceOnline(upgradeDevice.getSn()); |
| | |
| | | throw new IllegalArgumentException("Device is offline."); |
| | | } |
| | | Optional<DeviceFirmwareDTO> firmwareOpt = this.getFirmware( |
| | | upgradeDevice.getDeviceName(), upgradeDevice.getProductVersion()); |
| | | workspaceId, upgradeDevice.getDeviceName(), upgradeDevice.getProductVersion()); |
| | | if (firmwareOpt.isEmpty()) { |
| | | throw new IllegalArgumentException("This firmware version does not exist."); |
| | | } |
| | | if (!firmwareOpt.get().getFirmwareStatus()) { |
| | | throw new IllegalArgumentException("This firmware version is not available."); |
| | | throw new IllegalArgumentException("This firmware version does not exist or is not available."); |
| | | } |
| | | DeviceOtaCreateParam ota = dto2OtaCreateDto(firmwareOpt.get()); |
| | | ota.setSn(upgradeDevice.getSn()); |
| | |
| | | EventsReceiver<EventsOutputReceiver> eventsReceiver = objectMapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<EventsOutputReceiver>>(){}); |
| | | eventsReceiver.setBid(receiver.getBid()); |
| | | eventsReceiver.setSn(sn); |
| | | |
| | | EventsOutputReceiver output = eventsReceiver.getOutput(); |
| | | log.info("SN: {}, {} ===> Upgrading progress: {}", |
| | |
| | | |
| | | // Determine whether it is the ending state, delete the update state key in redis after the job ends. |
| | | EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus()); |
| | | Collection<ConcurrentWebSocketSession> sessions = webSocketManageService.getValueWithWorkspaceAndUserType( |
| | | device.getWorkspaceId(), UserTypeEnum.WEB.getVal()); |
| | | CustomWebSocketMessage<Object> build = CustomWebSocketMessage.builder() |
| | | .data(eventsReceiver) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(receiver.getMethod()) |
| | | .build(); |
| | | if (upgrade) { |
| | | if (statusEnum.getEnd()) { |
| | | // Delete the cache after the update is complete. |
| | |
| | | RedisConst.FIRMWARE_UPGRADING_PREFIX + sn, output.getProgress().getPercent(), |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | } |
| | | eventsReceiver.setSn(sn); |
| | | webSocketMessageService.sendBatch(sessions, build); |
| | | } |
| | | if (childUpgrade) { |
| | | if (!StringUtils.hasText(eventsReceiver.getSn())) { |
| | | eventsReceiver.setSn(childDeviceSn); |
| | | webSocketMessageService.sendBatch(sessions, build); |
| | | } |
| | | if (statusEnum.getEnd()) { |
| | | RedisOpsUtils.del(RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn); |
| | | } else { |
| | |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | } |
| | | } |
| | | |
| | | webSocketMessageService.sendBatch( |
| | | webSocketManageService.getValueWithWorkspaceAndUserType( |
| | | device.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | CustomWebSocketMessage.builder() |
| | | .data(eventsReceiver) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(receiver.getMethod()) |
| | | .build()); |
| | | |
| | | if (receiver.getNeedReply() != null && receiver.getNeedReply() == 1) { |
| | | String replyTopic = headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF; |
| | |
| | | |
| | | @Override |
| | | public Boolean checkFileExist(String workspaceId, String fileMd5) { |
| | | return mapper.selectCount(new LambdaQueryWrapper<DeviceFirmwareEntity>() |
| | | return RedisOpsUtils.checkExist(RedisConst.FILE_UPLOADING_PREFIX + workspaceId + fileMd5) || |
| | | mapper.selectCount(new LambdaQueryWrapper<DeviceFirmwareEntity>() |
| | | .eq(DeviceFirmwareEntity::getWorkspaceId, workspaceId) |
| | | .eq(DeviceFirmwareEntity::getFileMd5, fileMd5)) |
| | | > 0; |
| | |
| | | new LambdaQueryWrapper<DeviceFirmwareEntity>() |
| | | .eq(DeviceFirmwareEntity::getWorkspaceId, workspaceId) |
| | | .eq(Objects.nonNull(param.getStatus()), DeviceFirmwareEntity::getStatus, param.getStatus()) |
| | | .eq(StringUtils.hasText(param.getDeviceName()), DeviceFirmwareEntity::getDeviceName, param.getDeviceName()) |
| | | .like(StringUtils.hasText(param.getProductVersion()), DeviceFirmwareEntity::getFirmwareVersion, param.getProductVersion()) |
| | | .orderByDesc(DeviceFirmwareEntity::getReleaseDate)); |
| | | .orderByDesc(DeviceFirmwareEntity::getReleaseDate), param.getDeviceName()); |
| | | |
| | | List<DeviceFirmwareDTO> data = page.getRecords().stream().map(this::entity2Dto).collect(Collectors.toList()); |
| | | return new PaginationData<DeviceFirmwareDTO>(data, new Pagination(page)); |
| | |
| | | |
| | | @Override |
| | | public void importFirmwareFile(String workspaceId, String creator, DeviceFirmwareUploadParam param, MultipartFile file) { |
| | | String key = RedisConst.FILE_UPLOADING_PREFIX + workspaceId; |
| | | String existKey = key + file.getOriginalFilename(); |
| | | if (RedisOpsUtils.getExpire(existKey) > 0) { |
| | | throw new RuntimeException("Please try again later."); |
| | | } |
| | | RedisOpsUtils.setWithExpire(existKey, true, RedisConst.DEVICE_ALIVE_SECOND); |
| | | try (InputStream is = file.getInputStream()) { |
| | | long size = is.available(); |
| | | String md5 = DigestUtils.md5DigestAsHex(is); |
| | | key += md5; |
| | | boolean exist = checkFileExist(workspaceId, md5); |
| | | if (exist) { |
| | | throw new RuntimeException("The file already exists."); |
| | | } |
| | | |
| | | RedisOpsUtils.set(key, System.currentTimeMillis()); |
| | | Optional<DeviceFirmwareDTO> firmwareOpt = verifyFirmwareFile(file); |
| | | if (firmwareOpt.isEmpty()) { |
| | | throw new RuntimeException("The file format is incorrect."); |
| | |
| | | String objectKey = OssConfiguration.objectDirPrefix + File.separator + firmwareId + FirmwareFileProperties.FIRMWARE_FILE_SUFFIX; |
| | | |
| | | ossServiceContext.putObject(OssConfiguration.bucket, objectKey, file.getInputStream()); |
| | | log.info("upload success"); |
| | | log.info("upload success. {}", file.getOriginalFilename()); |
| | | DeviceFirmwareDTO firmware = DeviceFirmwareDTO.builder() |
| | | .deviceName(param.getDeviceName()) |
| | | .releaseNote(param.getReleaseNote()) |
| | | .firmwareStatus(param.getStatus()) |
| | | .fileMd5(md5) |
| | |
| | | .firmwareId(firmwareId) |
| | | .build(); |
| | | |
| | | saveFirmwareInfo(firmware); |
| | | saveFirmwareInfo(firmware, param.getDeviceName()); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } finally { |
| | | RedisOpsUtils.del(key); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void saveFirmwareInfo(DeviceFirmwareDTO firmware) { |
| | | mapper.insert(dto2Entity(firmware)); |
| | | public void saveFirmwareInfo(DeviceFirmwareDTO firmware, List<String> deviceNames) { |
| | | DeviceFirmwareEntity entity = dto2Entity(firmware); |
| | | mapper.insert(entity); |
| | | firmwareModelService.saveFirmwareDeviceName( |
| | | FirmwareModelDTO.builder().firmwareId(entity.getFirmwareId()).deviceNames(deviceNames).build()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | return DeviceFirmwareEntity.builder() |
| | | .fileName(dto.getFileName()) |
| | | .deviceName(dto.getDeviceName()) |
| | | .fileMd5(dto.getFileMd5()) |
| | | .fileSize(dto.getFileSize()) |
| | | .firmwareId(dto.getFirmwareId()) |
| | |
| | | return null; |
| | | } |
| | | return DeviceFirmwareDTO.builder() |
| | | .deviceName(entity.getDeviceName()) |
| | | .deviceName(Arrays.asList(entity.getDeviceName().split(","))) |
| | | .fileMd5(entity.getFileMd5()) |
| | | .fileSize(entity.getFileSize()) |
| | | .objectKey(entity.getObjectKey()) |
| | |
| | | |
| | | 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; |
| | |
| | | public void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, |
| | | Collection<ConcurrentWebSocketSession> webSessions, |
| | | CustomWebSocketMessage<TelemetryDTO> wsMessage) { |
| | | if (DeviceDomainEnum.SUB_DEVICE.getDesc().equals(device.getDomain())) { |
| | | if (DeviceDomainEnum.SUB_DEVICE.getVal() == device.getDomain()) { |
| | | wsMessage.setBizCode(BizCodeEnum.DEVICE_OSD.getCode()); |
| | | |
| | | OsdSubDeviceReceiver data = mapper.convertValue(receiver.getData(), OsdSubDeviceReceiver.class); |
| | |
| | | log.warn("Please remount the payload, or restart the drone. Otherwise the data of the payload will not be received."); |
| | | } |
| | | |
| | | redisOps.setWithExpire(RedisConst.OSD_PREFIX + device.getDeviceSn(), data, RedisConst.DEVICE_ALIVE_SECOND); |
| | | RedisOpsUtils.setWithExpire(RedisConst.OSD_PREFIX + device.getDeviceSn(), data, RedisConst.DEVICE_ALIVE_SECOND); |
| | | wsMessage.getData().setHost(data); |
| | | |
| | | sendMessageService.sendBatch(webSessions, wsMessage); |
| | |
| | | Optional<DeviceDTO> gatewayOpt = this.getDeviceBySn(gatewaySn); |
| | | if (gatewayOpt.isPresent()) { |
| | | DeviceDTO value = gatewayOpt.get(); |
| | | value.setBoundTime(null); |
| | | value.setLoginTime(null); |
| | | RedisOpsUtils.setWithExpire(key, value, RedisConst.DEVICE_ALIVE_SECOND); |
| | | this.pushDeviceOnlineTopo(value.getWorkspaceId(), gatewaySn, gatewaySn); |
| | | return true; |
| | |
| | | |
| | | // When connecting for the first time |
| | | DeviceEntity gatewayDevice = deviceGatewayConvertToDeviceEntity(gateway); |
| | | return firstSaveDevice(gatewayDevice, null); |
| | | return onlineSaveDevice(gatewayDevice, null).isPresent(); |
| | | } |
| | | |
| | | DeviceDTO deviceDTO = (DeviceDTO) (RedisOpsUtils.get(key)); |
| | |
| | | |
| | | RedisOpsUtils.del(key); |
| | | RedisOpsUtils.del(RedisConst.OSD_PREFIX + device.getDeviceSn()); |
| | | RedisOpsUtils.del(RedisConst.HMS_PREFIX + device.getDeviceSn()); |
| | | log.debug("{} offline.", deviceSn); |
| | | return true; |
| | | } |
| | |
| | | DeviceQueryParam.builder() |
| | | .childSn(deviceSn) |
| | | .build()); |
| | | gatewaysList.stream().filter( |
| | | gateway -> !gateway.getDeviceSn().equals(deviceGateway.getSn())) |
| | | gatewaysList.stream() |
| | | .filter(gateway -> !gateway.getDeviceSn().equals(deviceGateway.getSn())) |
| | | .findAny() |
| | | .ifPresent(gateway -> { |
| | | gateway.setChildDeviceSn(""); |
| | |
| | | }); |
| | | |
| | | DeviceEntity gateway = deviceGatewayConvertToDeviceEntity(deviceGateway); |
| | | DeviceEntity subDevice = subDeviceConvertToDeviceEntity(deviceGateway.getSubDevices().get(0)); |
| | | boolean isSave = firstSaveDevice(gateway, deviceSn) && firstSaveDevice(subDevice, null); |
| | | if (!isSave) { |
| | | Optional<DeviceEntity> gatewayEntityOpt = onlineSaveDevice(gateway, deviceSn); |
| | | if (gatewayEntityOpt.isEmpty()) { |
| | | log.error("Failed to go online, please check the status data or code logic."); |
| | | return false; |
| | | } |
| | | |
| | | // dock go online |
| | | if (deviceGateway.getDomain() != null && DeviceDomainEnum.DOCK.getVal() == deviceGateway.getDomain()) { |
| | | Optional<DeviceDTO> deviceOpt = this.getDeviceBySn(deviceGateway.getSn()); |
| | | if (deviceOpt.isEmpty()) { |
| | | log.info("The dock is not bound and cannot go online. Please refer to the Cloud API document video for binding."); |
| | | return false; |
| | | } |
| | | gateway.setNickname(null); |
| | | subDevice.setNickname(null); |
| | | DeviceEntity subDevice = subDeviceConvertToDeviceEntity(deviceGateway.getSubDevices().get(0)); |
| | | Optional<DeviceEntity> subDeviceEntityOpt = onlineSaveDevice(subDevice, null); |
| | | if (subDeviceEntityOpt.isEmpty()) { |
| | | log.error("Failed to go online, please check the status data or code logic."); |
| | | return false; |
| | | } |
| | | |
| | | String workspaceId = subDevice.getWorkspaceId(); |
| | | subDevice = subDeviceEntityOpt.get(); |
| | | gateway = gatewayEntityOpt.get(); |
| | | |
| | | this.subscribeTopicOnline(deviceGateway.getSn()); |
| | | if (!StringUtils.hasText(workspaceId)) { |
| | | log.info("The drone is not bound and cannot go online. Please refer to the Cloud API document video for binding."); |
| | | return true; |
| | | // 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()); |
| | | } |
| | | |
| | | // Subscribe to topic related to drone devices. |
| | | this.subscribeTopicOnline(deviceGateway.getSn()); |
| | | this.subscribeTopicOnline(deviceSn); |
| | | this.pushDeviceOnlineTopo(workspaceId, deviceGateway.getSn(), deviceSn); |
| | | this.pushDeviceOnlineTopo(subDevice.getWorkspaceId(), deviceGateway.getSn(), deviceSn); |
| | | |
| | | log.debug("{} online.", subDevice.getDeviceSn()); |
| | | return true; |
| | |
| | | List<DeviceDTO> devicesList = this.getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .workspaceId(workspaceId) |
| | | .domains(List.of(DeviceDomainEnum.SUB_DEVICE.getVal())) |
| | | .domains(List.of(DeviceDomainEnum.GATEWAY.getVal(), DeviceDomainEnum.DOCK.getVal())) |
| | | .build()); |
| | | |
| | | devicesList.forEach(device -> { |
| | | this.spliceDeviceTopo(device); |
| | | device.setWorkspaceId(workspaceId); |
| | | device.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())); |
| | | }); |
| | | devicesList.stream() |
| | | .filter(gateway -> DeviceDomainEnum.DOCK.getVal() == gateway.getDomain() || |
| | | RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + gateway.getDeviceSn())) |
| | | .forEach(this::spliceDeviceTopo); |
| | | |
| | | return devicesList; |
| | | } |
| | | |
| | | @Override |
| | | public void spliceDeviceTopo(DeviceDTO device) { |
| | | public void spliceDeviceTopo(DeviceDTO gateway) { |
| | | |
| | | // remote controller |
| | | List<DeviceDTO> gatewaysList = getDevicesByParams( |
| | | DeviceQueryParam.builder() |
| | | .childSn(device.getDeviceSn()) |
| | | .build()); |
| | | gateway.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + gateway.getDeviceSn())); |
| | | |
| | | // sub device |
| | | if (!StringUtils.hasText(gateway.getChildDeviceSn())) { |
| | | return; |
| | | } |
| | | |
| | | DeviceDTO subDevice = getDevicesByParams(DeviceQueryParam.builder().deviceSn(gateway.getChildDeviceSn()).build()).get(0); |
| | | subDevice.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + subDevice.getDeviceSn())); |
| | | gateway.setChildren(subDevice); |
| | | |
| | | // payloads |
| | | List<DevicePayloadDTO> payloadsList = payloadService |
| | | .getDevicePayloadEntitiesByDeviceSn(device.getDeviceSn()); |
| | | |
| | | |
| | | device.setGatewaysList(gatewaysList); |
| | | device.setPayloadsList(payloadsList); |
| | | |
| | | subDevice.setPayloadsList(payloadService.getDevicePayloadEntitiesByDeviceSn(gateway.getChildDeviceSn())); |
| | | } |
| | | |
| | | @Override |
| | |
| | | TopologyDeviceDTO.TopologyDeviceDTOBuilder builder = TopologyDeviceDTO.builder(); |
| | | |
| | | if (device != null) { |
| | | int domain = DeviceDomainEnum.getVal(device.getDomain()); |
| | | String subType = String.valueOf(device.getSubType()); |
| | | String type = String.valueOf(device.getType()); |
| | | |
| | | builder.sn(device.getDeviceSn()) |
| | | .deviceCallsign(device.getNickname()) |
| | | .deviceModel(DeviceModelDTO.builder() |
| | | .domain(String.valueOf(domain)) |
| | | .subType(subType) |
| | | .type(type) |
| | | .key(domain + "-" + type + "-" + subType) |
| | | .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(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())) |
| | | .boundStatus(device.getBoundStatus()) |
| | | .model(device.getDeviceName()) |
| | | .userId(device.getUserId()) |
| | | .domain(DeviceDomainEnum.getDesc(domain)) |
| | | .domain(device.getDomain()) |
| | | .build(); |
| | | } |
| | | return builder.build(); |
| | |
| | | .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); |
| | | return Optional.of(deviceEntity); |
| | |
| | | .workspaceId(entity.getWorkspaceId()) |
| | | .type(entity.getDeviceType()) |
| | | .subType(entity.getSubType()) |
| | | .domain(DeviceDomainEnum.getDesc(entity.getDomain())) |
| | | .domain(entity.getDomain()) |
| | | .iconUrl(IconUrlDTO.builder() |
| | | .normalUrl(entity.getUrlNormal()) |
| | | .selectUrl(entity.getUrlSelect()) |
| | |
| | | } |
| | | |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(); |
| | | DeviceDTO redisDevice = (DeviceDTO)RedisOpsUtils.get(key); |
| | | if (Objects.isNull(redisDevice)) { |
| | | if (!RedisOpsUtils.checkExist(key)) { |
| | | return false; |
| | | } |
| | | |
| | | DeviceDTO redisDevice = (DeviceDTO)RedisOpsUtils.get(key); |
| | | redisDevice.setWorkspaceId(device.getWorkspaceId()); |
| | | RedisOpsUtils.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND); |
| | | |
| | | if (DeviceDomainEnum.GATEWAY.getDesc().equals(redisDevice.getDomain())) { |
| | | if (DeviceDomainEnum.GATEWAY.getVal() == redisDevice.getDomain()) { |
| | | this.pushDeviceOnlineTopo(webSocketManageService.getValueWithWorkspace(device.getWorkspaceId()), |
| | | device.getDeviceSn(), device.getDeviceSn()); |
| | | } |
| | | if (DeviceDomainEnum.SUB_DEVICE.getDesc().equals(redisDevice.getDomain())) { |
| | | if (DeviceDomainEnum.SUB_DEVICE.getVal() == redisDevice.getDomain()) { |
| | | DeviceDTO subDevice = this.getDevicesByParams(DeviceQueryParam.builder() |
| | | .childSn(device.getChildDeviceSn()) |
| | | .build()).get(0); |
| | |
| | | |
| | | @Override |
| | | public PaginationData<DeviceDTO> getBoundDevicesWithDomain(String workspaceId, Long page, |
| | | Long pageSize, String domain) { |
| | | Long pageSize, Integer domain) { |
| | | |
| | | Page<DeviceEntity> pagination = mapper.selectPage(new Page<>(page, pageSize), |
| | | new LambdaQueryWrapper<DeviceEntity>() |
| | | .eq(DeviceEntity::getDomain, DeviceDomainEnum.getVal(domain)) |
| | | .eq(DeviceEntity::getDomain, domain) |
| | | .eq(DeviceEntity::getWorkspaceId, workspaceId) |
| | | .eq(DeviceEntity::getBoundStatus, true)); |
| | | List<DeviceDTO> devicesList = pagination.getRecords().stream().map(this::deviceEntityConvertToDTO) |
| | |
| | | |
| | | @Override |
| | | public ResponseResult createDeviceOtaJob(String workspaceId, List<DeviceFirmwareUpgradeDTO> upgradeDTOS) { |
| | | List<DeviceOtaCreateParam> deviceOtaFirmwares = deviceFirmwareService.getDeviceOtaFirmware(upgradeDTOS); |
| | | List<DeviceOtaCreateParam> deviceOtaFirmwares = deviceFirmwareService.getDeviceOtaFirmware(workspaceId, upgradeDTOS); |
| | | if (deviceOtaFirmwares.isEmpty()) { |
| | | return ResponseResult.error(); |
| | | } |
| | |
| | | .boundTime(dto.getBoundTime() != null ? |
| | | dto.getBoundTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null) |
| | | .childSn(dto.getChildDeviceSn()) |
| | | .domain(StringUtils.hasText(dto.getDomain()) ? DeviceDomainEnum.getVal(dto.getDomain()) : null) |
| | | .domain(dto.getDomain()) |
| | | .firmwareVersion(dto.getFirmwareVersion()) |
| | | .compatibleStatus(dto.getFirmwareStatus() == null ? null : |
| | | DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE != DeviceFirmwareStatusEnum.find(dto.getFirmwareStatus())) |
| | |
| | | .build(); |
| | | } |
| | | |
| | | private Boolean firstSaveDevice(DeviceEntity device, String deviceSn) { |
| | | private Optional<DeviceEntity> onlineSaveDevice(DeviceEntity device, String childSn) { |
| | | |
| | | Optional<DeviceDTO> deviceOpt = this.getDeviceBySn(device.getDeviceSn()); |
| | | if (deviceOpt.isEmpty()) { |
| | |
| | | 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()); |
| | | } |
| | | |
| | | deviceOpt.ifPresent(oldDevice -> device.setNickname(oldDevice.getNickname())); |
| | | device.setChildSn(deviceSn); |
| | | device.setChildSn(childSn); |
| | | device.setLoginTime(System.currentTimeMillis()); |
| | | |
| | | Optional<DeviceEntity> saveDeviceOpt = this.saveDevice(device); |
| | | if (saveDeviceOpt.isEmpty()) { |
| | | return false; |
| | | return saveDeviceOpt; |
| | | } |
| | | device.setWorkspaceId(saveDeviceOpt.get().getWorkspaceId()); |
| | | |
| | |
| | | DeviceDTO.builder() |
| | | .deviceSn(device.getDeviceSn()) |
| | | .workspaceId(device.getWorkspaceId()) |
| | | .childDeviceSn(deviceSn) |
| | | .domain(DeviceDomainEnum.getDesc(device.getDomain())) |
| | | .childDeviceSn(childSn) |
| | | .domain(device.getDomain()) |
| | | .type(device.getDeviceType()) |
| | | .subType(device.getSubType()) |
| | | .build(), |
| | | RedisConst.DEVICE_ALIVE_SECOND); |
| | | |
| | | return true; |
| | | return saveDeviceOpt; |
| | | } |
| | | } |
| | | } |
| | |
| | | Collection<ConcurrentWebSocketSession> webSessions, |
| | | CustomWebSocketMessage<TelemetryDTO> wsMessage) { |
| | | |
| | | if (DeviceDomainEnum.DOCK.getDesc().equals(device.getDomain())) { |
| | | if (DeviceDomainEnum.DOCK.getVal() == device.getDomain()) { |
| | | wsMessage.setBizCode(BizCodeEnum.DOCK_OSD.getCode()); |
| | | OsdDockReceiver data = mapper.convertValue(receiver.getData(), OsdDockReceiver.class); |
| | | wsMessage.getData().setHost(data); |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.manage.dao.IFirmwareModelMapper; |
| | | import com.dji.sample.manage.model.dto.FirmwareModelDTO; |
| | | import com.dji.sample.manage.model.entity.FirmwareModelEntity; |
| | | import com.dji.sample.manage.service.IFirmwareModelService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/12/21 |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | public class FirmwareModelServiceImpl implements IFirmwareModelService { |
| | | |
| | | @Autowired |
| | | private IFirmwareModelMapper mapper; |
| | | |
| | | @Override |
| | | public void saveFirmwareDeviceName(FirmwareModelDTO firmwareModel) { |
| | | dto2Entity(firmwareModel).forEach(entity -> mapper.insert(entity)); |
| | | } |
| | | |
| | | private List<FirmwareModelEntity> dto2Entity(FirmwareModelDTO dto) { |
| | | if (Objects.isNull(dto) || CollectionUtils.isEmpty(dto.getDeviceNames())) { |
| | | return Collections.EMPTY_LIST; |
| | | } |
| | | return dto.getDeviceNames().stream() |
| | | .map(deviceName -> FirmwareModelEntity.builder() |
| | | .firmwareId(dto.getFirmwareId()) |
| | | .deviceName(deviceName).build()) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | } |
| | |
| | | public void handleOSD(CommonTopicReceiver receiver, DeviceDTO device, |
| | | Collection<ConcurrentWebSocketSession> webSessions, |
| | | CustomWebSocketMessage<TelemetryDTO> wsMessage) { |
| | | if (DeviceDomainEnum.GATEWAY.getDesc().equals(device.getDomain())) { |
| | | if (DeviceDomainEnum.GATEWAY.getVal() == device.getDomain()) { |
| | | |
| | | wsMessage.setBizCode(BizCodeEnum.GATEWAY_OSD.getCode()); |
| | | OsdGatewayReceiver data = mapper.convertValue(receiver.getData(), OsdGatewayReceiver.class); |
| | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.model.ServiceReply; |
| | | import com.dji.sample.component.mqtt.model.StateDataEnum; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | |
| | | // Solve timing problems |
| | | for (CapacityDeviceReceiver capacityDeviceReceiver : liveCapacityReceiver.getDeviceList()) { |
| | | long last = (long) Objects.requireNonNullElse( |
| | | RedisOpsUtils.get(StateDataEnum.LIVE_CAPACITY + RedisConst.DELIMITER + capacityDeviceReceiver.getSn()), 0L); |
| | | RedisOpsUtils.get(RedisConst.LIVE_CAPACITY + capacityDeviceReceiver.getSn()), 0L); |
| | | if (last > timestamp) { |
| | | return; |
| | | } |
| | |
| | | .toString()); |
| | | break; |
| | | case RTSP: |
| | | String url = receiveReply.getInfo().toString(); |
| | | this.resolveUrlUser(url, live); |
| | | Object url = Objects.requireNonNullElse(receiveReply.getOutput(), receiveReply.getInfo()); |
| | | this.resolveUrlUser(String.valueOf(url), live); |
| | | break; |
| | | case UNKNOWN: |
| | | return ResponseResult.error(LiveErrorEnum.URL_TYPE_NOT_SUPPORTED); |
| | |
| | | if (ResponseResult.CODE_SUCCESS != responseResult.getCode()) { |
| | | return responseResult; |
| | | } |
| | | if (DeviceDomainEnum.GATEWAY.getDesc().equals(responseResult.getData().getDomain())) { |
| | | if (DeviceDomainEnum.GATEWAY.getVal() == responseResult.getData().getDomain()) { |
| | | return ResponseResult.error(LiveErrorEnum.FUNCTION_NOT_SUPPORT); |
| | | } |
| | | |
| | |
| | | return ResponseResult.error(LiveErrorEnum.NO_AIRCRAFT); |
| | | } |
| | | |
| | | if (deviceOpt.get().getDomain().equals(DeviceDomainEnum.DOCK.getDesc())) { |
| | | if (DeviceDomainEnum.DOCK.getVal() == deviceOpt.get().getDomain()) { |
| | | return ResponseResult.success(deviceOpt.get()); |
| | | } |
| | | List<DeviceDTO> gatewayList = deviceService.getDevicesByParams( |
| | |
| | | String token = JwtUtil.createToken(customClaim.convertToMap()); |
| | | |
| | | UserDTO userDTO = entityConvertToDTO(userEntity); |
| | | userDTO.setMqttAddr(new StringBuilder() |
| | | .append(mqttConfiguration.getProtocol().trim()) |
| | | .append("://") |
| | | .append(mqttConfiguration.getHost().trim()) |
| | | .append(":") |
| | | .append(mqttConfiguration.getPort()) |
| | | .toString()); |
| | | userDTO.setMqttAddr(MqttConfiguration.getBasicMqttAddress()); |
| | | userDTO.setAccessToken(token); |
| | | userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId()); |
| | | return ResponseResult.success(userDTO); |
| | |
| | | .userType(entity.getUserType()) |
| | | .mqttUsername(entity.getMqttUsername()) |
| | | .mqttPassword(entity.getMqttPassword()) |
| | | .mqttAddr(new StringBuilder() |
| | | .append(mqttConfiguration.getProtocol().trim()) |
| | | .append("://") |
| | | .append(mqttConfiguration.getHost().trim()) |
| | | .append(":") |
| | | .append(mqttConfiguration.getPort()) |
| | | .toString()) |
| | | .mqttAddr(MqttConfiguration.getBasicMqttAddress()) |
| | | .build(); |
| | | } |
| | | } |
| | |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | |
| | | MediaFileCountDTO mediaFileCount = (MediaFileCountDTO) RedisOpsUtils.hashGet(RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway(), jobId); |
| | | // duplicate data |
| | | if (receiver.getBid().equals(mediaFileCount.getBid()) && receiver.getTid().equals(mediaFileCount.getTid())) { |
| | | System.out.println("相同" + receiver.getBid() + "\t tid:" + receiver.getTid()); |
| | | messageSenderService.publish(topic, data); |
| | | return; |
| | | } |
| | | |
| | | Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(jobId); |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); |
| | | Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(device.getWorkspaceId(), jobId); |
| | | if (jobOpt.isPresent()) { |
| | | boolean isSave = parseMediaFile(callback, jobOpt.get()); |
| | | if (!isSave) { |
| | |
| | | |
| | | // After uploading, delete the key with the highest priority. |
| | | String highestKey = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + receiver.getGateway(); |
| | | if (jobId.equals(Objects.requireNonNullElse(RedisOpsUtils.get(highestKey), ""))) { |
| | | if (RedisOpsUtils.checkExist(highestKey) && |
| | | jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(highestKey)).getJobId())) { |
| | | RedisOpsUtils.del(highestKey); |
| | | } |
| | | |
| | |
| | | return; |
| | | } |
| | | |
| | | String dockSn = receiver.getGateway(); |
| | | String jobId = map.get(MapKeyConst.FLIGHT_ID).toString(); |
| | | String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + dockSn; |
| | | Object preJobId = RedisOpsUtils.get(key); |
| | | |
| | | RedisOpsUtils.setWithExpire(key, jobId, |
| | | RedisConst.DEVICE_ALIVE_SECOND * 5); |
| | | |
| | | DeviceDTO dock = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn); |
| | | |
| | | sendMessageService.sendBatch(webSocketManageService.getValueWithWorkspaceAndUserType(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | CustomWebSocketMessage.builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode()) |
| | | .data(MediaFileCountDTO.builder() |
| | | .preJobId(Objects.nonNull(preJobId) ? preJobId.toString() : null) |
| | | .jobId(jobId).build()) |
| | | .build()); |
| | | |
| | | messageSenderService.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF, |
| | | CommonTopicResponse.builder() |
| | | .data(RequestsReply.success()) |
| | |
| | | .bid(receiver.getBid()) |
| | | .tid(receiver.getTid()) |
| | | .build()); |
| | | |
| | | 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; |
| | | } |
| | | |
| | | countDTO.setPreJobId(countDTO.getJobId()); |
| | | } |
| | | countDTO.setJobId(jobId); |
| | | |
| | | RedisOpsUtils.setWithExpire(key, countDTO, RedisConst.DEVICE_ALIVE_SECOND * 5); |
| | | |
| | | DeviceDTO dock = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn); |
| | | sendMessageService.sendBatch(webSocketManageService.getValueWithWorkspaceAndUserType(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | CustomWebSocketMessage.builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode()) |
| | | .data(countDTO) |
| | | .build()); |
| | | |
| | | } |
| | | } |
| | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.validation.Valid; |
| | | import java.sql.SQLException; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | |
| | | * @throws SQLException |
| | | */ |
| | | @DeleteMapping("/{workspace_id}/jobs") |
| | | public ResponseResult publishCancelJob(@RequestParam(name = "job_id") List<String> jobIds, |
| | | public ResponseResult publishCancelJob(@RequestParam(name = "job_id") Set<String> jobIds, |
| | | @PathVariable(name = "workspace_id") String workspaceId) throws SQLException { |
| | | waylineJobService.cancelFlightTask(workspaceId, jobIds); |
| | | return ResponseResult.success(); |
| | |
| | | |
| | | private LocalDateTime executeTime; |
| | | |
| | | private LocalDateTime beginTime; |
| | | |
| | | private LocalDateTime endTime; |
| | | |
| | | private LocalDateTime completedTime; |
| | | |
| | | private Integer status; |
| | | |
| | |
| | | private Integer uploadedCount; |
| | | |
| | | private Boolean uploading; |
| | | |
| | | private String parentId; |
| | | } |
| File was renamed from src/main/java/com/dji/sample/wayline/model/dto/FlightTaskCreateDTO.java |
| | |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class FlightTaskCreateDTO { |
| | | public class WaylineTaskCreateDTO { |
| | | |
| | | private String flightId; |
| | | |
| | |
| | | |
| | | private Long executeTime; |
| | | |
| | | private FlightTaskFileDTO file; |
| | | private WaylineTaskFileDTO file; |
| | | |
| | | private Integer rthAltitude; |
| | | |
| | | private Integer outOfControlAction; |
| | | |
| | | } |
| File was renamed from src/main/java/com/dji/sample/wayline/model/dto/FlightTaskFileDTO.java |
| | |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class FlightTaskFileDTO { |
| | | public class WaylineTaskFileDTO { |
| | | |
| | | private String url; |
| | | |
| File was renamed from src/main/java/com/dji/sample/wayline/model/dto/FLightTaskProgress.java |
| | |
| | | * @date 2022/6/9 |
| | | */ |
| | | @Data |
| | | public class FLightTaskProgress { |
| | | public class WaylineTaskProgress { |
| | | |
| | | private Integer currentStep; |
| | | |
| File was renamed from src/main/java/com/dji/sample/wayline/model/dto/FlightTaskProgressExt.java |
| | |
| | | * @date 2022/6/9 |
| | | */ |
| | | @Data |
| | | public class FlightTaskProgressExt { |
| | | public class WaylineTaskProgressExt { |
| | | |
| | | private Integer currentWaypointIndex; |
| | | |
| | | private Integer mediaCount; |
| | | |
| | | private String flightId; |
| | | |
| | | private String trackId; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.1 |
| | | * @date 2022/6/9 |
| | | */ |
| | | @Data |
| | | public class WaylineTaskProgressReceiver { |
| | | |
| | | private WaylineTaskProgressExt ext; |
| | | |
| | | private WaylineTaskProgress progress; |
| | | |
| | | private String status; |
| | | |
| | | } |
| | |
| | | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateTime; |
| | | |
| | | @TableField("begin_time") |
| | | private Long beginTime; |
| | | |
| | | @TableField("completed_time") |
| | | private Long completedTime; |
| | | |
| | | @TableField("parent_id") |
| | | private String parentId; |
| | | } |
| | |
| | | |
| | | FAILED(5, true), |
| | | |
| | | UNKNOWN(6, true); |
| | | PAUSED(6, false), |
| | | |
| | | UNKNOWN(-1, true); |
| | | |
| | | int val; |
| | | |
| | |
| | | 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 org.springframework.messaging.MessageHeaders; |
| | | |
| | | import java.sql.SQLException; |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | |
| | | /** |
| | | * Create wayline job in the database. |
| | | * @param param |
| | | * @param customClaim user info |
| | | * @param workspaceId user info |
| | | * @param username user info |
| | | * @param beginTime The time the job started. |
| | | * @param endTime The time the job ended. |
| | | * @return |
| | | */ |
| | | Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, CustomClaim customClaim); |
| | | Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, String workspaceId, String username, Long beginTime, Long endTime); |
| | | |
| | | /** |
| | | * Create a sub-task based on the information of the parent task. |
| | | * @param workspaceId |
| | | * @param parentId |
| | | * @return |
| | | */ |
| | | Optional<WaylineJobDTO> createWaylineJobByParent(String workspaceId, String parentId); |
| | | |
| | | /** |
| | | * Issue wayline mission to the dock. |
| | |
| | | * @throws SQLException |
| | | * @return |
| | | */ |
| | | Boolean executeFlightTask(String jobId); |
| | | Boolean executeFlightTask(String workspaceId, String jobId); |
| | | |
| | | /** |
| | | * Cancel the task Base on job Ids. |
| | |
| | | void cancelFlightTask(String workspaceId, Collection<String> 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<String> jobIds); |
| | | |
| | | /** |
| | | * Query wayline jobs based on conditions. |
| | | * @param workspaceId |
| | | * @param jobIds |
| | | * @param status |
| | | * @return |
| | | */ |
| | | List<WaylineJobDTO> getJobsByConditions(String workspaceId, Collection<String> jobIds, WaylineJobStatusEnum status); |
| | | |
| | | /** |
| | | * Query job information based on job id. |
| | | * @param workspaceId |
| | | * @param jobId |
| | | * @return job information |
| | | */ |
| | | Optional<WaylineJobDTO> getJobByJobId(String jobId); |
| | | Optional<WaylineJobDTO> getJobByJobId(String workspaceId, String jobId); |
| | | |
| | | /** |
| | | * Update job data. |
| | |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.media.model.MediaFileCountDTO; |
| | | import com.dji.sample.wayline.model.dto.FlightTaskProgressReceiver; |
| | | import com.dji.sample.wayline.model.dto.WaylineJobDTO; |
| | | import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; |
| | | import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; |
| | | import com.dji.sample.wayline.service.IFlightTaskService; |
| | | import com.dji.sample.wayline.service.IWaylineJobService; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.Objects; |
| | | import java.util.*; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | |
| | | @Override |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS, outputChannel = ChannelName.OUTBOUND) |
| | | public void handleProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | EventsReceiver<FlightTaskProgressReceiver> eventsReceiver = mapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<FlightTaskProgressReceiver>>(){}); |
| | | String receivedTopic = String.valueOf(headers.get(MqttHeaders.RECEIVED_TOPIC)); |
| | | String dockSn = receivedTopic.substring((TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT).length(), |
| | | receivedTopic.indexOf(TopicConst.EVENTS_SUF)); |
| | | EventsReceiver<WaylineTaskProgressReceiver> eventsReceiver = mapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<WaylineTaskProgressReceiver>>(){}); |
| | | eventsReceiver.setBid(receiver.getBid()); |
| | | eventsReceiver.setSn(receiver.getGateway()); |
| | | |
| | | FlightTaskProgressReceiver output = eventsReceiver.getOutput(); |
| | | WaylineTaskProgressReceiver output = eventsReceiver.getOutput(); |
| | | |
| | | log.info("Task progress: {}", output.getProgress().toString()); |
| | | |
| | |
| | | } |
| | | |
| | | EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus()); |
| | | String key = RedisConst.WAYLINE_JOB_RUNNING_PREFIX + dockSn; |
| | | RedisOpsUtils.setWithExpire(key, eventsReceiver, RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | |
| | | if (statusEnum.getEnd()) { |
| | | WaylineJobDTO job = WaylineJobDTO.builder() |
| | | .jobId(receiver.getBid()) |
| | | .status(WaylineJobStatusEnum.SUCCESS.getVal()) |
| | | .endTime(LocalDateTime.now()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .mediaCount(output.getExt().getMediaCount()) |
| | | .build(); |
| | | |
| | | // record the update of the media count. |
| | | if (Objects.nonNull(job.getMediaCount())) { |
| | | 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()); |
| | | } |
| | |
| | | } |
| | | |
| | | waylineJobService.updateJob(job); |
| | | RedisOpsUtils.del(receiver.getBid()); |
| | | RedisOpsUtils.del(key); |
| | | RedisOpsUtils.del(RedisConst.WAYLINE_JOB_PAUSED_PREFIX + receiver.getBid()); |
| | | } |
| | | RedisOpsUtils.setWithExpire(receiver.getBid(), eventsReceiver, RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); |
| | | websocketMessageService.sendBatch( |
| | |
| | | .build()); |
| | | |
| | | if (receiver.getNeedReply() == 1) { |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF; |
| | | messageSender.publish(topic, |
| | | messageSender.publish(receivedTopic + TopicConst._REPLY_SUF, |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | |
| | | |
| | | @Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS) |
| | | private void checkScheduledJob() { |
| | | Object jobIdValue = RedisOpsUtils.zGetMin(RedisConst.WAYLINE_JOB); |
| | | log.info("Check the timed jobs of the wayline. {}", jobIdValue); |
| | | Object jobIdValue = RedisOpsUtils.zGetMin(RedisConst.WAYLINE_JOB_TIMED_EXECUTE); |
| | | if (Objects.isNull(jobIdValue)) { |
| | | return; |
| | | } |
| | | String jobId = String.valueOf(jobIdValue); |
| | | double time = RedisOpsUtils.zScore(RedisConst.WAYLINE_JOB, jobIdValue); |
| | | 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, jobId); |
| | | RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, jobIdValue); |
| | | waylineJobService.updateJob(WaylineJobDTO.builder() |
| | | .jobId(jobId) |
| | | .jobId(jobArr[2]) |
| | | .status(WaylineJobStatusEnum.FAILED.getVal()) |
| | | .endTime(LocalDateTime.now()) |
| | | .executeTime(LocalDateTime.now()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .code(HttpStatus.SC_REQUEST_TIMEOUT).build()); |
| | | return; |
| | | } |
| | | |
| | | if (now <= time && time <= now + offset) { |
| | | try { |
| | | waylineJobService.executeFlightTask(jobId); |
| | | waylineJobService.executeFlightTask(jobArr[0], jobArr[2]); |
| | | } catch (Exception e) { |
| | | log.info("The scheduled task delivery failed."); |
| | | waylineJobService.updateJob(WaylineJobDTO.builder() |
| | | .jobId(jobId) |
| | | .jobId(jobArr[2]) |
| | | .status(WaylineJobStatusEnum.FAILED.getVal()) |
| | | .endTime(LocalDateTime.now()) |
| | | .executeTime(LocalDateTime.now()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .code(HttpStatus.SC_INTERNAL_SERVER_ERROR).build()); |
| | | } finally { |
| | | RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB, jobId); |
| | | RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, jobIdValue); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | ZipEntry nextEntry = unzipFile.getNextEntry(); |
| | | while (Objects.nonNull(nextEntry)) { |
| | | boolean isWaylines = (KmzFileProperties.FILE_DIR_FIRST + File.separator + KmzFileProperties.FILE_DIR_SECOND_WAYLINES).equals(nextEntry.getName()); |
| | | boolean isWaylines = (KmzFileProperties.FILE_DIR_FIRST + "/" + KmzFileProperties.FILE_DIR_SECOND_WAYLINES).equals(nextEntry.getName()); |
| | | if (!isWaylines) { |
| | | nextEntry = unzipFile.getNextEntry(); |
| | | continue; |
| | |
| | | |
| | | import java.net.URL; |
| | | import java.sql.SQLException; |
| | | import java.time.Instant; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.time.*; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | |
| | | @Autowired |
| | | private IFileService fileService; |
| | | |
| | | @Override |
| | | public Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, CustomClaim customClaim) { |
| | | if (Objects.isNull(param)) { |
| | | return Optional.empty(); |
| | | } |
| | | // Immediate tasks, allocating time on the backend. |
| | | if (Objects.isNull(param.getExecuteTime())) { |
| | | param.setExecuteTime(System.currentTimeMillis()); |
| | | } |
| | | WaylineJobEntity jobEntity = WaylineJobEntity.builder() |
| | | .name(param.getName()) |
| | | .dockSn(param.getDockSn()) |
| | | .fileId(param.getFileId()) |
| | | .username(customClaim.getUsername()) |
| | | .workspaceId(customClaim.getWorkspaceId()) |
| | | .jobId(UUID.randomUUID().toString()) |
| | | .executeTime(param.getExecuteTime()) |
| | | .status(WaylineJobStatusEnum.PENDING.getVal()) |
| | | .taskType(param.getTaskType()) |
| | | .waylineType(param.getWaylineType()) |
| | | .outOfControlAction(param.getOutOfControlAction()) |
| | | .rthAltitude(param.getRthAltitude()) |
| | | .mediaCount(0) |
| | | .build(); |
| | | private Optional<WaylineJobDTO> insertWaylineJob(WaylineJobEntity jobEntity) { |
| | | int id = mapper.insert(jobEntity); |
| | | if (id <= 0) { |
| | | return Optional.empty(); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, String workspaceId, String username, Long beginTime, Long endTime) { |
| | | if (Objects.isNull(param)) { |
| | | return Optional.empty(); |
| | | } |
| | | // Immediate tasks, allocating time on the backend. |
| | | WaylineJobEntity jobEntity = WaylineJobEntity.builder() |
| | | .name(param.getName()) |
| | | .dockSn(param.getDockSn()) |
| | | .fileId(param.getFileId()) |
| | | .username(username) |
| | | .workspaceId(workspaceId) |
| | | .jobId(UUID.randomUUID().toString()) |
| | | .beginTime(beginTime) |
| | | .endTime(endTime) |
| | | .status(WaylineJobStatusEnum.PENDING.getVal()) |
| | | .taskType(param.getTaskType()) |
| | | .waylineType(param.getWaylineType()) |
| | | .outOfControlAction(param.getOutOfControlAction()) |
| | | .rthAltitude(param.getRthAltitude()) |
| | | .mediaCount(0) |
| | | .build(); |
| | | |
| | | return insertWaylineJob(jobEntity); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<WaylineJobDTO> createWaylineJobByParent(String workspaceId, String parentId) { |
| | | Optional<WaylineJobDTO> parentJobOpt = this.getJobByJobId(workspaceId, parentId); |
| | | if (parentJobOpt.isEmpty()) { |
| | | return Optional.empty(); |
| | | } |
| | | WaylineJobEntity jobEntity = this.dto2Entity(parentJobOpt.get()); |
| | | jobEntity.setJobId(UUID.randomUUID().toString()); |
| | | jobEntity.setErrorCode(null); |
| | | jobEntity.setCompletedTime(null); |
| | | jobEntity.setExecuteTime(null); |
| | | jobEntity.setStatus(WaylineJobStatusEnum.PENDING.getVal()); |
| | | jobEntity.setParentId(parentId); |
| | | |
| | | return this.insertWaylineJob(jobEntity); |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException { |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.createWaylineJob(param, customClaim); |
| | | if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == param.getTaskType()) { |
| | | param.setExecuteTime(System.currentTimeMillis()); |
| | | } |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.createWaylineJob(param, |
| | | customClaim.getWorkspaceId(), customClaim.getUsername(), |
| | | param.getExecuteTime(), param.getExecuteTime()); |
| | | if (waylineJobOpt.isEmpty()) { |
| | | throw new SQLException("Failed to create wayline job."); |
| | | } |
| | |
| | | // get file url |
| | | URL url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getWaylineId()); |
| | | |
| | | FlightTaskCreateDTO flightTask = FlightTaskCreateDTO.builder() |
| | | WaylineTaskCreateDTO flightTask = WaylineTaskCreateDTO.builder() |
| | | .flightId(waylineJob.getJobId()) |
| | | .executeTime(waylineJob.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) |
| | | .executeTime(waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) |
| | | .taskType(waylineJob.getTaskType()) |
| | | .waylineType(waylineJob.getWaylineType()) |
| | | .rthAltitude(waylineJob.getRthAltitude()) |
| | | .outOfControlAction(waylineJob.getOutOfControlAction()) |
| | | .file(FlightTaskFileDTO.builder() |
| | | .file(WaylineTaskFileDTO.builder() |
| | | .url(url.toString()) |
| | | .fingerprint(waylineFile.get().getSign()) |
| | | .build()) |
| | |
| | | this.updateJob(WaylineJobDTO.builder() |
| | | .workspaceId(waylineJob.getWorkspaceId()) |
| | | .jobId(waylineJob.getJobId()) |
| | | .executeTime(LocalDateTime.now()) |
| | | .status(WaylineJobStatusEnum.FAILED.getVal()) |
| | | .endTime(LocalDateTime.now()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .code(serviceReply.getResult()).build()); |
| | | return ResponseResult.error("Prepare task ====> Error code: " + serviceReply.getResult()); |
| | | } |
| | | |
| | | // Issue an immediate task execution command. |
| | | if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == waylineJob.getTaskType()) { |
| | | if (!executeFlightTask(waylineJob.getJobId())) { |
| | | if (!executeFlightTask(waylineJob.getWorkspaceId(), waylineJob.getJobId())) { |
| | | return ResponseResult.error("Failed to execute job."); |
| | | } |
| | | } |
| | | |
| | | if (WaylineTaskTypeEnum.TIMED.getVal() == waylineJob.getTaskType()) { |
| | | boolean isAdd = RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB, waylineJob.getJobId(), |
| | | waylineJob.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | 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 ResponseResult.error("Failed to create scheduled job."); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public Boolean executeFlightTask(String jobId) { |
| | | public Boolean executeFlightTask(String workspaceId, String jobId) { |
| | | // get job |
| | | Optional<WaylineJobDTO> waylineJob = this.getJobByJobId(jobId); |
| | | Optional<WaylineJobDTO> waylineJob = this.getJobByJobId(workspaceId, jobId); |
| | | if (waylineJob.isEmpty()) { |
| | | throw new IllegalArgumentException("Job doesn't exist."); |
| | | } |
| | |
| | | } |
| | | |
| | | WaylineJobDTO job = waylineJob.get(); |
| | | FlightTaskCreateDTO flightTask = FlightTaskCreateDTO.builder().flightId(jobId).build(); |
| | | WaylineTaskCreateDTO flightTask = WaylineTaskCreateDTO.builder().flightId(jobId).build(); |
| | | |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + |
| | | job.getDockSn() + TopicConst.SERVICES_SUF; |
| | |
| | | log.info("Execute job ====> Error code: {}", serviceReply.getResult()); |
| | | this.updateJob(WaylineJobDTO.builder() |
| | | .jobId(jobId) |
| | | .executeTime(LocalDateTime.now()) |
| | | .status(WaylineJobStatusEnum.FAILED.getVal()) |
| | | .endTime(LocalDateTime.now()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .code(serviceReply.getResult()).build()); |
| | | return false; |
| | | } |
| | | |
| | | this.updateJob(WaylineJobDTO.builder() |
| | | .jobId(jobId) |
| | | .executeTime(LocalDateTime.now()) |
| | | .status(WaylineJobStatusEnum.IN_PROGRESS.getVal()) |
| | | .build()); |
| | | RedisOpsUtils.setWithExpire(jobId, |
| | | EventsReceiver.<FlightTaskProgressReceiver>builder().bid(jobId).sn(job.getDockSn()).build(), |
| | | RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + job.getDockSn(), |
| | | EventsReceiver.<WaylineTaskProgressReceiver>builder().bid(jobId).sn(job.getDockSn()).build(), |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public void cancelFlightTask(String workspaceId, Collection<String> jobIds) { |
| | | List<WaylineJobEntity> waylineJobs = mapper.selectList( |
| | | new LambdaQueryWrapper<WaylineJobEntity>() |
| | | .or(wrapper -> jobIds.forEach(id -> wrapper.eq(WaylineJobEntity::getJobId, id)))); |
| | | List<WaylineJobDTO> waylineJobs = getJobsByConditions(workspaceId, jobIds, WaylineJobStatusEnum.PENDING); |
| | | |
| | | // Check if the job have ended. |
| | | List<String> endJobs = waylineJobs.stream() |
| | | .filter(job -> WaylineJobStatusEnum.find(job.getStatus()).getEnd()) |
| | | .map(WaylineJobEntity::getName) |
| | | .collect(Collectors.toList()); |
| | | if (!CollectionUtils.isEmpty(endJobs)) { |
| | | throw new IllegalArgumentException("There are jobs that have ended." + Arrays.toString(endJobs.toArray())); |
| | | } |
| | | |
| | | Set<String> ids = waylineJobs.stream().map(WaylineJobEntity::getJobId).collect(Collectors.toSet()); |
| | | for (String id : jobIds) { |
| | | if (!ids.contains(id)) { |
| | | throw new IllegalArgumentException("Job id " + id + " doesn't exist."); |
| | | } |
| | | Set<String> 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<String, List<String>> dockJobs = waylineJobs.stream() |
| | | .collect(Collectors.groupingBy(WaylineJobEntity::getDockSn, |
| | | Collectors.mapping(WaylineJobEntity::getJobId, Collectors.toList()))); |
| | | .collect(Collectors.groupingBy(WaylineJobDTO::getDockSn, |
| | | Collectors.mapping(WaylineJobDTO::getJobId, Collectors.toList()))); |
| | | dockJobs.forEach((dockSn, idList) -> this.publishCancelTask(workspaceId, dockSn, idList)); |
| | | |
| | | } |
| | | |
| | | private void publishCancelTask(String workspaceId, String dockSn, List<String> jobIds) { |
| | | public void publishCancelTask(String workspaceId, String dockSn, List<String> jobIds) { |
| | | boolean isOnline = deviceService.checkDeviceOnline(dockSn); |
| | | if (!isOnline) { |
| | | throw new RuntimeException("Dock is offline."); |
| | |
| | | .workspaceId(workspaceId) |
| | | .jobId(jobId) |
| | | .status(WaylineJobStatusEnum.CANCEL.getVal()) |
| | | .endTime(LocalDateTime.now()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .build()); |
| | | RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB, jobId); |
| | | RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, workspaceId + RedisConst.DELIMITER + dockSn + RedisConst.DELIMITER + jobId); |
| | | } |
| | | |
| | | } |
| | | |
| | | public List<WaylineJobDTO> getJobsByConditions(String workspaceId, Collection<String> jobIds, WaylineJobStatusEnum status) { |
| | | return mapper.selectList( |
| | | new LambdaQueryWrapper<WaylineJobEntity>() |
| | | .eq(WaylineJobEntity::getWorkspaceId, workspaceId) |
| | | .eq(Objects.nonNull(status), WaylineJobEntity::getStatus, status.getVal()) |
| | | .and(!CollectionUtils.isEmpty(jobIds), |
| | | wrapper -> jobIds.forEach(id -> wrapper.eq(WaylineJobEntity::getJobId, id).or()))) |
| | | .stream() |
| | | .map(this::entity2Dto) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<WaylineJobDTO> getJobByJobId(String jobId) { |
| | | public Optional<WaylineJobDTO> getJobByJobId(String workspaceId, String jobId) { |
| | | WaylineJobEntity jobEntity = mapper.selectOne( |
| | | new LambdaQueryWrapper<WaylineJobEntity>() |
| | | .eq(WaylineJobEntity::getWorkspaceId, workspaceId) |
| | | .eq(WaylineJobEntity::getJobId, jobId)); |
| | | return Optional.ofNullable(entity2Dto(jobEntity)); |
| | | } |
| | |
| | | |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString() + TopicConst._REPLY_SUF; |
| | | |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.getJobByJobId(jobId); |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.getJobByJobId(device.getWorkspaceId(), jobId); |
| | | if (waylineJobOpt.isEmpty()) { |
| | | builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); |
| | | messageSender.publish(topic, builder.build()); |
| | |
| | | URL url = null; |
| | | try { |
| | | url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getWaylineId()); |
| | | builder.data(RequestsReply.success(FlightTaskCreateDTO.builder() |
| | | .file(FlightTaskFileDTO.builder() |
| | | builder.data(RequestsReply.success(WaylineTaskCreateDTO.builder() |
| | | .file(WaylineTaskFileDTO.builder() |
| | | .url(url.toString()) |
| | | .fingerprint(waylineFile.get().getSign()) |
| | | .build()) |
| | |
| | | |
| | | @Override |
| | | public void uploadMediaHighestPriority(String workspaceId, String jobId) { |
| | | Optional<WaylineJobDTO> jobOpt = getJobByJobId(jobId); |
| | | Optional<WaylineJobDTO> 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(RedisOpsUtils.get(key).toString())) { |
| | | if (RedisOpsUtils.checkExist(key) && |
| | | jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(key)).getJobId())) { |
| | | return; |
| | | } |
| | | |
| | |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | throw new RuntimeException("Failed to set media job upload priority. Error Code: " + reply.getResult()); |
| | | } |
| | | RedisOpsUtils.setWithExpire(key, jobId, RedisConst.DEVICE_ALIVE_SECOND * 5); |
| | | } |
| | | |
| | | private WaylineJobEntity dto2Entity(WaylineJobDTO dto) { |
| | |
| | | if (dto == null) { |
| | | return builder.build(); |
| | | } |
| | | if (Objects.nonNull(dto.getBeginTime())) { |
| | | builder.beginTime(dto.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | } |
| | | if (Objects.nonNull(dto.getEndTime())) { |
| | | builder.endTime(dto.getEndTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | } |
| | | if (Objects.nonNull(dto.getExecuteTime())) { |
| | | builder.executeTime(dto.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | } |
| | | if (Objects.nonNull(dto.getCompletedTime())) { |
| | | builder.completedTime(dto.getCompletedTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | } |
| | | return builder.status(dto.getStatus()) |
| | | .mediaCount(dto.getMediaCount()) |
| | | .name(dto.getJobName()) |
| | | .errorCode(dto.getCode()) |
| | | .jobId(dto.getJobId()) |
| | | .fileId(dto.getFileId()) |
| | | .dockSn(dto.getDockSn()) |
| | | .workspaceId(dto.getWorkspaceId()) |
| | | .taskType(dto.getTaskType()) |
| | | .waylineType(dto.getWaylineType()) |
| | | .username(dto.getUsername()) |
| | | .rthAltitude(dto.getRthAltitude()) |
| | | .outOfControlAction(dto.getOutOfControlAction()) |
| | | .parentId(dto.getParentId()) |
| | | .build(); |
| | | } |
| | | |
| | |
| | | .orElse(DeviceDTO.builder().build()).getNickname()) |
| | | .username(entity.getUsername()) |
| | | .workspaceId(entity.getWorkspaceId()) |
| | | .status(entity.getStatus()) |
| | | .status(WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && |
| | | RedisOpsUtils.checkExist(RedisConst.WAYLINE_JOB_PAUSED_PREFIX + entity.getJobId()) ? |
| | | WaylineJobStatusEnum.PAUSED.getVal() : entity.getStatus()) |
| | | .code(entity.getErrorCode()) |
| | | .executeTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getExecuteTime()), ZoneId.systemDefault())) |
| | | .beginTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getBeginTime()), ZoneId.systemDefault())) |
| | | .endTime(Objects.nonNull(entity.getEndTime()) ? |
| | | LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getEndTime()), ZoneId.systemDefault()) : null) |
| | | .executeTime(Objects.nonNull(entity.getExecuteTime()) ? |
| | | 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(entity.getTaskType()) |
| | | .waylineType(entity.getWaylineType()) |
| | | .rthAltitude(entity.getRthAltitude()) |
| | |
| | | builder.endTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getEndTime()), ZoneId.systemDefault())); |
| | | } |
| | | if (WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && RedisOpsUtils.getExpire(entity.getJobId()) > 0) { |
| | | EventsReceiver<FlightTaskProgressReceiver> taskProgress = (EventsReceiver<FlightTaskProgressReceiver>) RedisOpsUtils.get(entity.getJobId()); |
| | | EventsReceiver<WaylineTaskProgressReceiver> taskProgress = (EventsReceiver<WaylineTaskProgressReceiver>) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + entity.getDockSn()); |
| | | if (Objects.nonNull(taskProgress.getOutput()) && Objects.nonNull(taskProgress.getOutput().getProgress())) { |
| | | builder.progress(taskProgress.getOutput().getProgress().getPercent()); |
| | | } |
| | |
| | | Object mediaFileCount = RedisOpsUtils.hashGet(countKey, entity.getJobId()); |
| | | if (Objects.nonNull(mediaFileCount)) { |
| | | builder.uploadedCount(((MediaFileCountDTO) mediaFileCount).getUploadedCount()) |
| | | .uploading(RedisOpsUtils.checkExist(key) && entity.getJobId().equals(RedisOpsUtils.get(key))); |
| | | .uploading(RedisOpsUtils.checkExist(key) && entity.getJobId().equals(((MediaFileCountDTO)RedisOpsUtils.get(key)).getJobId())); |
| | | return builder.build(); |
| | | } |
| | | |
| | |
| | | age: 86400 |
| | | |
| | | mqtt: |
| | | protocol: tcp |
| | | host: Please enter your ip. # 192.168.1.1 |
| | | port: 1883 |
| | | username: JavaServer |
| | | password: 123456 |
| | | client-id: 123456 |
| | | # Topics that need to be subscribed when initially connecting to mqtt, multiple topics are divided by ",". |
| | | inbound-topic: sys/product/+/status,thing/product/+/requests |
| | | # @see com.dji.sample.component.mqtt.model.MqttUseEnum |
| | | # BASIC parameters are required. |
| | | BASIC: |
| | | protocol: MQTT # @see com.dji.sample.component.mqtt.model.MqttProtocolEnum |
| | | host: Please enter your ip. |
| | | port: 1883 |
| | | username: JavaServer |
| | | password: 123456 |
| | | 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 |
| | | |
| | | url: |
| | | manage: |
| | |
| | | prefix: /control |
| | | version: /api/v1 |
| | | |
| | | # Tutorial: https://help.aliyun.com/document_detail/100624.htm?spm=a2c4g.11186623.0.0.74075e34eIhK7T#concept-xzh-nzk-2gb |
| | | # Tutorial: https://www.alibabacloud.com/help/en/object-storage-service/latest/use-a-temporary-credential-provided-by-sts-to-access-oss |
| | | oss: |
| | | enable: true |
| | | provider: ali # @see com.dji.sample.component.OssConfiguration.model.enums.OssTypeEnum |
| | |
| | | |
| | | ntp: |
| | | server: |
| | | host: Google.mzr.me |
| | | host: Google.mzr.me |
| | | |
| | | # To create a license for an application: https://developer.dji.com/user/apps/#all |
| | | cloud-api: |
| | | app: |
| | | id: Please enter the app id. |
| | | key: Please enter the app key. |
| | | license: Please enter the app license. |