增加天气阻飞

This commit is contained in:
sdy 2026-06-17 17:04:20 +08:00
parent 305e40da0f
commit 1591170585
30 changed files with 1378 additions and 37 deletions

View File

@ -0,0 +1,15 @@
package com.multictrl.common.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum FlightTaskType {
ROUTE(1, "航线飞行"),
MANUAL(2, "手动飞行"),
SCHEDULED(3, "定时飞行");
private final Integer code;
private final String desc;
}

View File

@ -13,6 +13,9 @@ public interface SysParamsKey {
String ZHIMOU_AI_APP_KEY = "zhimou_ai_app_key"; String ZHIMOU_AI_APP_KEY = "zhimou_ai_app_key";
String ZHIMOU_AI_APP_SECRET = "zhimou_ai_app_secret"; String ZHIMOU_AI_APP_SECRET = "zhimou_ai_app_secret";
//********************************* gaode *********************************//
String GAODE_API_KEY = "gaode_api_key";
//********************************* sys component *********************************// //********************************* sys component *********************************//
String SRS_RTMP_MAP_URL = "srs_rtmp_map_url"; String SRS_RTMP_MAP_URL = "srs_rtmp_map_url";
String EMQX_MAP_URL = "emqx_map_url"; String EMQX_MAP_URL = "emqx_map_url";

View File

@ -0,0 +1,70 @@
package com.multictrl.common.utils.weather;
/**
* 雾量工具类
*
* @author Sdy
* @since 1.0.0 2025/10/13
*/
public class FogUtils {
// 轻雾类天气现象
public static final String[] LIGHT_FOG_TYPES = {
"轻雾"
};
// 中雾类天气现象
public static final String[] MODERATE_FOG_TYPES = {
"",
"浓雾"
};
// 大雾类天气现象
public static final String[] HEAVY_FOG_TYPES = {
"强浓雾",
"大雾",
"特强浓雾"
};
/**
* 判断是否为轻雾类
*/
public static boolean isMinFog(String fogType) {
return contains(LIGHT_FOG_TYPES, fogType);
}
/**
* 判断是否为中雾类
*/
public static boolean isModerateFog(String fogType) {
return contains(MODERATE_FOG_TYPES, fogType);
}
/**
* 判断是否为大雾类
*/
public static boolean isMaxFog(String fogType) {
return contains(HEAVY_FOG_TYPES, fogType);
}
/**
* 获取雾类型所属的类别名称
*/
public static String getCategory(String fogType) {
if (isMinFog(fogType)) return "轻雾";
if (isModerateFog(fogType)) return "中雾";
if (isMaxFog(fogType)) return "大雾";
return "未知";
}
/**
* 检查字符串是否在数组中
*/
private static boolean contains(String[] array, String value) {
for (String item : array) {
if (item.equals(value)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,69 @@
package com.multictrl.common.utils.weather;
/**
* 霾量工具类
*
* @author Sdy
* @since 1.0.0 2025/10/13
*/
public class HazeUtils {
// 轻度霾类天气现象
public static final String[] LIGHT_HAZE_TYPES = {
""
};
// 中度霾类天气现象
public static final String[] MODERATE_HAZE_TYPES = {
"中度霾"
};
// 重度霾类天气现象
public static final String[] HEAVY_HAZE_TYPES = {
"重度霾",
"严重霾"
};
/**
* 判断是否为轻度霾类
*/
public static boolean isMinHaze(String hazeType) {
return contains(LIGHT_HAZE_TYPES, hazeType);
}
/**
* 判断是否为中度霾类
*/
public static boolean isModerateHaze(String hazeType) {
return contains(MODERATE_HAZE_TYPES, hazeType);
}
/**
* 判断是否为重度霾类
*/
public static boolean isMaxHaze(String hazeType) {
return contains(HEAVY_HAZE_TYPES, hazeType);
}
/**
* 获取霾类型所属的类别名称
*/
public static String getCategory(String hazeType) {
if (isMinHaze(hazeType)) return "轻度霾";
if (isModerateHaze(hazeType)) return "中度霾";
if (isMaxHaze(hazeType)) return "重度霾";
return "未知";
}
/**
* 检查字符串是否在数组中
*/
private static boolean contains(String[] array, String value) {
for (String item : array) {
if (item.equals(value)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,87 @@
package com.multictrl.common.utils.weather;
/**
* 雨量工具类
*
* @author Sdy
* @since 1.0.0 2025/10/13
*/
public class RainfallUtils {
// 小雨类天气现象
public static final String[] LIGHT_RAIN_TYPES = {
"毛毛雨/细雨",
"小雨",
"阵雨",
"雷阵雨",
"雨雪天气",
"雨夹雪",
"阵雨夹雪",
"冻雨"
};
// 中雨类天气现象
public static final String[] MODERATE_RAIN_TYPES = {
"中雨",
"小雨-中雨",
"雷阵雨并伴有冰雹"
};
// 大雨类天气现象
public static final String[] HEAVY_RAIN_TYPES = {
"大雨",
"中雨-大雨",
"强阵雨",
"强雷阵雨",
"暴雨",
"大暴雨",
"特大暴雨",
"极端降雨",
"大雨-暴雨",
"暴雨-大暴雨",
"大暴雨-特大暴雨"
};
/**
* 判断是否为小雨类
*/
public static boolean isMinRain(String precipitation) {
return contains(LIGHT_RAIN_TYPES, precipitation);
}
/**
* 判断是否为中雨类
*/
public static boolean isModerateRain(String precipitation) {
return contains(MODERATE_RAIN_TYPES, precipitation);
}
/**
* 判断是否为大雨类
*/
public static boolean isMaxRain(String precipitation) {
return contains(HEAVY_RAIN_TYPES, precipitation);
}
/**
* 获取降水类型所属的类别名称
*/
public static String getCategory(String precipitation) {
if (isMinRain(precipitation)) return "小雨";
if (isModerateRain(precipitation)) return "中雨";
if (isMaxRain(precipitation)) return "大雨";
return "未知";
}
/**
* 检查字符串是否在数组中
*/
private static boolean contains(String[] array, String value) {
for (String item : array) {
if (item.equals(value)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,76 @@
package com.multictrl.common.utils.weather;
/**
* 雪量工具类
*
* @author Sdy
* @since 1.0.0 2025/10/13
*/
public class SnowUtils {
// 小雪类天气现象
public static final String[] LIGHT_SNOW_TYPES = {
"",
"阵雪",
"小雪",
"雨雪天气",
"雨夹雪",
"阵雨夹雪"
};
// 中雪类天气现象
public static final String[] MODERATE_SNOW_TYPES = {
"中雪",
"小雪-中雪"
};
// 大雪类天气现象
public static final String[] HEAVY_SNOW_TYPES = {
"大雪",
"暴雪",
"中雪-大雪",
"大雪-暴雪"
};
/**
* 判断是否为小雪类
*/
public static boolean isMinSnow(String snowType) {
return contains(LIGHT_SNOW_TYPES, snowType);
}
/**
* 判断是否为中雪类
*/
public static boolean isModerateSnow(String snowType) {
return contains(MODERATE_SNOW_TYPES, snowType);
}
/**
* 判断是否为大雪类
*/
public static boolean isMaxSnow(String snowType) {
return contains(HEAVY_SNOW_TYPES, snowType);
}
/**
* 获取雪类型所属的类别名称
*/
public static String getCategory(String snowType) {
if (isMinSnow(snowType)) return "小雪";
if (isModerateSnow(snowType)) return "中雪";
if (isMaxSnow(snowType)) return "大雪";
return "未知";
}
/**
* 检查字符串是否在数组中
*/
private static boolean contains(String[] array, String value) {
for (String item : array) {
if (item.equals(value)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,51 @@
package com.multictrl.common.utils.weather;
/**
* 飞速级别转换
*
* @author Sdy
* @since 1.0.0 2025/10/12
*/
public class WindSpeedConverter {
/**
* 根据级别名称获取最大风速
*
* @param levelName 级别名称 ("≤3", "4", "5", ..., "12")
* @return 最大风速 (m/s)
*/
public static double getMaxWindSpeed(String levelName) {
return switch (levelName) {
case "≤3" -> 5.4;
case "4" -> 7.9;
case "5" -> 10.7;
case "6" -> 13.8;
case "7" -> 17.1;
case "8" -> 20.7;
case "9" -> 24.4;
case "10" -> 28.4;
case "11" -> 32.6;
case "12" -> 56.0;
default -> throw new IllegalArgumentException("无效的级别名称: " + levelName);
};
}
/**
* 获取指定级别的最小风速
*/
public static double getMinWindSpeed(String levelName) {
return switch (levelName) {
case "≤3" -> 0.0;
case "4" -> 5.5;
case "5" -> 8.0;
case "6" -> 10.8;
case "7" -> 13.9;
case "8" -> 17.2;
case "9" -> 20.8;
case "10" -> 24.5;
case "11" -> 28.5;
case "12" -> 32.7;
default -> throw new IllegalArgumentException("无效的级别名称: " + levelName);
};
}
}

View File

@ -2,6 +2,7 @@ package com.multictrl.modules.business.controller;
import com.multictrl.common.annotation.ApiOrder; import com.multictrl.common.annotation.ApiOrder;
import com.multictrl.common.annotation.LogOperation; import com.multictrl.common.annotation.LogOperation;
import com.multictrl.common.constant.FlightTaskType;
import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.utils.Result; import com.multictrl.common.utils.Result;
import com.multictrl.modules.business.dto.flight.FlightExecute; import com.multictrl.modules.business.dto.flight.FlightExecute;
@ -25,10 +26,10 @@ import java.util.List;
@RestController @RestController
@RequestMapping("business/command") @RequestMapping("business/command")
@Tag(name = "航线飞行", description = """ @Tag(name = "航线飞行", description = """
航线管理是无人机自主作业的重要功能 航线管理是无人机自主作业的重要功能
可以实现行业领域的批量化智能化作业 可以实现行业领域的批量化智能化作业
上云 API 提供了相关的接口实现了航线任务在云端的共享查看下发执行取消以及进度上报等功能 上云 API 提供了相关的接口实现了航线任务在云端的共享查看下发执行取消以及进度上报等功能
用户需要遵照航线文件格式规范WPML编写航线文件定义航线任务一个航线任务中可以定义多条航线""") 用户需要遵照航线文件格式规范WPML编写航线文件定义航线任务一个航线任务中可以定义多条航线""")
@ApiOrder(5) @ApiOrder(5)
@RequiredArgsConstructor @RequiredArgsConstructor
public class RouteFlightController { public class RouteFlightController {
@ -77,7 +78,7 @@ public class RouteFlightController {
@Operation(summary = "取消任务") @Operation(summary = "取消任务")
@RequiresPermissions("bus:route:flight:flightTaskUndo") @RequiresPermissions("bus:route:flight:flightTaskUndo")
public Result<Object> flightTaskUndo(@PathVariable String dockSn, public Result<Object> flightTaskUndo(@PathVariable String dockSn,
@RequestBody List<String> taskIds) { @RequestBody List<String> taskIds) {
if (taskIds.isEmpty()) { if (taskIds.isEmpty()) {
return new Result<>().error(ErrorCode.PARAMS_ERROR); return new Result<>().error(ErrorCode.PARAMS_ERROR);
} }
@ -90,9 +91,9 @@ public class RouteFlightController {
@Operation(summary = "执行航线") @Operation(summary = "执行航线")
@RequiresPermissions("bus:route:flight:flightExecute") @RequiresPermissions("bus:route:flight:flightExecute")
public Result<Object> flightExecute(@PathVariable String dockSn, public Result<Object> flightExecute(@PathVariable String dockSn,
@RequestBody FlightExecute flightExecute) { @RequestBody FlightExecute flightExecute) {
return new Result<>().ok(routeFlightService.flightExecute(dockSn, flightExecute, true)); return new Result<>().ok(routeFlightService.flightExecute(dockSn, flightExecute, true, FlightTaskType.ROUTE.getCode()));
} }
@LogOperation("空中下发航线") @LogOperation("空中下发航线")
@ -100,8 +101,8 @@ public class RouteFlightController {
@Operation(summary = "空中下发航线") @Operation(summary = "空中下发航线")
@RequiresPermissions("bus:route:flight:inFlightWaylineDeliver") @RequiresPermissions("bus:route:flight:inFlightWaylineDeliver")
public Result<Object> inFlightWaylineDeliver(@PathVariable String dockSn, public Result<Object> inFlightWaylineDeliver(@PathVariable String dockSn,
@Parameter(name = "routeId", description = "航线标识") @Parameter(name = "routeId", description = "航线标识")
@RequestParam Long routeId) { @RequestParam Long routeId) {
return new Result<>().ok(routeFlightService.inFlightWaylineDeliver(dockSn, routeId)); return new Result<>().ok(routeFlightService.inFlightWaylineDeliver(dockSn, routeId));
} }

View File

@ -0,0 +1,78 @@
package com.multictrl.modules.business.controller;
import com.multictrl.common.annotation.ApiOrder;
import com.multictrl.common.annotation.LogOperation;
import com.multictrl.common.utils.Result;
import com.multictrl.common.validator.AssertUtils;
import com.multictrl.common.validator.ValidatorUtils;
import com.multictrl.common.validator.group.AddGroup;
import com.multictrl.common.validator.group.DefaultGroup;
import com.multictrl.common.validator.group.UpdateGroup;
import com.multictrl.modules.business.dto.WeatherNoflyDTO;
import com.multictrl.modules.business.service.WeatherNoflyService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
/**
* 天气阻飞
*
* @author Sdy
* @since 1.0.0 2026-06-15
*/
@RestController
@RequestMapping("business/weathernofly")
@Tag(name = "天气阻飞")
@ApiOrder(24)
@RequiredArgsConstructor
public class WeatherNoflyController {
private final WeatherNoflyService weatherNoflyService;
@GetMapping("{dockSn}")
@Operation(summary = "信息")
@RequiresPermissions("bus:weathernofly:info")
public Result<WeatherNoflyDTO> get(@PathVariable String dockSn) {
WeatherNoflyDTO data = weatherNoflyService.getInfoByDockSn(dockSn);
return new Result<WeatherNoflyDTO>().ok(data);
}
@PostMapping
@Operation(summary = "保存")
@LogOperation("保存")
@RequiresPermissions("bus:weathernofly:save")
public Result<Object> save(@RequestBody WeatherNoflyDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, AddGroup.class);
weatherNoflyService.addWeatherNofly(dto);
return new Result<>();
}
@PutMapping
@Operation(summary = "修改")
@LogOperation("修改")
@RequiresPermissions("bus:weathernofly:update")
public Result<Object> update(@RequestBody WeatherNoflyDTO dto) {
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class);
weatherNoflyService.update(dto);
return new Result<>();
}
@DeleteMapping
@Operation(summary = "删除")
@LogOperation("删除")
@RequiresPermissions("bus:weathernofly:delete")
public Result<Object> delete(@RequestBody Long[] ids) {
//效验数据
AssertUtils.isArrayEmpty(ids, "id");
weatherNoflyService.delete(ids);
return new Result<>();
}
}

View File

@ -0,0 +1,16 @@
package com.multictrl.modules.business.dao;
import com.multictrl.common.dao.BaseDao;
import com.multictrl.modules.business.entity.WeatherNoflyEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 天气阻飞
*
* @author Sdy
* @since 1.0.0 2026-06-15
*/
@Mapper
public interface WeatherNoflyDao extends BaseDao<WeatherNoflyEntity> {
}

View File

@ -0,0 +1,114 @@
package com.multictrl.modules.business.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.multictrl.common.validator.group.AddGroup;
import com.multictrl.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 天气阻飞
*
* @author Sdy
* @since 1.0.0 2026-06-15
*/
@Data
@Schema(name = "天气阻飞")
public class WeatherNoflyDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Null(message = "标识必须为空", groups = AddGroup.class)
@NotNull(message = "标识不能为空", groups = UpdateGroup.class)
@Schema(description = "标识")
private Long id;
@NotNull(message = "机库SN不能为空", groups = {AddGroup.class, UpdateGroup.class})
@Schema(description = "机库sn")
private String dockSn;
@NotNull(message = "是否开启阻飞不能为空", groups = {AddGroup.class, UpdateGroup.class})
@Schema(description = "是否开启阻飞(开启后使用机库上报数据)")
private Boolean isOpen;
@Schema(description = "风速m/s")
private Double windSpeed;
@Schema(description = "0关闭 1小雨 2中雨 3大雨")
private Integer rainfall;
@JsonIgnore
@Schema(hidden = true)
@AssertTrue(message = "参数为空或错误", groups = {AddGroup.class, UpdateGroup.class})
public boolean isIsOpenValid() {
if (isOpen) {
return windSpeed != null && rainfall != null && (rainfall == 0 || rainfall == 1);
}
return true;
}
@NotNull(message = "是否全部阻飞不能为空", groups = {AddGroup.class, UpdateGroup.class})
@Schema(description = "是否全部阻飞(开启后全部阻飞不开启只阻飞计划库)")
private Boolean isAllPrevent;
@NotNull(message = "是否开启天气阻飞不能为空", groups = {AddGroup.class, UpdateGroup.class})
@Schema(description = "是否开启天气阻飞(开启后在机库数据上增加天气预报数据)")
private Boolean isOpenWeather;
@Schema(description = "风速m/s天气预报")
private Double windSpeedWeather;
@Schema(description = "雨(天气预报 0关闭 1小雨 2中雨 3大雨")
private Integer rainfallWeather;
@Schema(description = "雪(天气预报 0关闭 1小雪 2中雪 3大雪")
private Integer snowWeather;
@Schema(description = "雾(天气预报 0关闭 1轻雾 2中雾 3大雾")
private Integer fogWeather;
@Schema(description = "霾(天气预报 0关闭 1轻度霾 2中度霾 3重度霾")
private Integer hazeWeather;
@Schema(description = "冰雹阻飞")
private Boolean isHailWeather;
@Schema(description = "沙尘暴阻飞")
private Boolean isSandstormWeather;
@Schema(description = "龙卷风阻飞")
private Boolean isTornadoWeather;
@Schema(description = "温度上限(摄氏度)")
private Double temperatureMax;
@Schema(description = "温度下限(摄氏度)")
private Double temperatureMin;
@JsonIgnore
@Schema(hidden = true)
@AssertTrue(message = "天气参数为空或错误", groups = {AddGroup.class, UpdateGroup.class})
public boolean isIsOpenWeatherValid() {
if (isOpenWeather) {
return windSpeedWeather != null
&& (rainfallWeather != null && rainfallWeather >= 0 && rainfallWeather <= 3)
&& (snowWeather != null && snowWeather >= 0 && snowWeather <= 3)
&& (fogWeather != null && fogWeather >= 0 && fogWeather <= 3)
&& (hazeWeather != null && hazeWeather >= 0 && hazeWeather <= 3)
&& isHailWeather != null
&& isSandstormWeather != null
&& isTornadoWeather != null
&& temperatureMax != null
&& temperatureMin != null;
}
return true;
}
}

View File

@ -0,0 +1,31 @@
package com.multictrl.modules.business.dto;
import lombok.Data;
/**
* 天气阻飞任务记录参数
*
* @author Sdy
* @since 1.0.0 2026/6/17
*/
@Data
public class WeatherNoflyTaskDTO {
//任务标识
private String taskId;
//机库SN
private String dockSn;
//航线标识
private Long routeId;
//定时任务标识
private Long jobId;
//任务类型 1航线 2手动 3定时
private Integer taskType;
//失败原因
private String reason;
}

View File

@ -0,0 +1,96 @@
package com.multictrl.modules.business.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.multictrl.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 天气阻飞
*
* @author Sdy
* @since 1.0.0 2026-06-15
*/
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("bus_weather_nofly")
public class WeatherNoflyEntity extends BaseEntity {
/**
* 机库sn
*/
private String dockSn;
/**
* 是否开启阻飞开启后使用机库上报数据
*/
private Boolean isOpen;
/**
* 风速m/s
*/
private Double windSpeed;
/**
* 0关闭 1小雨 2中雨 3大雨
*/
private Integer rainfall;
/**
* 是否全部阻飞开启后全部阻飞不开启只阻飞计划库
*/
private Boolean isAllPrevent;
/**
* 是否开启天气阻飞开启后在机库数据上增加天气预报数据
*/
private Boolean isOpenWeather;
/**
* 风速m/s天气预报
*/
private Double windSpeedWeather;
/**
* 天气预报 0关闭 1小雨 2中雨 3大雨
*/
private Integer rainfallWeather;
/**
* 天气预报 0关闭 1小雪 2中雪 3大雪
*/
private Integer snowWeather;
/**
* 天气预报 0关闭 1轻雾 2中雾 3大雾
*/
private Integer fogWeather;
/**
* 天气预报 0关闭 1轻度霾 2中度霾 3重度霾
*/
private Integer hazeWeather;
/**
* 冰雹阻飞
*/
private Boolean isHailWeather;
/**
* 沙尘暴阻飞
*/
private Boolean isSandstormWeather;
/**
* 龙卷风阻飞
*/
private Boolean isTornadoWeather;
/**
* 温度上限摄氏度
*/
private Double temperatureMax;
/**
* 温度下限摄氏度
*/
private Double temperatureMin;
/**
* 修改人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 修改时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}

View File

@ -47,6 +47,9 @@ public interface DJIBaseService {
//获取机场备降点 //获取机场备降点
LatLonDTO getDockAlternateLandPoint(String dockSn); LatLonDTO getDockAlternateLandPoint(String dockSn);
//获取机库位置
LatLonDTO getDockLocation(String dockSn);
//机场是否在线 //机场是否在线
Boolean isDockOnline(String dockSn); Boolean isDockOnline(String dockSn);

View File

@ -5,6 +5,7 @@ import com.multictrl.common.page.PageData;
import com.multictrl.common.service.CrudService; import com.multictrl.common.service.CrudService;
import com.multictrl.modules.business.dto.FlightTaskDTO; import com.multictrl.modules.business.dto.FlightTaskDTO;
import com.multictrl.modules.business.dto.MediaFileDTO; import com.multictrl.modules.business.dto.MediaFileDTO;
import com.multictrl.modules.business.dto.WeatherNoflyTaskDTO;
import com.multictrl.modules.business.entity.FlightTaskEntity; import com.multictrl.modules.business.entity.FlightTaskEntity;
import com.multictrl.modules.business.entity.SrsRecordEntity; import com.multictrl.modules.business.entity.SrsRecordEntity;
import com.multictrl.modules.business.influxdb.UavReport; import com.multictrl.modules.business.influxdb.UavReport;
@ -30,6 +31,8 @@ public interface FlightTaskService extends CrudService<FlightTaskEntity, FlightT
void addJobRouteTask(String taskId, Long jobId);//计划飞行任务 void addJobRouteTask(String taskId, Long jobId);//计划飞行任务
void addWeatherNoflyTask(WeatherNoflyTaskDTO dto);//天气阻飞任务
//更新架次完成信息 //更新架次完成信息
void updateTaskComplete(String dockSn); void updateTaskComplete(String dockSn);

View File

@ -3,6 +3,7 @@ package com.multictrl.modules.business.service;
import com.multictrl.modules.business.dto.flight.FlightExecute; import com.multictrl.modules.business.dto.flight.FlightExecute;
import com.multictrl.modules.business.dto.multi.PrivateMultiFlightBindDockInfo; import com.multictrl.modules.business.dto.multi.PrivateMultiFlightBindDockInfo;
import java.util.List; import java.util.List;
/** /**
@ -17,7 +18,7 @@ public interface RouteFlightService {
String flightTaskUndo(String dockSn, List<String> taskIds); String flightTaskUndo(String dockSn, List<String> taskIds);
//执行航线 //执行航线
String flightExecute(String dockSn, FlightExecute flightExecute, Boolean isRecord); String flightExecute(String dockSn, FlightExecute flightExecute, Boolean isRecord, Integer taskType);
//空中下发航线 //空中下发航线
String inFlightWaylineDeliver(String dockSn, Long routeId); String inFlightWaylineDeliver(String dockSn, Long routeId);

View File

@ -0,0 +1,24 @@
package com.multictrl.modules.business.service;
import cn.hutool.json.JSONObject;
import com.multictrl.common.service.CrudService;
import com.multictrl.modules.business.dto.WeatherNoflyDTO;
import com.multictrl.modules.business.entity.WeatherNoflyEntity;
/**
* 天气阻飞
*
* @author Sdy
* @since 1.0.0 2026-06-15
*/
public interface WeatherNoflyService extends CrudService<WeatherNoflyEntity, WeatherNoflyDTO> {
//信息
WeatherNoflyDTO getInfoByDockSn(String dockSn);
//新增阻飞
void addWeatherNofly(WeatherNoflyDTO dto);
//判断机库是否阻飞
JSONObject allowFlight(String dockSn, boolean isJobTask);
}

View File

@ -0,0 +1,13 @@
package com.multictrl.modules.business.service;
/**
* 天气服务
*
* @author Sdy
* @since 1.0.0 2026/6/16
*/
public interface WeatherService {
//根据经纬度获取实时天气
String getWeatherByLonLat(Double longitude, Double latitude);
}

View File

@ -4,25 +4,21 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.multictrl.common.config.MqttConfig; import com.multictrl.common.config.MqttConfig;
import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.constant.DockMode; import com.multictrl.common.constant.DockMode;
import com.multictrl.common.constant.FlightTaskProgressStatus; import com.multictrl.common.constant.FlightTaskProgressStatus;
import com.multictrl.common.constant.FlightTaskType;
import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException; import com.multictrl.common.exception.RenException;
import com.multictrl.common.utils.CacheUtils; import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.Utils; import com.multictrl.common.utils.Utils;
import com.multictrl.modules.business.dao.DockDeviceDao;
import com.multictrl.modules.business.dto.LatLonDTO; import com.multictrl.modules.business.dto.LatLonDTO;
import com.multictrl.modules.business.dto.WeatherNoflyTaskDTO;
import com.multictrl.modules.business.dto.command.DrcStick; import com.multictrl.modules.business.dto.command.DrcStick;
import com.multictrl.modules.business.dto.command.FlyToPoint; import com.multictrl.modules.business.dto.command.FlyToPoint;
import com.multictrl.modules.business.dto.command.TakeoffToPoint; import com.multictrl.modules.business.dto.command.TakeoffToPoint;
import com.multictrl.modules.business.entity.DockDeviceEntity; import com.multictrl.modules.business.service.*;
import com.multictrl.modules.business.service.CommandService;
import com.multictrl.modules.business.service.DJIBaseService;
import com.multictrl.modules.business.service.FlightTaskService;
import com.multictrl.modules.business.service.MqttPushService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -45,9 +41,9 @@ import java.util.concurrent.locks.ReentrantLock;
public class CommandServiceImpl implements CommandService { public class CommandServiceImpl implements CommandService {
private final DJIBaseService djiBaseService; private final DJIBaseService djiBaseService;
private final MqttConfig mqttConfig; private final MqttConfig mqttConfig;
private final DockDeviceDao dockDeviceDao;
private final MqttPushService mqttPushService; private final MqttPushService mqttPushService;
private final FlightTaskService flightTaskService; private final FlightTaskService flightTaskService;
private final WeatherNoflyService weatherNoflyService;
private final Map<String, ReentrantLock> cacheLock = new ConcurrentHashMap<>(); private final Map<String, ReentrantLock> cacheLock = new ConcurrentHashMap<>();
@Override @Override
@ -104,6 +100,19 @@ public class CommandServiceImpl implements CommandService {
//设置默认相机模式需要定频推送前端 //设置默认相机模式需要定频推送前端
CacheUtils.set(BusinessConstant.UAV_VIDEO_TYPE, "wide"); CacheUtils.set(BusinessConstant.UAV_VIDEO_TYPE, "wide");
//判断天气阻飞
JSONObject allowFlightResult = weatherNoflyService.allowFlight(dockSn, false);
Boolean isAllow = allowFlightResult.getBool("isAllow");
if (!isAllow) {
String reason = allowFlightResult.getStr("reason");
WeatherNoflyTaskDTO dto = new WeatherNoflyTaskDTO();
dto.setTaskId(taskId);
dto.setDockSn(dockSn);
dto.setTaskType(FlightTaskType.MANUAL.getCode());
dto.setReason(reason);
flightTaskService.addWeatherNoflyTask(dto);
return reason;
}
//发送执行 //发送执行
String result = djiBaseService.executeAndReturnResult(dockSn, "takeoff_to_point", Utils.beanToSnakeJson(takeoffToPoint)); String result = djiBaseService.executeAndReturnResult(dockSn, "takeoff_to_point", Utils.beanToSnakeJson(takeoffToPoint));
flightTaskService.addTakeoffTask(taskId, dockSn); flightTaskService.addTakeoffTask(taskId, dockSn);

View File

@ -29,7 +29,9 @@ import java.util.concurrent.ConcurrentHashMap;
public class DJIBaseServiceImpl implements DJIBaseService { public class DJIBaseServiceImpl implements DJIBaseService {
private final MqttPushService mqttPushService; private final MqttPushService mqttPushService;
/** 设备SN → 当前正在等待回复的命令信息(供模拟回复使用) */ /**
* 设备SN 当前正在等待回复的命令信息供模拟回复使用
*/
private final ConcurrentHashMap<String, JSONObject> pendingCmds = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, JSONObject> pendingCmds = new ConcurrentHashMap<>();
@Override @Override
@ -237,6 +239,20 @@ public class DJIBaseServiceImpl implements DJIBaseService {
return null; return null;
} }
@Override
public LatLonDTO getDockLocation(String dockSn) {
Object o = CacheUtils.get(BusinessConstant.DOCK_OSD + dockSn);
if (o != null) {
JSONObject data = (JSONObject) o;
LatLonDTO gps = new LatLonDTO();
gps.setLatitude(data.getDouble("latitude"));
gps.setLongitude(data.getDouble("longitude"));
gps.setHeight(data.getDouble("height"));
return gps;
}
return null;
}
@Override @Override
public Boolean isDockOnline(String dockSn) { public Boolean isDockOnline(String dockSn) {
Object object = CacheUtils.get(BusinessConstant.DOCK_OSD + dockSn); Object object = CacheUtils.get(BusinessConstant.DOCK_OSD + dockSn);

View File

@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.constant.FlightTaskStatus; import com.multictrl.common.constant.FlightTaskStatus;
import com.multictrl.common.constant.FlightTaskType;
import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException; import com.multictrl.common.exception.RenException;
import com.multictrl.common.page.PageData; import com.multictrl.common.page.PageData;
@ -19,6 +20,7 @@ import com.multictrl.modules.business.dao.FlightTaskDao;
import com.multictrl.modules.business.dto.FlightTaskDTO; import com.multictrl.modules.business.dto.FlightTaskDTO;
import com.multictrl.modules.business.dto.MediaFileDTO; import com.multictrl.modules.business.dto.MediaFileDTO;
import com.multictrl.modules.business.dto.RouteDTO; import com.multictrl.modules.business.dto.RouteDTO;
import com.multictrl.modules.business.dto.WeatherNoflyTaskDTO;
import com.multictrl.modules.business.entity.*; import com.multictrl.modules.business.entity.*;
import com.multictrl.modules.business.influxdb.UavReport; import com.multictrl.modules.business.influxdb.UavReport;
import com.multictrl.modules.business.service.*; import com.multictrl.modules.business.service.*;
@ -141,6 +143,48 @@ public class FlightTaskServiceImpl extends CrudServiceImpl<FlightTaskDao, Flight
baseDao.insert(entity); baseDao.insert(entity);
} }
@Override
public void addWeatherNoflyTask(WeatherNoflyTaskDTO dto) {
FlightTaskEntity entity = new FlightTaskEntity();
entity.setTaskId(dto.getTaskId());
entity.setTaskType(dto.getTaskType());
Long routeId = null;
//1航线 2手动 3定时
if (Objects.equals(dto.getTaskType(), FlightTaskType.ROUTE.getCode())) {
addDockInfo(entity, dto.getDockSn());
routeId = dto.getRouteId();
entity.setRouteId(routeId);
entity.setDeptId(SecurityUser.getDeptId());
entity.setCreateName(SecurityUser.getUser().getRealName());
} else if (Objects.equals(dto.getTaskType(), FlightTaskType.MANUAL.getCode())) {
entity.setTaskId(dto.getTaskId());
addDockInfo(entity, dto.getDockSn());
entity.setRouteName("一键起飞");
entity.setDeptId(SecurityUser.getDeptId());
entity.setCreateName(SecurityUser.getUser().getRealName());
} else if (Objects.equals(dto.getTaskType(), FlightTaskType.SCHEDULED.getCode())) {
ScheduleJobEntity jobEntity = scheduleJobDao.getById(dto.getJobId());
addDockInfo(entity, jobEntity.getDockSn());
routeId = Long.parseLong(jobEntity.getParams());
entity.setRouteId(routeId);
entity.setDeptId(jobEntity.getDeptId());
SysUserDTO userDTO = userService.get(jobEntity.getUpdater());
entity.setCreateName(userDTO.getRealName());
}
if (routeId != null) {
RouteDTO route = routeService.getRoute(routeId);
if (route != null) {
entity.setRouteName(route.getRouteName());
entity.setRouteType(route.getTemplateType());
entity.setRouteDistance(route.getTotalDistance());
entity.setWaypointNum(route.getRouteWaypointList().size());
}
}
entity.setTaskStatus(FlightTaskStatus.PREVENTED.getCode());
entity.setFailureReason(dto.getReason());
baseDao.insert(entity);
}
@Override @Override
public void updateTaskComplete(String dockSn) { public void updateTaskComplete(String dockSn) {
String taskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn); String taskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn);

View File

@ -5,10 +5,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.constant.*;
import com.multictrl.common.constant.Constant;
import com.multictrl.common.constant.DJIImage;
import com.multictrl.common.constant.DockMode;
import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException; import com.multictrl.common.exception.RenException;
import com.multictrl.common.page.PageData; import com.multictrl.common.page.PageData;
@ -246,7 +243,7 @@ public class MultiServiceImpl implements MultiService {
flightExecute.setPrivateMultiFlightBindDockInfos(privateMultiFlightBindDockInfos); flightExecute.setPrivateMultiFlightBindDockInfos(privateMultiFlightBindDockInfos);
log.info("privateMultiDockFlightTaskOutbound-> 下发航线给妙算 flightExecute:{}", flightExecute); log.info("privateMultiDockFlightTaskOutbound-> 下发航线给妙算 flightExecute:{}", flightExecute);
return routeFlightService.flightExecute(multiTaskDTO.getMiaoSuan().getDockSn(), flightExecute, true); return routeFlightService.flightExecute(multiTaskDTO.getMiaoSuan().getDockSn(), flightExecute, true, FlightTaskType.ROUTE.getCode());
} }
@Override @Override

View File

@ -6,20 +6,20 @@ import cn.hutool.json.JSONObject;
import com.multictrl.common.config.MinioConfig; import com.multictrl.common.config.MinioConfig;
import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.constant.DockMode; import com.multictrl.common.constant.DockMode;
import com.multictrl.common.constant.FlightTaskType;
import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException; import com.multictrl.common.exception.RenException;
import com.multictrl.common.utils.CacheUtils; import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.JsonUtils; import com.multictrl.common.utils.JsonUtils;
import com.multictrl.common.utils.Utils; import com.multictrl.common.utils.Utils;
import com.multictrl.modules.business.dto.RouteDTO; import com.multictrl.modules.business.dto.RouteDTO;
import com.multictrl.modules.business.dto.WeatherNoflyDTO;
import com.multictrl.modules.business.dto.WeatherNoflyTaskDTO;
import com.multictrl.modules.business.dto.flight.FlightExecute; import com.multictrl.modules.business.dto.flight.FlightExecute;
import com.multictrl.modules.business.dto.flight.FlightTaskPrepare; import com.multictrl.modules.business.dto.flight.FlightTaskPrepare;
import com.multictrl.modules.business.dto.flight.InFlightWaylineDeliver; import com.multictrl.modules.business.dto.flight.InFlightWaylineDeliver;
import com.multictrl.modules.business.dto.multi.PrivateMultiFlightBindDockInfo; import com.multictrl.modules.business.dto.multi.PrivateMultiFlightBindDockInfo;
import com.multictrl.modules.business.service.DJIBaseService; import com.multictrl.modules.business.service.*;
import com.multictrl.modules.business.service.FlightTaskService;
import com.multictrl.modules.business.service.RouteFlightService;
import com.multictrl.modules.business.service.RouteService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -46,6 +46,7 @@ public class RouteFlightServiceImpl implements RouteFlightService {
private final RouteService routeService; private final RouteService routeService;
private final MinioConfig minioConfig; private final MinioConfig minioConfig;
private final FlightTaskService flightTaskService; private final FlightTaskService flightTaskService;
private final WeatherNoflyService weatherNoflyService;
private final Map<String, ReentrantLock> cacheLock = new ConcurrentHashMap<>(); private final Map<String, ReentrantLock> cacheLock = new ConcurrentHashMap<>();
@Override @Override
@ -59,7 +60,7 @@ public class RouteFlightServiceImpl implements RouteFlightService {
* multiFlightBindDockInfos aros机场蛙跳模式参数把N个参与到蛙跳的的机场信息给到妙算返航时自动计算离哪个机库位置近就降落到哪个机库 * multiFlightBindDockInfos aros机场蛙跳模式参数把N个参与到蛙跳的的机场信息给到妙算返航时自动计算离哪个机库位置近就降落到哪个机库
*/ */
@Override @Override
public String flightExecute(String dockSn, FlightExecute flightExecute, Boolean isRecord) { public String flightExecute(String dockSn, FlightExecute flightExecute, Boolean isRecord, Integer taskType) {
ReentrantLock lock = cacheLock.computeIfAbsent(dockSn, k -> new ReentrantLock()); ReentrantLock lock = cacheLock.computeIfAbsent(dockSn, k -> new ReentrantLock());
lock.lock(); lock.lock();
try { try {
@ -99,6 +100,20 @@ public class RouteFlightServiceImpl implements RouteFlightService {
if (globalRthHeight <= 50) { if (globalRthHeight <= 50) {
throw new RenException(ErrorCode.GLOBAL_RTH_HEIGHT_NOT_BELOW, "50"); throw new RenException(ErrorCode.GLOBAL_RTH_HEIGHT_NOT_BELOW, "50");
} }
//判断天气阻飞
JSONObject allowFlightResult = weatherNoflyService.allowFlight(dockSn, Objects.equals(taskType, FlightTaskType.SCHEDULED.getCode()));
Boolean isAllow = allowFlightResult.getBool("isAllow");
if (!isAllow) {
String reason = allowFlightResult.getStr("reason");
WeatherNoflyTaskDTO dto = new WeatherNoflyTaskDTO();
dto.setTaskId(taskId);
dto.setDockSn(dockSn);
dto.setRouteId(route.getId());
dto.setTaskType(taskType);
dto.setReason(reason);
flightTaskService.addWeatherNoflyTask(dto);
return reason;
}
//下发任务 //下发任务
int exitOnRcLost = "executeLostAction".equals(route.getExitOnRcLost()) ? 1 : 0; int exitOnRcLost = "executeLostAction".equals(route.getExitOnRcLost()) ? 1 : 0;
String kmzUrl = minioConfig.getEndpoint() + "/" + route.getKmzUrl(); String kmzUrl = minioConfig.getEndpoint() + "/" + route.getKmzUrl();
@ -120,7 +135,7 @@ public class RouteFlightServiceImpl implements RouteFlightService {
} }
if (flightExecute.getPrivateMultiFlightBindDockInfos() != null) { if (flightExecute.getPrivateMultiFlightBindDockInfos() != null) {
flightTaskPrepare.setPrivate_multi_flight_bind_dock_info( flightTaskPrepare.setPrivate_multi_flight_bind_dock_info(
flightExecute.getPrivateMultiFlightBindDockInfos()); flightExecute.getPrivateMultiFlightBindDockInfos());
} }
JSONObject data = JsonUtils.parseObject(JsonUtils.toJsonString(flightTaskPrepare), JSONObject.class); JSONObject data = JsonUtils.parseObject(JsonUtils.toJsonString(flightTaskPrepare), JSONObject.class);
String result = djiBaseService.executeAndReturnResult(dockSn, "flighttask_prepare", data); String result = djiBaseService.executeAndReturnResult(dockSn, "flighttask_prepare", data);
@ -128,7 +143,7 @@ public class RouteFlightServiceImpl implements RouteFlightService {
//执行任务 //执行任务
Utils.sleep(1000); Utils.sleep(1000);
String resultMsg = djiBaseService.executeAndReturnResult(dockSn, "flighttask_execute", String resultMsg = djiBaseService.executeAndReturnResult(dockSn, "flighttask_execute",
new JSONObject().set("flight_id", taskId)); new JSONObject().set("flight_id", taskId));
if (isRecord) { if (isRecord) {
flightTaskService.addRouteTask(taskId, dockSn, route.getId()); flightTaskService.addRouteTask(taskId, dockSn, route.getId());
} }
@ -192,7 +207,7 @@ public class RouteFlightServiceImpl implements RouteFlightService {
throw new RenException(ErrorCode.ROUTE_TASK_NOT_EXIST); throw new RenException(ErrorCode.ROUTE_TASK_NOT_EXIST);
} }
return djiBaseService.executeAndReturnResult(dockSn, "in_flight_wayline_stop", return djiBaseService.executeAndReturnResult(dockSn, "in_flight_wayline_stop",
new JSONObject().set("in_flight_wayline_id", flightId)); new JSONObject().set("in_flight_wayline_id", flightId));
} }
@Override @Override
@ -202,6 +217,6 @@ public class RouteFlightServiceImpl implements RouteFlightService {
throw new RenException(ErrorCode.ROUTE_TASK_NOT_EXIST); throw new RenException(ErrorCode.ROUTE_TASK_NOT_EXIST);
} }
return djiBaseService.executeAndReturnResult(dockSn, "in_flight_wayline_recover", return djiBaseService.executeAndReturnResult(dockSn, "in_flight_wayline_recover",
new JSONObject().set("in_flight_wayline_id", flightId)); new JSONObject().set("in_flight_wayline_id", flightId));
} }
} }

View File

@ -0,0 +1,275 @@
package com.multictrl.modules.business.service.impl;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.service.impl.CrudServiceImpl;
import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.ConvertUtils;
import com.multictrl.common.utils.JsonUtils;
import com.multictrl.common.utils.weather.*;
import com.multictrl.modules.business.dao.WeatherNoflyDao;
import com.multictrl.modules.business.dto.LatLonDTO;
import com.multictrl.modules.business.dto.WeatherNoflyDTO;
import com.multictrl.modules.business.entity.WeatherNoflyEntity;
import com.multictrl.modules.business.service.DJIBaseService;
import com.multictrl.modules.business.service.WeatherNoflyService;
import cn.hutool.core.util.StrUtil;
import com.multictrl.modules.business.service.WeatherService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* 天气阻飞
*
* @author Sdy
* @since 1.0.0 2026-06-15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class WeatherNoflyServiceImpl extends CrudServiceImpl<WeatherNoflyDao, WeatherNoflyEntity, WeatherNoflyDTO> implements WeatherNoflyService {
private final DJIBaseService djiBaseService;
private final WeatherService weatherService;
@Override
public QueryWrapper<WeatherNoflyEntity> getWrapper(Map<String, Object> params) {
String id = (String) params.get("id");
QueryWrapper<WeatherNoflyEntity> wrapper = new QueryWrapper<>();
wrapper.eq(StrUtil.isNotBlank(id), "id", id);
return wrapper;
}
@Override
public WeatherNoflyDTO getInfoByDockSn(String dockSn) {
WeatherNoflyEntity entity = baseDao.selectOne(new QueryWrapper<WeatherNoflyEntity>().eq("dock_sn", dockSn));
return ConvertUtils.sourceToTarget(entity, WeatherNoflyDTO.class);
}
@Override
public void addWeatherNofly(WeatherNoflyDTO dto) {
WeatherNoflyEntity entity = baseDao.selectOne(new QueryWrapper<WeatherNoflyEntity>().eq("dock_sn", dto.getDockSn()));
if (entity != null) {
deleteById(entity.getId());
}
save(dto);
}
@Override
public JSONObject allowFlight(String dockSn, boolean isJobTask) {
JSONObject jsonObject = new JSONObject();
jsonObject.set("isAllow", true);
//查询机库是否有阻飞信息
WeatherNoflyEntity entity = baseDao.selectOne(new QueryWrapper<WeatherNoflyEntity>()
.eq("dock_sn", dockSn).eq("is_open", true));
if (entity != null) {//存在阻飞设置
Boolean isAllPrevent = entity.getIsAllPrevent();
if (isAllPrevent || isJobTask) {
Boolean isOpenWeather = entity.getIsOpenWeather();//判断天气阻飞
if (isOpenWeather) {
LatLonDTO dockLocation = djiBaseService.getDockLocation(dockSn);
String weatherString = weatherService.getWeatherByLonLat(dockLocation.getLongitude(), dockLocation.getLatitude());
JSONObject weatherObject = JsonUtils.parseObject(weatherString, JSONObject.class);
if (weatherObject != null) {
//风速判断
String windpower = weatherObject.getStr("windpower");//风力级别
double maxWindSpeed = WindSpeedConverter.getMaxWindSpeed(windpower);
if (maxWindSpeed >= entity.getWindSpeedWeather()) {
log.info("风力过大,级别:{},最大风速:{},阀值:{}", windpower, maxWindSpeed, entity.getWindSpeedWeather());
jsonObject.set("isAllow", false);
jsonObject.set("reason", "风力过大,阀值:{" + entity.getWindSpeedWeather() + "},起飞时风速:{" + maxWindSpeed + "}");
}
//温度判断
double temperature = Double.parseDouble(weatherObject.getStr("temperature"));//温度
if (temperature < entity.getTemperatureMin()) {
log.info("气温过低,当前温度:{},设置温度下限:{}", temperature, entity.getTemperatureMin());
jsonObject.set("isAllow", false);
jsonObject.set("reason", "气温过低,阀值:{" + entity.getTemperatureMin() + "},起飞时温度:{" + temperature + "}");
}
if (temperature > entity.getTemperatureMax()) {
log.info("气温过高,当前温度:{},设置温度上限:{}", temperature, entity.getTemperatureMax());
jsonObject.set("isAllow", false);
jsonObject.set("reason", "气温过高,阀值:{" + entity.getTemperatureMax() + "},起飞时温度:{" + temperature + "}");
}
String weather = weatherObject.getStr("weather");//天气现象
//雨量判断
Integer rainfallWeather = entity.getRainfallWeather();
if (rainfallWeather > 0) {
if (rainfallWeather == 1) {
if (RainfallUtils.isMinRain(weather) || RainfallUtils.isModerateRain(weather) || RainfallUtils.isMaxRain(weather)) {
log.info("已达雨量上限,设置:小雨,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雨量{小雨}阻飞,起飞时天气:{" + weather + "}");
}
} else if (rainfallWeather == 2) {
if (RainfallUtils.isModerateRain(weather) || RainfallUtils.isMaxRain(weather)) {
log.info("已达雨量上限,设置:中雨,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雨量{中雨}阻飞,起飞时天气:{" + weather + "}");
}
} else if (rainfallWeather == 3) {
if (RainfallUtils.isMaxRain(weather)) {
log.info("已达雨量上限,设置:大雨,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雨量{大雨}阻飞,起飞时天气:{" + weather + "}");
}
}
}
//雪量判断
Integer snowWeather = entity.getSnowWeather();
if (snowWeather > 0) {
if (snowWeather == 1) {
if (SnowUtils.isMinSnow(weather) || SnowUtils.isModerateSnow(weather) || SnowUtils.isMaxSnow(weather)) {
log.info("已达雪量上限,设置:小雪,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雪量{小雪}阻飞,起飞时天气:{" + weather + "}");
}
} else if (snowWeather == 2) {
if (SnowUtils.isModerateSnow(weather) || SnowUtils.isMaxSnow(weather)) {
log.info("已达雪量上限,设置:中雪,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雪量{中雪}阻飞,起飞时天气:{" + weather + "}");
}
} else if (snowWeather == 3) {
if (SnowUtils.isMaxSnow(weather)) {
log.info("已达雪量上限,设置:大雪,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雪量{大雪}阻飞,起飞时天气:{" + weather + "}");
}
}
}
//雾量判断
Integer fogWeather = entity.getFogWeather();
if (fogWeather > 0) {
if (fogWeather == 1) {
if (FogUtils.isMinFog(weather) || FogUtils.isModerateFog(weather) || FogUtils.isMaxFog(weather)) {
log.info("已达雾量上限,设置:轻雾,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雾量{轻雾}阻飞,起飞时天气:{" + weather + "}");
}
} else if (fogWeather == 2) {
if (FogUtils.isModerateFog(weather) || FogUtils.isMaxFog(weather)) {
log.info("已达雾量上限,设置:中雾,当前:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雾量{中雾}阻飞,起飞时天气:{" + weather + "}");
}
} else if (fogWeather == 3) {
if (FogUtils.isMaxFog(weather)) {
log.info("已达雾量上限,设置:大雾,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发雾量{大雾}阻飞,起飞时天气:{" + weather + "}");
}
}
}
//霾量判断
Integer hazeWeather = entity.getHazeWeather();
if (hazeWeather > 0) {
if (hazeWeather == 1) {
if (HazeUtils.isMinHaze(weather) || HazeUtils.isModerateHaze(weather) || HazeUtils.isMaxHaze(weather)) {
log.info("已达霾量上限,设置:轻度霾,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发霾量{轻度霾}阻飞,起飞时天气:{" + weather + "}");
}
} else if (hazeWeather == 2) {
if (HazeUtils.isModerateHaze(weather) || HazeUtils.isMaxHaze(weather)) {
log.info("已达霾量上限,设置:中度霾,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发霾量{中度霾}阻飞,起飞时天气:{" + weather + "}");
}
} else if (hazeWeather == 3) {
if (HazeUtils.isMaxHaze(weather)) {
log.info("已达霾量上限,设置:重度霾,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发霾量{重度霾}阻飞,起飞时天气:{" + weather + "}");
}
}
}
//冰雹判断
Boolean isHailWeather = entity.getIsHailWeather();
if (isHailWeather) {
if ("雷阵雨并伴有冰雹".equals(weather)) {
log.info("已达冰雹阻飞,设置:开启冰雹阻飞,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发冰雹阻飞,起飞时天气:{" + weather + "}");
}
}
//沙尘暴判断
Boolean isSandstormWeather = entity.getIsSandstormWeather();
if (isSandstormWeather) {
if ("沙尘暴".equals(weather) || "强沙尘暴".equals(weather)) {
log.info("已达沙尘暴阻飞,设置:开启沙尘暴阻飞,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发沙尘暴阻飞,起飞时天气:{" + weather + "}");
}
}
//龙卷风判断
Boolean isTornadoWeather = entity.getIsTornadoWeather();
if (isTornadoWeather) {
if ("龙卷风".equals(weather)) {
log.info("已达龙卷风阻飞,设置:开启龙卷风阻飞,实际:{}", weather);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发龙卷风阻飞,起飞时天气:{" + weather + "}");
}
}
}
}
//云端阻飞
Integer rainfall = entity.getRainfall();
Double windSpeed = entity.getWindSpeed();
JSONObject data = (JSONObject) CacheUtils.get(BusinessConstant.DOCK_OSD + dockSn);
if (data != null) {
if (rainfall == 1 || rainfall == 2 || rainfall == 3) {
//0无雨 1小雨 2中雨 3大雨
int value = data.getInt("rainfall");
if (rainfall == 1) {
if (value >= 1) {
String message = value == 1 ? "小雨" : value == 2 ? "中雨" : "大雨";
log.info("已达下雨阻飞,设置:小雨阻飞,实际:{}", message);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发下雨阻飞,起飞时天气:{" + message + "}");
}
}
if (rainfall == 2) {
if (value >= 2) {
String message = value == 2 ? "中雨" : "大雨";
log.info("已达下雨阻飞,设置:中雨阻飞,实际:{}", message);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发下雨阻飞,起飞时天气:{" + message + "}");
}
}
if (rainfall == 3) {
if (value >= 3) {
log.info("已达下雨阻飞,设置:大雨阻飞,实际:{大雨}");
jsonObject.set("isAllow", false);
jsonObject.set("reason", "触发下雨阻飞,起飞时天气:{大雨}");
}
}
}
//风速
Float value = data.getFloat("wind_speed");
if (value >= windSpeed) {
log.info("风力过大,设置:风速阻飞:{},实际风速:{}", windSpeed, value);
jsonObject.set("isAllow", false);
jsonObject.set("reason", "风力过大,阀值:{" + windSpeed + "},起飞时风速:{" + value + "}");
}
}
}
}
return jsonObject;
}
}

View File

@ -0,0 +1,132 @@
package com.multictrl.modules.business.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.multictrl.common.constant.SysParamsKey;
import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException;
import com.multictrl.common.utils.JsonUtils;
import com.multictrl.modules.business.service.WeatherService;
import com.multictrl.modules.sys.dao.SysParamsDao;
import com.multictrl.modules.sys.entity.SysParamsEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 天气服务
*
* @author Sdy
* @since 1.0.0 2026/6/16
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class WeatherServiceImpl implements WeatherService {
private final SysParamsDao paramsDao;
private final static String GAODE_GEOCODE_REGEO = "https://restapi.amap.com/v3/geocode/regeo?key=%s&location=%s&radius=1&extensions=all";
public static final String GAODE_WEATHER_API = "https://restapi.amap.com/v3/weather/weatherInfo?key=%s&city=%s&extensions=base";
@Override
public String getWeatherByLonLat(Double longitude, Double latitude) {
String location = longitude + "," + latitude;
try {
String adCode = getAdCode(location);
if (StrUtil.isEmpty(adCode)) {
log.debug("获取 adcode 失败,经纬度:{}", location);
throw new RenException(ErrorCode.GAODE_REQUEST_ERROR);
}
String weatherByAdCode = getWeatherByAdCode(adCode);
if (StrUtil.isEmpty(weatherByAdCode)) {
log.debug("获取 weatherByAdCode 失败,经纬度:{}, adCode{}", location, adCode);
throw new RenException(ErrorCode.GAODE_REQUEST_ERROR);
}
return weatherByAdCode;
} catch (Exception e) {
log.error("获取天气信息异常,经纬度:{}", location, e);
throw new RenException(ErrorCode.GAODE_REQUEST_ERROR);
}
}
/**
* 获取高德Key
*/
private String getGaoDeKey() {
SysParamsEntity entity = paramsDao.selectOne(new QueryWrapper<SysParamsEntity>()
.eq("param_code", SysParamsKey.GAODE_API_KEY));
if (entity == null) {
throw new RenException(ErrorCode.GAODE_API_KEY_NO_CONFIG);
}
return entity.getParamValue();
}
/**
* 调用高德地理编码 API 获取 adcode
*/
private String getAdCode(String location) {
String url = String.format(GAODE_GEOCODE_REGEO, getGaoDeKey(), location);
try {
String response = HttpUtil.get(url);
if (StrUtil.isEmpty(response)) {
log.debug("gaode_地理编码接口返回空url{}", url);
return null;
}
JSONObject jsonObject = JsonUtils.parseObject(response, JSONObject.class);
if (jsonObject == null || !"OK".equals(jsonObject.getStr("info"))) {
log.debug("gaode_地理编码接口返回失败url{}, response{}", url, response);
return null;
}
JSONObject regeocode = jsonObject.getJSONObject("regeocode");
if (regeocode == null) {
return null;
}
JSONObject addressComponent = regeocode.getJSONObject("addressComponent");
if (addressComponent == null) {
return null;
}
return addressComponent.getStr("adcode");
} catch (Exception e) {
log.error("gaode_地理编码请求异常url{}", url, e);
return null;
}
}
/**
* 根据 adcode 调用高德天气 API 获取实时天气
*/
private String getWeatherByAdCode(String adCode) {
String url = String.format(GAODE_WEATHER_API, getGaoDeKey(), adCode);
try {
String response = HttpUtil.get(url);
if (StrUtil.isEmpty(response)) {
log.debug("gaode_天气接口返回空adCode{}", adCode);
return null;
}
JSONObject weatherObject = JsonUtils.parseObject(response, JSONObject.class);
if (weatherObject == null) {
log.debug("gaode_天气接口返回非 JSONadCode{}", adCode);
return null;
}
// 高德天气成功状态为 "1"
if (!"1".equals(weatherObject.getStr("status"))) {
log.debug("gaode_天气接口返回失败adCode{}, response{}", adCode, response);
return null;
}
JSONArray lives = weatherObject.getJSONArray("lives");
if (lives == null || lives.isEmpty()) {
log.debug("gaode_天气接口返回 lives 为空adCode{}", adCode);
return null;
}
JSONObject firstLive = lives.getJSONObject(0);
return firstLive.toString();
} catch (Exception e) {
log.error("gaode_天气请求异常url{}", url, e);
return null;
}
}
}

View File

@ -2,9 +2,11 @@ package com.multictrl.modules.job.task;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.constant.FlightTaskType;
import com.multictrl.common.utils.CacheUtils; import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.JsonUtils; import com.multictrl.common.utils.JsonUtils;
import com.multictrl.common.utils.Utils; import com.multictrl.common.utils.Utils;
import com.multictrl.modules.business.dto.WeatherNoflyTaskDTO;
import com.multictrl.modules.business.dto.flight.FlightExecute; import com.multictrl.modules.business.dto.flight.FlightExecute;
import com.multictrl.modules.business.service.FlightTaskService; import com.multictrl.modules.business.service.FlightTaskService;
import com.multictrl.modules.business.service.RouteFlightService; import com.multictrl.modules.business.service.RouteFlightService;
@ -50,10 +52,19 @@ public class RouteTask implements ITask {
//执行任务 //执行任务
FlightExecute flightExecute = new FlightExecute(); FlightExecute flightExecute = new FlightExecute();
flightExecute.setRouteId(Long.parseLong(dto.getParams())); flightExecute.setRouteId(Long.parseLong(dto.getParams()));
routeFlightService.flightExecute(dockSn, flightExecute, false); String result = routeFlightService.flightExecute(dockSn, flightExecute, false, FlightTaskType.SCHEDULED.getCode());
//添加日志 //添加日志
Utils.sleep(1000); Utils.sleep(1000);
String taskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn); String taskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn);
flightTaskService.addJobRouteTask(taskId, jobId); if (result.contains("阻飞")) {
WeatherNoflyTaskDTO noflyTask = new WeatherNoflyTaskDTO();
noflyTask.setTaskId(taskId);
noflyTask.setJobId(jobId);
noflyTask.setTaskType(FlightTaskType.SCHEDULED.getCode());
noflyTask.setReason(result);
flightTaskService.addWeatherNoflyTask(noflyTask);
} else {
flightTaskService.addJobRouteTask(taskId, jobId);
}
} }
} }

View File

@ -1,14 +1,17 @@
package com.multictrl.modules.sys.controller; package com.multictrl.modules.sys.controller;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.multictrl.common.annotation.ApiOrder; import com.multictrl.common.annotation.ApiOrder;
import com.multictrl.common.utils.Result; import com.multictrl.common.utils.Result;
import com.multictrl.modules.business.influxdb.UavReport; import com.multictrl.modules.business.influxdb.UavReport;
import com.multictrl.modules.business.service.InfluxService; import com.multictrl.modules.business.service.InfluxService;
import com.multictrl.modules.business.service.WeatherService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap; import java.util.HashMap;
@ -25,6 +28,7 @@ import java.util.List;
@RequiredArgsConstructor @RequiredArgsConstructor
public class IndexController { public class IndexController {
private final InfluxService influxService; private final InfluxService influxService;
private final WeatherService weatherService;
@GetMapping("/") @GetMapping("/")
public Result<String> index() { public Result<String> index() {
@ -42,4 +46,11 @@ public class IndexController {
System.out.println(flyList); System.out.println(flyList);
return new Result<>(); return new Result<>();
} }
@Operation(summary = "经纬度获取天气信息")
@GetMapping("/getWeatherByLocation")
public Result<JSONObject> getWeatherByLocation(@RequestParam Double lon, @RequestParam Double lat) {
String data = weatherService.getWeatherByLonLat(lon, lat);
return new Result<JSONObject>().ok(JSONObject.parseObject(data));
}
} }

View File

@ -72,4 +72,6 @@ public interface ErrorCode {
int ZHIMOU_NO_CONFIG = 20031; int ZHIMOU_NO_CONFIG = 20031;
int ZHIMOU_REQUEST_ERROR = 20032; int ZHIMOU_REQUEST_ERROR = 20032;
int ZHIMOU_TASK_NOT_EXIST = 20033; int ZHIMOU_TASK_NOT_EXIST = 20033;
int GAODE_API_KEY_NO_CONFIG = 20034;
int GAODE_REQUEST_ERROR = 20035;
} }

View File

@ -61,3 +61,5 @@
20031=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u4FE1\u606F\u672A\u914D\u7F6E 20031=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u4FE1\u606F\u672A\u914D\u7F6E
20032=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u8BF7\u6C42\u51FA\u9519 20032=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u8BF7\u6C42\u51FA\u9519
20033=AI\u4EFB\u52A1\u672A\u5F00\u542F 20033=AI\u4EFB\u52A1\u672A\u5F00\u542F
20034=\u5929\u6C14\u4FE1\u606F\u8BBF\u95EEkey\u672A\u914D\u7F6E
20035=\u5929\u6C14\u63A5\u53E3\u8BF7\u6C42\u51FA\u9519

View File

@ -2352,3 +2352,79 @@ ALTER TABLE public.bus_flight_task
ADD ai_media_num int4 NULL; ADD ai_media_num int4 NULL;
COMMENT COMMENT
ON COLUMN public.bus_flight_task.ai_media_num IS 'ai媒体数量'; ON COLUMN public.bus_flight_task.ai_media_num IS 'ai媒体数量';
--2026/6/17
DROP TABLE IF EXISTS "public"."bus_weather_nofly";
CREATE TABLE "public"."bus_weather_nofly"
(
"id" int8 NOT NULL,
"dock_sn" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"is_open" bool NOT NULL,
"wind_speed" float8,
"rainfall" int4,
"is_all_prevent" bool NOT NULL,
"is_open_weather" bool NOT NULL,
"wind_speed_weather" float8,
"rainfall_weather" int4,
"snow_weather" int4,
"fog_weather" int4,
"haze_weather" int4,
"is_hail_weather" bool,
"is_sandstorm_weather" bool,
"is_tornado_weather" bool,
"temperature_max" float8,
"temperature_min" float8,
"creator" int8,
"create_date" timestamp(6),
"updater" int8,
"update_date" timestamp(6)
)
;
COMMENT
ON COLUMN "public"."bus_weather_nofly"."dock_sn" IS '机库sn';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."is_open" IS '是否开启阻飞(开启后使用机库上报数据)';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."wind_speed" IS '风速m/s';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."rainfall" IS '0关闭 1小雨 2中雨 3大雨';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."is_all_prevent" IS '是否全部阻飞(开启后全部阻飞不开启只阻飞计划库)';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."is_open_weather" IS '是否开启天气阻飞(开启后在机库数据上增加天气预报数据)';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."wind_speed_weather" IS '风速m/s天气预报';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."rainfall_weather" IS '雨(天气预报 0关闭 1小雨 2中雨 3大雨';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."snow_weather" IS '雪(天气预报 0关闭 1小雪 2中雪 3大雪';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."fog_weather" IS '雾(天气预报 0关闭 1轻雾 2中雾 3大雾';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."haze_weather" IS '霾(天气预报 0关闭 1轻度霾 2中度霾 3重度霾';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."is_hail_weather" IS '冰雹阻飞';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."is_sandstorm_weather" IS '沙尘暴阻飞';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."is_tornado_weather" IS '龙卷风阻飞';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."temperature_max" IS '温度上限(摄氏度)';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."temperature_min" IS '温度下限(摄氏度)';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."creator" IS '创建人';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."create_date" IS '创建时间';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."updater" IS '修改人';
COMMENT
ON COLUMN "public"."bus_weather_nofly"."update_date" IS '修改时间';
COMMENT
ON TABLE "public"."bus_weather_nofly" IS '天气阻飞';
-- ----------------------------
-- Primary Key structure for table bus_weather_nofly
-- ----------------------------
ALTER TABLE "public"."bus_weather_nofly"
ADD CONSTRAINT "uav_hangar_prevent_pkey" PRIMARY KEY ("id");