Merge remote-tracking branch 'origin/dev' into dev
6 files added
26 files modified
2 files deleted
| | |
| | | * 查询地图管理列表 |
| | | */ |
| | | @ApiOperation("查询地图管理列表") |
| | | @PreAuthorize("@ss.hasPermi('system:map:list')") |
| | | @PreAuthorize("@ss.hasPermi('data:map:list')") |
| | | @GetMapping("/list") |
| | | public TableDataInfo list(ArdMap ardMap) { |
| | | startPage(); |
| | |
| | | * 导出地图管理列表 |
| | | */ |
| | | @ApiOperation("导出地图管理列表") |
| | | @PreAuthorize("@ss.hasPermi('system:map:export')") |
| | | @PreAuthorize("@ss.hasPermi('data:map:export')") |
| | | @Log(title = "地图管理", businessType = BusinessType.EXPORT) |
| | | @PostMapping("/export") |
| | | public void export(HttpServletResponse response, ArdMap ardMap) { |
| | |
| | | * 获取地图管理详细信息 |
| | | */ |
| | | @ApiOperation("获取地图管理详细信息") |
| | | @PreAuthorize("@ss.hasPermi('system:map:query')") |
| | | @PreAuthorize("@ss.hasPermi('data:map:query')") |
| | | @GetMapping(value = "/{id}") |
| | | public AjaxResult getInfo(@PathVariable("id") String id) { |
| | | return success(ardMapService.selectArdMapById(id)); |
| | |
| | | * 新增地图管理 |
| | | */ |
| | | @ApiOperation("新增地图管理") |
| | | @PreAuthorize("@ss.hasPermi('system:map:add')") |
| | | @PreAuthorize("@ss.hasPermi('data:map:add')") |
| | | @Log(title = "地图管理", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult add(@RequestBody ArdMap ardMap) { |
| | |
| | | * 修改地图管理 |
| | | */ |
| | | @ApiOperation("修改地图管理") |
| | | @PreAuthorize("@ss.hasPermi('system:map:edit')") |
| | | @PreAuthorize("@ss.hasPermi('data:map:edit')") |
| | | @Log(title = "地图管理", businessType = BusinessType.UPDATE) |
| | | @PutMapping |
| | | public AjaxResult edit(@RequestBody ArdMap ardMap) { |
| | |
| | | * 删除地图管理 |
| | | */ |
| | | @ApiOperation("删除地图管理") |
| | | @PreAuthorize("@ss.hasPermi('system:map:remove')") |
| | | @PreAuthorize("@ss.hasPermi('data:map:remove')") |
| | | @Log(title = "地图管理", businessType = BusinessType.DELETE) |
| | | @DeleteMapping("/{ids}") |
| | | public AjaxResult remove(@PathVariable String[] ids) { |
| | |
| | | * 查询模型管理列表 |
| | | */ |
| | | @ApiOperation("查询模型管理列表") |
| | | @PreAuthorize("@ss.hasPermi('system:model:list')") |
| | | @PreAuthorize("@ss.hasPermi('data:model:list')") |
| | | @GetMapping("/list") |
| | | public TableDataInfo list(ArdModel ardModel) { |
| | | startPage(); |
| | |
| | | * 导出模型管理列表 |
| | | */ |
| | | @ApiOperation("导出模型管理列表") |
| | | @PreAuthorize("@ss.hasPermi('system:model:export')") |
| | | @PreAuthorize("@ss.hasPermi('data:model:export')") |
| | | @Log(title = "模型管理", businessType = BusinessType.EXPORT) |
| | | @PostMapping("/export") |
| | | public void export(HttpServletResponse response, ArdModel ardModel) { |
| | |
| | | * 获取模型管理详细信息 |
| | | */ |
| | | @ApiOperation("获取模型管理详细信息") |
| | | @PreAuthorize("@ss.hasPermi('system:model:query')") |
| | | @PreAuthorize("@ss.hasPermi('data:model:query')") |
| | | @GetMapping(value = "/{id}") |
| | | public AjaxResult getInfo(@PathVariable("id") String id) { |
| | | return success(ardModelService.selectArdModelById(id)); |
| | |
| | | * 新增模型管理 |
| | | */ |
| | | @ApiOperation("新增模型管理") |
| | | @PreAuthorize("@ss.hasPermi('system:model:add')") |
| | | @PreAuthorize("@ss.hasPermi('data:model:add')") |
| | | @Log(title = "模型管理", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult add(@RequestBody ArdModel ardModel) { |
| | |
| | | * 修改模型管理 |
| | | */ |
| | | @ApiOperation("修改模型管理") |
| | | @PreAuthorize("@ss.hasPermi('system:model:edit')") |
| | | @PreAuthorize("@ss.hasPermi('data:model:edit')") |
| | | @Log(title = "模型管理", businessType = BusinessType.UPDATE) |
| | | @PutMapping |
| | | public AjaxResult edit(@RequestBody ArdModel ardModel) { |
| | |
| | | * 删除模型管理 |
| | | */ |
| | | @ApiOperation("删除模型管理") |
| | | @PreAuthorize("@ss.hasPermi('system:model:remove')") |
| | | @PreAuthorize("@ss.hasPermi('data:model:remove')") |
| | | @Log(title = "模型管理", businessType = BusinessType.DELETE) |
| | | @DeleteMapping("/{ids}") |
| | | public AjaxResult remove(@PathVariable String[] ids) { |
| | |
| | | * 查询tile管理列表 |
| | | */ |
| | | @ApiOperation("查询tile管理列表") |
| | | @PreAuthorize("@ss.hasPermi('system:tile:list')") |
| | | @PreAuthorize("@ss.hasPermi('data:tile:list')") |
| | | @GetMapping("/list") |
| | | public TableDataInfo list(ArdTile ardTile) { |
| | | startPage(); |
| | |
| | | * 导出tile管理列表 |
| | | */ |
| | | @ApiOperation("导出tile管理列表") |
| | | @PreAuthorize("@ss.hasPermi('system:tile:export')") |
| | | @PreAuthorize("@ss.hasPermi('data:tile:export')") |
| | | @Log(title = "tile管理", businessType = BusinessType.EXPORT) |
| | | @PostMapping("/export") |
| | | public void export(HttpServletResponse response, ArdTile ardTile) { |
| | |
| | | * 获取tile管理详细信息 |
| | | */ |
| | | @ApiOperation("获取tile管理详细信息") |
| | | @PreAuthorize("@ss.hasPermi('system:tile:query')") |
| | | @PreAuthorize("@ss.hasPermi('data:tile:query')") |
| | | @GetMapping(value = "/{id}") |
| | | public AjaxResult getInfo(@PathVariable("id") String id) { |
| | | return success(ardTileService.selectArdTileById(id)); |
| | |
| | | * 新增tile管理 |
| | | */ |
| | | @ApiOperation("新增tile管理") |
| | | @PreAuthorize("@ss.hasPermi('system:tile:add')") |
| | | @PreAuthorize("@ss.hasPermi('data:tile:add')") |
| | | @Log(title = "tile管理", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult add(@RequestBody ArdTile ardTile) { |
| | |
| | | * 修改tile管理 |
| | | */ |
| | | @ApiOperation("修改tile管理") |
| | | @PreAuthorize("@ss.hasPermi('system:tile:edit')") |
| | | @PreAuthorize("@ss.hasPermi('data:tile:edit')") |
| | | @Log(title = "tile管理", businessType = BusinessType.UPDATE) |
| | | @PutMapping |
| | | public AjaxResult edit(@RequestBody ArdTile ardTile) { |
| | |
| | | * 删除tile管理 |
| | | */ |
| | | @ApiOperation("删除tile管理") |
| | | @PreAuthorize("@ss.hasPermi('system:tile:remove')") |
| | | @PreAuthorize("@ss.hasPermi('data:tile:remove')") |
| | | @Log(title = "tile管理", businessType = BusinessType.DELETE) |
| | | @DeleteMapping("/{ids}") |
| | | public AjaxResult remove(@PathVariable String[] ids) { |
| | |
| | | */ |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("/device/cameras") |
| | | @RequestMapping("/device/camera") |
| | | @Api(tags = "相机管理接口") |
| | | public class ArdCameraController extends BaseController { |
| | | |
| | |
| | | /** |
| | | * 查询相机设备列表 |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('device:cameras:list')") |
| | | @PreAuthorize("@ss.hasPermi('device:camera:list')") |
| | | @GetMapping("/list") |
| | | @ApiOperation("查询相机设备列表") |
| | | public TableDataInfo list(ArdCamera ardCamera) { |
| | |
| | | * 导出相机设备列表 |
| | | */ |
| | | @ApiOperation("导出相机设备列表") |
| | | @PreAuthorize("@ss.hasPermi('device:cameras:export')") |
| | | @PreAuthorize("@ss.hasPermi('device:camera:export')") |
| | | @Log(title = "相机设备", businessType = BusinessType.EXPORT) |
| | | @PostMapping("/export") |
| | | public void export(HttpServletResponse response, ArdCamera ardCamera) { |
| | |
| | | * 获取相机设备详细信息 |
| | | */ |
| | | @ApiOperation("获取相机设备详细信息") |
| | | @PreAuthorize("@ss.hasPermi('device:cameras:query')") |
| | | @PreAuthorize("@ss.hasPermi('device:camera:query')") |
| | | @GetMapping(value = "/{id}") |
| | | public AjaxResult getInfo(@PathVariable("id") String id) { |
| | | return success(ardCameraService.selectArdCameraById(id)); |
| | |
| | | * 新增相机设备 |
| | | */ |
| | | @ApiOperation("新增相机设备") |
| | | @PreAuthorize("@ss.hasPermi('device:cameras:add')") |
| | | @PreAuthorize("@ss.hasPermi('device:camera:add')") |
| | | @Log(title = "相机设备", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult add(@RequestBody ArdCamera camera) { |
| | |
| | | * 修改相机设备 |
| | | */ |
| | | @ApiOperation("修改相机设备") |
| | | @PreAuthorize("@ss.hasPermi('device:cameras:edit')") |
| | | @PreAuthorize("@ss.hasPermi('device:camera:edit')") |
| | | @Log(title = "相机设备", businessType = BusinessType.UPDATE) |
| | | @PutMapping |
| | | public AjaxResult edit(@RequestBody ArdCamera camera) { |
| | |
| | | * 删除相机设备 |
| | | */ |
| | | @ApiOperation("删除相机设备") |
| | | @PreAuthorize("@ss.hasPermi('device:cameras:remove')") |
| | | @PreAuthorize("@ss.hasPermi('device:camera:remove')") |
| | | @Log(title = "相机设备", businessType = BusinessType.DELETE) |
| | | @DeleteMapping("/{ids}") |
| | | public AjaxResult remove(@PathVariable String[] ids) { |
| | |
| | | |
| | | |
| | | @Log(title = "导入相机设备", businessType = BusinessType.IMPORT) |
| | | @PreAuthorize("@ss.hasPermi('device:cameras:import')") |
| | | @PreAuthorize("@ss.hasPermi('device:camera:import')") |
| | | @PostMapping("/importData") |
| | | @ApiOperation("导入相机设备") |
| | | public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { |
| | |
| | | return toAjax(ardDroneInfoService.deleteArdDroneByIds(ids)); |
| | | } |
| | | |
| | | /** |
| | | * 批量新增无人机盒子 |
| | | */ |
| | | @ApiOperation("批量新增无人机盒子") |
| | | @PreAuthorize("@ss.hasPermi('work:drone:insertArdDroneLs')") |
| | | @Log(title = "无人机管理", businessType = BusinessType.DELETE) |
| | | @PostMapping("insertArdDroneLs/{boxSn}/{deptId}") |
| | | public AjaxResult insertArdDroneLs(@PathVariable String[] boxSn, @PathVariable Long deptId) { |
| | | try { |
| | | int result = ardDroneInfoService.insertArdDroneLs(boxSn, deptId); |
| | | if (result > 0) { |
| | | return AjaxResult.success("成功新增 " + result + " 个无人机盒子"); |
| | | } else { |
| | | return AjaxResult.success("没有需要新增的无人机盒子"); |
| | | } |
| | | } catch (Exception e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | @ApiOperation("修改服务器状态") |
| | | @PreAuthorize("@ss.hasPermi('work:server:updateStatus')") |
| | | @Log(title = "服务器管理", businessType = BusinessType.UPDATE) |
| | | @PutMapping("/{id}") |
| | | public AjaxResult updateServerStatus(@PathVariable("id") String id) { |
| | | return toAjax(ardServerInfoService.updateServerStatus(id)); |
| | | @PutMapping("/{id}/{status}") |
| | | public AjaxResult updateServerStatus(@PathVariable("id") String id,@PathVariable("status") String status) { |
| | | return toAjax(ardServerInfoService.updateServerStatus(id,status)); |
| | | } |
| | | |
| | | } |
| | |
| | | return AjaxResult.success(tyService.getAccessToken(serverId)); |
| | | } |
| | | |
| | | @ApiOperation("获取六合盒子列表") |
| | | @ApiOperation("获取盒子列表") |
| | | @ApiOperationSupport(order = 2) |
| | | @GetMapping("/getBoxList") |
| | | public AjaxResult getBoxList() { |
| | | List<BoxInfoReceiver> boxList = tyService.getBoxList(); |
| | | return AjaxResult.success(boxList); |
| | | public AjaxResult getBoxList(@RequestParam(required = false) String serverId) { |
| | | if (serverId == null) { |
| | | return AjaxResult.success(tyService.getBoxList()); |
| | | } else { |
| | | return AjaxResult.success(tyService.getBoxList(serverId)); |
| | | } |
| | | } |
| | | |
| | | @ApiOperation("获取飞行历史") |
| | | @ApiOperationSupport(order = 3) |
| | | @ApiOperationSupport(order = 4) |
| | | @PostMapping("/getBoxHistory") |
| | | public AjaxResult getBoxHistory(@RequestBody BoxHistoryDTO boxHistoryDTO) { |
| | | List<BoxHistoryReceiver> boxHistory = tyService.getBoxHistory(boxHistoryDTO); |
| | |
| | | } |
| | | |
| | | @ApiOperation("获取任务照片") |
| | | @ApiOperationSupport(order = 4) |
| | | @ApiOperationSupport(order = 5) |
| | | @PostMapping("/getBoxPhotos") |
| | | public AjaxResult getBoxPhotos(@RequestBody BoxPhotosDTO boxPhotosDTO) { |
| | | List<BoxPhotosReceiver> boxPhotos = tyService.getBoxPhotos(boxPhotosDTO); |
| | |
| | | } |
| | | |
| | | @ApiOperation("获取直播地址") |
| | | @ApiOperationSupport(order = 5) |
| | | @ApiOperationSupport(order = 6) |
| | | @GetMapping("/getBoxList/{boxSn}") |
| | | public AjaxResult getBoxLiveUrl(@PathVariable("boxSn") String boxSn) { |
| | | BoxStreamUrlReceiver boxLiveUrl = tyService.getBoxLiveUrl(boxSn); |
| | |
| | | } |
| | | |
| | | @ApiOperation("修改云盒设置") |
| | | @ApiOperationSupport(order = 6) |
| | | @ApiOperationSupport(order = 7) |
| | | @PostMapping("/updateBoxSettings") |
| | | public AjaxResult updateBoxSettings(@RequestBody BoxSettingsDTO boxSettingsDTO) { |
| | | Boolean state = tyService.updateBoxSettings(boxSettingsDTO); |
| | |
| | | } |
| | | |
| | | @ApiOperation("根据[服务器id]获取六合盒子列表") |
| | | @ApiOperationSupport(order = 7) |
| | | @ApiOperationSupport(order = 8) |
| | | @GetMapping("/getBoxSnToList/{id}") |
| | | public TableDataInfo getBoxSnToList(@PathVariable("id") String id) { |
| | | startPage(); |
| New file |
| | |
| | | package com.ard.data.noflyzone.domain; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class ArdNoFlyBox { |
| | | private double minx; |
| | | private double miny; |
| | | private double maxx; |
| | | private double maxy; |
| | | } |
| | |
| | | package com.ard.data.noflyzone.domain; |
| | | |
| | | import com.ard.data.noflyzone.utils.GeoUtils; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | import com.ard.common.annotation.Excel; |
| | | import com.ard.common.core.domain.BaseEntity; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 禁飞区管理对象 ard_no_fly_zone |
| | |
| | | /** 数据json */ |
| | | @Excel(name = "数据json") |
| | | private FeatureCollection geojson; |
| | | |
| | | /** 禁飞区外接矩形 */ |
| | | private ArdNoFlyBox ardNoFlyBox; |
| | | |
| | | private List<Vertex> vertices; |
| | | |
| | | // 懒加载 / 动态计算 |
| | | public ArdNoFlyBox getArdNoFlyBox() { |
| | | if (ardNoFlyBox == null && geojson != null) { |
| | | ardNoFlyBox = GeoUtils.getArdNoFlyBox(geojson); |
| | | } |
| | | return ardNoFlyBox; |
| | | } |
| | | |
| | | public List<Vertex> getVertices() { |
| | | if (vertices == null && geojson != null) { |
| | | vertices = GeoUtils.toVertexes(geojson); |
| | | } |
| | | return vertices; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.ard.data.noflyzone.domain; |
| | | |
| | | public class Vertex { |
| | | public double x; |
| | | public double y; |
| | | |
| | | public Vertex() { |
| | | |
| | | } |
| | | |
| | | public Vertex(double x, double y) { |
| | | this.x = x; |
| | | this.y = y; |
| | | } |
| | | |
| | | // 判断这个点是不是和另外一个点重合(允许极小的误差,算作同一个点 |
| | | public boolean IsSame(Vertex anotherV) { |
| | | double almostZero = 0.00000001; |
| | | if ((Math.abs(x - anotherV.x) < almostZero) && (Math.abs(y - anotherV.y) < almostZero)) { |
| | | return true; |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 直线距离 |
| | | public double Distance(Vertex anotherV) { |
| | | return Math.sqrt(Math.pow(x - anotherV.x, 2) + Math.pow(y - anotherV.y, 2)); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ard.data.noflyzone.utils; |
| | | |
| | | import com.ard.data.noflyzone.domain.ArdNoFlyBox; |
| | | import com.ard.data.noflyzone.domain.FeatureCollection; |
| | | import com.ard.data.noflyzone.domain.Vertex; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 几何工具类 |
| | | */ |
| | | public class GeoUtils { |
| | | /** |
| | | * 根据 GeoJSON(FeatureCollection)计算外接矩形 |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static ArdNoFlyBox getArdNoFlyBox(FeatureCollection featureCollection) { |
| | | if (featureCollection == null || featureCollection.getFeatures() == null) { |
| | | return null; |
| | | } |
| | | |
| | | double minx = Double.MAX_VALUE; |
| | | double miny = Double.MAX_VALUE; |
| | | double maxx = Double.MIN_VALUE; |
| | | double maxy = Double.MIN_VALUE; |
| | | |
| | | for (FeatureCollection.Feature feature : featureCollection.getFeatures()) { |
| | | FeatureCollection.Geometry geometry = feature.getGeometry(); |
| | | if (geometry == null || geometry.getCoordinates() == null) { |
| | | continue; |
| | | } |
| | | |
| | | String type = geometry.getType(); |
| | | Object coords = geometry.getCoordinates(); |
| | | |
| | | if ("Polygon".equalsIgnoreCase(type)) { |
| | | // Polygon: [[[lon, lat], [lon, lat], ...]] |
| | | List<List<List<Double>>> rings = (List<List<List<Double>>>) coords; |
| | | for (List<List<Double>> ring : rings) { |
| | | for (List<Double> point : ring) { |
| | | if (point.size() < 2) { |
| | | continue; |
| | | } |
| | | double x = point.get(0); |
| | | double y = point.get(1); |
| | | minx = Math.min(minx, x); |
| | | miny = Math.min(miny, y); |
| | | maxx = Math.max(maxx, x); |
| | | maxy = Math.max(maxy, y); |
| | | } |
| | | } |
| | | } |
| | | else if ("MultiPolygon".equalsIgnoreCase(type)) { |
| | | // MultiPolygon: [[[[lon, lat], ...]], ...] |
| | | List<List<List<List<Double>>>> polygons = (List<List<List<List<Double>>>>) coords; |
| | | for (List<List<List<Double>>> polygon : polygons) { |
| | | for (List<List<Double>> ring : polygon) { |
| | | for (List<Double> point : ring) { |
| | | if (point.size() < 2) { |
| | | continue; |
| | | } |
| | | double x = point.get(0); |
| | | double y = point.get(1); |
| | | minx = Math.min(minx, x); |
| | | miny = Math.min(miny, y); |
| | | maxx = Math.max(maxx, x); |
| | | maxy = Math.max(maxy, y); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return new ArdNoFlyBox(minx, miny, maxx, maxy); |
| | | } |
| | | /** |
| | | * 判断点是否在多边形内(包括边界) |
| | | * 射线法:从该点向右发射水平射线,计算交点个数 |
| | | * |
| | | * @param vertex 点对象 |
| | | * @param vertexes 多边形顶点列表(按顺序闭合) |
| | | * @return true 表示点在多边形内或在边上 |
| | | */ |
| | | public static boolean isVertexInPoly(Vertex vertex, List<Vertex> vertexes) { |
| | | if (vertex == null || vertexes == null || vertexes.size() < 3) { |
| | | return false; // 无效输入 |
| | | } |
| | | |
| | | int count = 0; |
| | | int n = vertexes.size(); |
| | | |
| | | for (int i = 0; i < n; i++) { |
| | | Vertex v1 = vertexes.get(i); |
| | | Vertex v2 = vertexes.get((i + 1) % n); |
| | | |
| | | // 1. 点就是多边形的顶点 |
| | | if (v1.IsSame(vertex)) { |
| | | return true; |
| | | } |
| | | |
| | | // 2. 点在线段上 |
| | | double minX = Math.min(v1.x, v2.x); |
| | | double maxX = Math.max(v1.x, v2.x); |
| | | double minY = Math.min(v1.y, v2.y); |
| | | double maxY = Math.max(v1.y, v2.y); |
| | | |
| | | // 水平线段 |
| | | if (minY == maxY) { |
| | | if (vertex.y == minY && vertex.x >= minX && vertex.x <= maxX) { |
| | | return true; |
| | | } else { |
| | | continue; // 不计入交点 |
| | | } |
| | | } |
| | | |
| | | // 点在线段纵向范围外 |
| | | if (vertex.y < minY || vertex.y > maxY) { |
| | | continue; |
| | | } |
| | | |
| | | // 计算射线交点的 X 坐标 |
| | | double xIntersect = v1.x + (vertex.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y); |
| | | |
| | | // 交点在点左侧,不计入 |
| | | if (xIntersect < vertex.x) { |
| | | continue; |
| | | } |
| | | |
| | | // 交点就在点上,认为在边界 |
| | | if (xIntersect == vertex.x) { |
| | | return true; |
| | | } |
| | | |
| | | // 避免射线穿过线段下端点重复计数 |
| | | if (vertex.y == minY) { |
| | | continue; |
| | | } |
| | | |
| | | // 其它情况,交点计数 |
| | | count++; |
| | | } |
| | | |
| | | // 奇数交点 → 点在多边形内 |
| | | return count % 2 != 0; |
| | | } |
| | | |
| | | /** |
| | | * 将 GeoJSON Polygon 的外环坐标转换成 Vertex 列表 |
| | | */ |
| | | public static List<Vertex> toVertexes(FeatureCollection featureCollection) { |
| | | List<Vertex> vertexes = new ArrayList<>(); |
| | | if (featureCollection == null || featureCollection.getFeatures() == null || featureCollection.getFeatures().isEmpty()) { |
| | | return vertexes; |
| | | } |
| | | FeatureCollection.Feature feature = featureCollection.getFeatures().get(0); |
| | | if (feature.getGeometry() != null) { |
| | | List<List<List<Double>>> coords = feature.getGeometry().getCoordinates(); |
| | | if (coords != null && !coords.isEmpty()) { |
| | | List<List<Double>> outerRing = coords.get(0); |
| | | for (List<Double> point : outerRing) { |
| | | if (point.size() >= 2) { |
| | | vertexes.add(new Vertex(point.get(0), point.get(1))); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return vertexes; |
| | | } |
| | | |
| | | } |
| | |
| | | <dependency> |
| | | <groupId>com.dtflys.forest</groupId> |
| | | <artifactId>forest-spring-boot-starter</artifactId> |
| | | <version>1.5.33</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>org.projectlombok</groupId> |
| New file |
| | |
| | | package com.ard.vtdu.client; |
| | | |
| | | import com.ard.vtdu.config.MediamtxConfig; |
| | | import com.dtflys.forest.http.ForestRequest; |
| | | import com.dtflys.forest.interceptor.ForestInterceptor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | import java.util.Base64; |
| | | |
| | | /** |
| | | * 这个拦截器暂时先不使用,需要时在启用 |
| | | * 用于mediaMtx流媒体接口安全请求 |
| | | * |
| | | * @author lsy |
| | | * @date 2025/10/16 |
| | | */ |
| | | |
| | | @Component |
| | | @Slf4j |
| | | public class MediamtxAuthInterceptor implements ForestInterceptor { |
| | | |
| | | /** |
| | | * 该方法在请求发送之前被调用, 若返回false则不会继续发送请求 |
| | | * |
| | | * @Param request Forest请求对象 |
| | | */ |
| | | @Override |
| | | public boolean beforeExecute(ForestRequest req) { |
| | | try { |
| | | |
| | | String username = "admin"; |
| | | String password = "mypass"; |
| | | //对应mediamtx.yml |
| | | //- user: argon2:$argon2id$v=19$m=4096,t=3,p=1$c2FsdEl0V2l0aFNhbHQ$Mi4H+feNHQk/E0sm46PEz3dnHPTzZMNWV5LVdDD/4SI |
| | | // pass: argon2:$argon2id$v=19$m=4096,t=3,p=1$c2FsdEl0V2l0aFNhbHQ$5VEdlOBG/er50vvc4GZO4uA3juhk/uvUzYOzTMv6RiQ |
| | | |
| | | // 拼接用户名和密码 |
| | | String authString = username + ":" + password; |
| | | |
| | | // Base64 编码 |
| | | String encodedAuth = Base64.getEncoder().encodeToString(authString.getBytes()); |
| | | |
| | | // 最终的 Authorization 头 |
| | | String authorizationHeader = "Basic " + encodedAuth; |
| | | |
| | | log.debug("Authorization header: " + authorizationHeader); |
| | | req.addHeader("Authorization", authorizationHeader); |
| | | } catch (Exception e) { |
| | | log.error("TokenInterceptor 处理异常:{}", e.getMessage()); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | } |
| | |
| | | <dependency> |
| | | <groupId>com.dtflys.forest</groupId> |
| | | <artifactId>forest-spring-boot-starter</artifactId> |
| | | <version>1.8.0</version> |
| | | </dependency> |
| | | |
| | | <!-- netty依赖 springboot2.x自动导入版本 --> |
| | |
| | | import com.ard.work.domain.receiver.AccessTokenReceiver; |
| | | import com.ard.work.service.IArdServerInfoService; |
| | | import com.dtflys.forest.http.ForestRequest; |
| | | import com.dtflys.forest.interceptor.Interceptor; |
| | | import com.dtflys.forest.interceptor.ForestInterceptor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | import javax.annotation.Resource; |
| | | |
| | | @Component |
| | | @Slf4j |
| | | public class TokenInterceptor<T> implements Interceptor<T> { |
| | | public class TokenInterceptor implements ForestInterceptor { |
| | | @Resource |
| | | private IArdServerInfoService ardServerService; |
| | | |
| | |
| | | */ |
| | | @Override |
| | | public boolean beforeExecute(ForestRequest req) { |
| | | log.debug("invoke Simple beforeExecute"); |
| | | String serverId = (String) req.getVariableValue("serverId"); |
| | | AccessTokenReceiver accessToken = ardServerService.getAccessToken(serverId); |
| | | req.addHeader("x-cid", accessToken.getCompanyId()); |
| | | req.addHeader("x-token", accessToken.getAccessToken()); |
| | | return true; // 继续执行请求返回true |
| | | try { |
| | | String serverId = (String) req.getVariableValue("serverId"); |
| | | AccessTokenReceiver accessToken = ardServerService.getAccessToken(serverId); |
| | | if (accessToken == null) { |
| | | log.warn("AccessToken 为 null, serverId={}", serverId); |
| | | return false; // 阻止请求继续 |
| | | } |
| | | req.addHeader("x-cid", accessToken.getCompanyId()); |
| | | req.addHeader("x-token", accessToken.getAccessToken()); |
| | | } catch (Exception e) { |
| | | log.error("TokenInterceptor 处理异常:{}", e.getMessage()); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ard.work.component.netty; |
| | | |
| | | import com.ard.work.component.netty.command.BaseCmd; |
| | | import com.ard.work.component.netty.cache.TyTcpServerCache; |
| | | import io.netty.channel.Channel; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import java.net.InetSocketAddress; |
| | | |
| | | /** |
| | | * Description: |
| | | * Author: lsy |
| | | * Date: 2025年10月17日14:27 |
| | | **/ |
| | | @Slf4j |
| | | public class TcpClientUtil { |
| | | |
| | | /** |
| | | * 向指定边缘服务器发送消息 |
| | | */ |
| | | public static void sendMessage(String serverId, BaseCmd msg) { |
| | | try { |
| | | Channel channel = TyTcpServerCache.getChannel(serverId); |
| | | if (channel == null) { |
| | | log.error("未找到边缘服务器 [{}] 的连接", serverId); |
| | | return; |
| | | } |
| | | |
| | | if (!channel.isActive()) { |
| | | log.warn("边缘服务器 [{}] 连接未激活,无法发送消息", serverId); |
| | | return; |
| | | } |
| | | |
| | | InetSocketAddress remote = (InetSocketAddress) channel.remoteAddress(); |
| | | channel.writeAndFlush(msg); |
| | | log.warn("→ 向边缘服务器 [{}] ({}:{}) 发送消息,命令码:0x{}", |
| | | serverId, remote.getHostString(), remote.getPort(), |
| | | String.format("%02X", msg.getCmdCode() & 0xFF)); |
| | | } catch (Exception e) { |
| | | log.error("发送消息到边缘服务器 [{}] 异常:{}", serverId, e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | |
| | | package com.ard.work.component.netty; |
| | | |
| | | import com.ard.work.component.netty.command.BaseCmd; |
| | | import com.ard.work.component.netty.command.register.HeartbeatCmd; |
| | | import com.ard.work.component.netty.handler.ClientHandler; |
| | | import com.ard.work.component.netty.handler.MessageProtocolDecoder; |
| | | import com.ard.work.component.netty.handler.MessageProtocolEncoder; |
| | | import com.ard.work.domain.cache.TyTcpServerCache; |
| | | import com.ard.work.component.netty.cache.TyTcpServerCache; |
| | | import com.ard.work.domain.entity.ArdServerInfo; |
| | | import com.ard.work.service.IArdServerInfoService; |
| | | import io.netty.bootstrap.Bootstrap; |
| | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.PostConstruct; |
| | | import javax.annotation.Resource; |
| | | import java.net.InetSocketAddress; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | |
| | | @Component |
| | | @Slf4j |
| | | public class TyTcpClientManager { |
| | | /** |
| | | * 以 serverId 作为唯一 key |
| | | */ |
| | | public static final Map<String, TcpClientSession> channels = new ConcurrentHashMap<>(); |
| | | |
| | | |
| | | private final EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); |
| | | |
| | |
| | | return bootstrap; |
| | | } |
| | | |
| | | /** 新增或重连服务器 */ |
| | | /** |
| | | * 新增或重连服务器 |
| | | */ |
| | | public void connectServer(ArdServerInfo serverInfo) { |
| | | String serverId = serverInfo.getId(); |
| | | String host = serverInfo.getHost(); |
| | |
| | | disconnectServer(serverId); |
| | | |
| | | Bootstrap bootstrap = getBootstrap(); |
| | | ChannelFuture future = bootstrap.connect(host, port).addListener((ChannelFuture f) -> { |
| | | bootstrap.connect(host, port).addListener((ChannelFuture f) -> { |
| | | if (f.isSuccess()) { |
| | | log.info("✅ 服务器 [{}] 连接成功", serverId); |
| | | Channel channel = f.channel(); |
| | | channel.attr(AttributeKey.valueOf("serverId")).set(serverId); |
| | | TyTcpServerCache.putTcpServerCache(serverId, true); |
| | | // 断开服务器,修改状态为: 0 - 在线 |
| | | ardServerService.updateServerStatus(serverId, "1"); |
| | | |
| | | // 成功后再加入 channels |
| | | TyTcpServerCache.putChannel(serverId, channel); |
| | | |
| | | } else { |
| | | log.warn("❌ 服务器 [{}] 连接失败", serverId); |
| | | TyTcpServerCache.putTcpServerCache(serverId, false); |
| | | // 断开服务器,修改状态为: 0 - 离线 |
| | | ardServerService.updateServerStatus(serverId, "0"); |
| | | } |
| | | }); |
| | | |
| | | TcpClientSession session = TcpClientSession.builder() |
| | | .ClientId("TY_" + serverId) |
| | | .channelFuture(future) |
| | | .isReConnect(true) |
| | | .serverId(serverId) |
| | | .host(host) |
| | | .port(port) |
| | | .build(); |
| | | |
| | | channels.put(serverId, session); |
| | | } |
| | | |
| | | /** 断开服务器连接 */ |
| | | /** |
| | | * 断开服务器连接 |
| | | */ |
| | | public void disconnectServer(String serverId) { |
| | | TcpClientSession session = channels.remove(serverId); |
| | | if (session != null && session.getChannelFuture() != null) { |
| | | session.getChannelFuture().channel().close(); |
| | | Channel channel = TyTcpServerCache.getChannel(serverId); |
| | | if (channel != null) { |
| | | channel.close(); |
| | | log.info("断开服务器 [{}] 连接", serverId); |
| | | // 断开服务器,修改状态为: 0 - 离线 |
| | | ardServerService.updateServerStatus(serverId, "0"); |
| | | TyTcpServerCache.removeChannel(serverId); |
| | | } |
| | | TyTcpServerCache.removeTcpServerCache(serverId); |
| | | } |
| | | |
| | | /** |
| | | * 向指定边缘服务器发送消息 |
| | | */ |
| | | public void sendMessage(Long serverId, BaseCmd msg) { |
| | | try { |
| | | TcpClientSession session = channels.get(serverId); |
| | | if (session == null) { |
| | | log.error("未找到六合服务器 [{}] 的连接", serverId); |
| | | return; |
| | | } |
| | | |
| | | ChannelFuture future = session.getChannelFuture(); |
| | | if (future == null || !future.channel().isActive()) { |
| | | log.warn("六合服务器 [{}] 连接未激活,无法发送消息", serverId); |
| | | return; |
| | | } |
| | | |
| | | InetSocketAddress remote = (InetSocketAddress) future.channel().remoteAddress(); |
| | | future.channel().writeAndFlush(msg); |
| | | log.debug("→ 向六合服务器 [{}] ({}:{}) 发送消息,命令码:0x{}", |
| | | serverId, remote.getHostString(), remote.getPort(), |
| | | String.format("%02X", msg.getCmdCode() & 0xFF)); |
| | | } catch (Exception e) { |
| | | log.error("发送消息到六合服务器 [{}] 异常:{}", serverId, e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 启动时从数据库加载所有边缘服务器并建立 TCP 连接 |
| | |
| | | if (Boolean.FALSE.equals(state)) { // 连接失败的服务器 |
| | | ArdServerInfo serverInfo = ardServerService.selectArdServerInfoById(key); |
| | | if (serverInfo != null) { |
| | | log.warn("服务器 [{}] 连接失败,尝试重连", key); |
| | | connectServer(serverInfo); |
| | | } |
| | | } |
| | |
| | | @Scheduled(fixedRate = 5000) // 每5秒执行一次 |
| | | public void sendHeartBeat() { |
| | | long timestamp = System.currentTimeMillis(); |
| | | channels.values().stream() |
| | | .filter(session -> { |
| | | ChannelFuture future = session.getChannelFuture(); |
| | | // 只保留活跃连接 |
| | | return future != null && future.channel().isActive(); |
| | | }) |
| | | .forEach(session -> { |
| | | TyTcpServerCache.getChannels().stream() |
| | | // 过滤出仍然活跃的连接 |
| | | .filter(channel -> channel != null && channel.isActive()) |
| | | .forEach(channel -> { |
| | | String serverId = (String) channel.attr(AttributeKey.valueOf("serverId")).get(); |
| | | HeartbeatCmd heartbeat = new HeartbeatCmd(timestamp); |
| | | ChannelFuture future = session.getChannelFuture(); |
| | | future.channel().writeAndFlush(heartbeat); |
| | | log.info("→ 向边缘服务器 [{}] ({}:{}) 发送心跳,时间戳={}", |
| | | session.getServerId(), session.getHost(), session.getPort(), timestamp); |
| | | channel.writeAndFlush(heartbeat); |
| | | log.info("→ 向边缘服务器 [{}]发送心跳,时间戳={}", serverId, timestamp); |
| | | }); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.ard.work.component.netty.cache; |
| | | |
| | | import io.netty.channel.Channel; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * 六合tcp服务器缓存 |
| | | * |
| | | * @author liusuyi |
| | | * @date 2024年11月01日13:53 |
| | | **/ |
| | | public class TyTcpServerCache { |
| | | /** |
| | | * 以 serverId 作为唯一 key 存储 Channel |
| | | */ |
| | | private static Map<String, Channel> channels = new ConcurrentHashMap<>(); |
| | | |
| | | //存储通道 |
| | | public static void putChannel(String serverId, Channel channel) { |
| | | channels.put(serverId, channel); |
| | | } |
| | | |
| | | //获取通道 |
| | | public static Channel getChannel(String serverId) { |
| | | return channels.get(serverId); |
| | | } |
| | | |
| | | //删除通道 |
| | | public static void removeChannel(String serverId) { |
| | | channels.remove(serverId); |
| | | } |
| | | |
| | | //获取所有 |
| | | public static Collection<Channel> getChannels() { |
| | | return Collections.unmodifiableCollection(channels.values()); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 以 serverId 作为唯一 key 存储 连接状态 |
| | | */ |
| | | private static final ConcurrentHashMap<String, Boolean> tcpServerCache = new ConcurrentHashMap<>(); |
| | | |
| | | // 存储TCP服务连接状态到缓存 |
| | | public static void putTcpServerCache(String serverId, Boolean state) { |
| | | tcpServerCache.put(serverId, state); |
| | | } |
| | | |
| | | // 从缓存中获取TCP服务连接状态 |
| | | public static Boolean getTcpServerCache(String serverId) { |
| | | return tcpServerCache.getOrDefault(serverId, false); |
| | | } |
| | | |
| | | // 删除缓存中的TCP服务连接状态 |
| | | public static void removeTcpServerCache(String serverId) { |
| | | tcpServerCache.remove(serverId); |
| | | } |
| | | |
| | | public static Map<String, Boolean> getTcpServerCacheMap() { |
| | | return new HashMap<>(tcpServerCache); // 返回一个副本,避免外部修改原始缓存 |
| | | } |
| | | |
| | | } |
| | |
| | | package com.ard.work.component.netty.handler; |
| | | |
| | | import com.ard.work.component.netty.TyTcpClientManager; |
| | | import com.ard.work.component.netty.command.register.HeartbeatCmd; |
| | | import com.ard.work.domain.cache.TyTcpServerCache; |
| | | import com.ard.work.component.netty.command.BaseCmd; |
| | | import com.ard.work.component.netty.command.register.HeartbeatCmd; |
| | | import com.ard.work.component.netty.cache.TyTcpServerCache; |
| | | import com.ard.work.service.ITyService; |
| | | import io.netty.channel.Channel; |
| | | import io.netty.channel.ChannelHandler; |
| | | import io.netty.channel.ChannelHandlerContext; |
| | | import io.netty.channel.SimpleChannelInboundHandler; |
| | | import io.netty.handler.timeout.IdleState; |
| | | import io.netty.handler.timeout.IdleStateEvent; |
| | | import io.netty.util.AttributeKey; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | import javax.annotation.Resource; |
| | | import java.net.InetSocketAddress; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | |
| | | @Component |
| | |
| | | |
| | | @Resource |
| | | private ITyService tyService; |
| | | |
| | | @Resource |
| | | private TyTcpClientManager tyTcpClientManager; |
| | | |
| | | @Override |
| | | public void channelRead0(ChannelHandlerContext ctx, BaseCmd command) { |
| | |
| | | import com.ard.common.annotation.Excel; |
| | | import com.ard.common.core.domain.BaseEntity; |
| | | import com.ard.work.domain.receiver.BoxInfoReceiver; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import lombok.*; |
| | | |
| | | import java.util.Date; |
| | |
| | | private String boxVersion; |
| | | |
| | | /** 启动时间 */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @Excel(name = "启动时间") |
| | | private Date startTime; |
| | | |
| | |
| | | @Excel(name = "服务器管理ID") |
| | | private String serverId; |
| | | |
| | | /** 盒子别名 */ |
| | | @Excel(name = "盒子别名") |
| | | private String boxAliasName; |
| | | |
| | | /** |
| | | * 服务器实体对象 |
| | | */ |
| | |
| | | /** 部门ID */ |
| | | private Long deptId; |
| | | |
| | | /** 服务器状态 */ |
| | | private Long serverStatus; |
| | | /** |
| | | * 服务器状态 |
| | | * 0:离线 |
| | | * 1:在线 |
| | | */ |
| | | private String serverStatus; |
| | | |
| | | private List<ArdDroneInfo> ardDroneInfoList; |
| | | |
| | |
| | | package com.ard.work.mapper; |
| | | import com.ard.work.domain.entity.ArdServerInfo; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | * @param id 服务器管理主键 |
| | | * @return 结果 |
| | | */ |
| | | public int updateServerStatus(String id); |
| | | public int updateServerStatus(@Param("id") String id, @Param("status") String status); |
| | | |
| | | } |
| | |
| | | /** |
| | | * 批量新增无人机盒子 |
| | | * |
| | | * @param ids 无人机盒子主键集合 |
| | | * @param boxSn 盒子编码 |
| | | * @param deptId 部门ID |
| | | * @return 结果 |
| | | */ |
| | | public int insertArdDroneLs(String[] ids); |
| | | public int insertArdDroneLs(String[] boxSn,Long deptId); |
| | | |
| | | } |
| | |
| | | /** |
| | | * 修改服务器状态 |
| | | */ |
| | | public int updateServerStatus(String id); |
| | | public int updateServerStatus(String id,String status); |
| | | |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * 获取云盒列表 |
| | | * 获取全部服务器云盒列表 |
| | | * @return |
| | | */ |
| | | List<BoxInfoReceiver> getBoxList(); |
| | | |
| | | /** |
| | | * 获取指定服务器云盒列表 |
| | | * |
| | | * @return List<BoxInfoReceiver> |
| | | */ |
| | | List<BoxInfoReceiver> getBoxList(String serverId); |
| | | |
| | | /** |
| | | * 获取直播地址 |
| | | * @return |
| | | */ |
| | |
| | | package com.ard.work.service.impl; |
| | | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.ard.common.exception.ServiceException; |
| | | import com.ard.common.utils.DateUtils; |
| | | import com.ard.common.utils.uuid.IdUtils; |
| | | import com.ard.system.authorize.service.AuthorizeService; |
| | |
| | | import com.ard.work.service.IArdDroneInfoService; |
| | | import com.ard.work.service.ITyService; |
| | | import com.ard.work.util.UtilMethod; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | * @author ruoyi |
| | | * @date 2025-03-04 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class ArdDroneInfoServiceImpl implements IArdDroneInfoService { |
| | | |
| | |
| | | |
| | | @Resource |
| | | private ITyService tyService; |
| | | |
| | | @Resource |
| | | private AuthorizeService authorizeService; |
| | | |
| | | UtilMethod utilMethod = new UtilMethod(); |
| | | |
| | |
| | | /** |
| | | * 批量新增无人机盒子 |
| | | * |
| | | * @param ids 无人机盒子主键集合 |
| | | * @return 结果 |
| | | * @param boxSn 盒子编码数组 |
| | | * @param deptId 部门ID |
| | | * @return 成功插入的记录数 |
| | | */ |
| | | @Override |
| | | public int insertArdDroneLs(String[] ids) { |
| | | // 获取无人机盒子信息 |
| | | List<ArdDroneInfo> drone = new ArrayList<>(); |
| | | for (String id : ids) { |
| | | ArdDroneInfo ardDrone = ArdDroneInfoMapper.selectArdDroneById(id); |
| | | drone.add(ardDrone); |
| | | public int insertArdDroneLs(String[] boxSn, Long deptId) { |
| | | // 参数校验 |
| | | if (boxSn == null || boxSn.length == 0) { |
| | | log.warn("传入的盒子编码为空"); |
| | | return 1; |
| | | } |
| | | // 检验数量droneNum是否合规。 |
| | | utilMethod.authorizationVerification(drone.size()); |
| | | // 插入 |
| | | if (!drone.isEmpty()) { |
| | | return ArdDroneInfoMapper.insertArdDroneLs(drone); |
| | | if (deptId == null) { |
| | | throw new IllegalArgumentException("部门ID不能为空"); |
| | | } |
| | | return 0; |
| | | try { |
| | | // 1. 检查已存在的无人机盒子 |
| | | List<ArdDroneInfo> existingDrones = getExistingDrones(boxSn); |
| | | // 2. 授权数量验证 |
| | | validateAuthorization(existingDrones.size(), boxSn.length); |
| | | // 3. 获取并组装新的无人机信息 |
| | | List<ArdDroneInfo> newDrones = assembleNewDrones(boxSn, deptId); |
| | | // 4. 批量插入 |
| | | return batchInsertDrones(newDrones); |
| | | } catch (Exception e) { |
| | | log.error("批量新增无人机盒子失败, boxSn: {}, deptId: {}", Arrays.toString(boxSn), deptId, e); |
| | | throw new ServiceException("批量新增无人机盒子失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取已存在的无人机信息 |
| | | */ |
| | | private List<ArdDroneInfo> getExistingDrones(String[] boxSn) { |
| | | return Arrays.stream(boxSn) |
| | | .map(ArdDroneInfoMapper::selectArdDroneByBoxSn) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * 验证授权数量 |
| | | */ |
| | | private void validateAuthorization(int existingCount, int newCount) { |
| | | int totalCount = existingCount + newCount; |
| | | |
| | | JSONObject authInfo = authorizeService.getAuthorizationInfo(); |
| | | String authType = authInfo.getString("Type"); |
| | | |
| | | switch (authType) { |
| | | case "normal": |
| | | int allowedDroneNum = authInfo.getIntValue("droneNum"); |
| | | if (totalCount > allowedDroneNum) { |
| | | throw new RuntimeException(String.format( |
| | | "授权数量%d,当前操作将导致总数达到%d,超过授权限制", |
| | | allowedDroneNum, totalCount)); |
| | | } |
| | | break; |
| | | case "expired": |
| | | throw new RuntimeException("授权文件已过期"); |
| | | case "mismatch": |
| | | throw new RuntimeException("授权文件不匹配"); |
| | | default: |
| | | throw new RuntimeException("未知的授权类型: " + authType); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 组装新的无人机信息 |
| | | */ |
| | | private List<ArdDroneInfo> assembleNewDrones(String[] boxSn, Long deptId) { |
| | | List<BoxInfoReceiver> allBoxes = tyService.getBoxList(); |
| | | Set<String> targetBoxSnSet = new HashSet<>(Arrays.asList(boxSn)); |
| | | return allBoxes.stream() |
| | | .filter(box -> targetBoxSnSet.contains(box.getBoxSn())) |
| | | .map(box -> createDroneInfo(box, deptId)) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * 创建无人机信息对象 |
| | | */ |
| | | private ArdDroneInfo createDroneInfo(BoxInfoReceiver box, Long deptId) { |
| | | ArdDroneInfo drone = new ArdDroneInfo(); |
| | | drone.setId(IdUtils.simpleUUID()); |
| | | drone.setBoxSn(box.getBoxSn()); |
| | | drone.setBoxName(box.getBoxName()); |
| | | drone.setBoxAliasName(box.getBoxName()); |
| | | drone.setStartTime(new Date()); |
| | | drone.setOnLine(box.getOnLine()); |
| | | drone.setVideoServer(box.getVideoServer()); |
| | | drone.setVideoPort(box.getVideoPort()); |
| | | drone.setLastTaskId(box.getLastTaskId()); |
| | | drone.setDeptId(deptId); |
| | | |
| | | // 设置创建信息和更新信息 |
| | | setCreateAndUpdateInfo(drone); |
| | | |
| | | return drone; |
| | | } |
| | | |
| | | /** |
| | | * 设置创建和更新信息 |
| | | */ |
| | | private void setCreateAndUpdateInfo(ArdDroneInfo drone) { |
| | | Date currentTime = new Date(); |
| | | drone.setCreateTime(currentTime); |
| | | drone.setUpdateTime(currentTime); |
| | | } |
| | | |
| | | /** |
| | | * 批量插入无人机信息 |
| | | */ |
| | | private int batchInsertDrones(List<ArdDroneInfo> drones) { |
| | | if (drones.isEmpty()) { |
| | | return 0; |
| | | } |
| | | return ArdDroneInfoMapper.insertArdDroneLs(drones); |
| | | } |
| | | |
| | | /** |
| | |
| | | * 修改服务器状态 |
| | | */ |
| | | @Override |
| | | public int updateServerStatus(String id) { |
| | | return ardServerInfoMapper.updateServerStatus(id); |
| | | public int updateServerStatus(String id,String status) { |
| | | return ardServerInfoMapper.updateServerStatus(id,status); |
| | | } |
| | | |
| | | private String getEncryptStr(ArdServerInfo serverInfo, long timeStamp) { |
| | |
| | | package com.ard.work.service.impl; |
| | | |
| | | import com.ard.common.utils.ByteUtils; |
| | | import com.ard.work.aspect.annotation.CheckTakeoffAltitude; |
| | | import com.ard.work.aspect.annotation.CheckControlRight; |
| | | import com.ard.work.aspect.annotation.CheckTakeoffAltitude; |
| | | import com.ard.work.component.forest.TyClient; |
| | | import com.ard.work.component.netty.TcpClientUtil; |
| | | import com.ard.work.component.netty.command.BaseCmd; |
| | | import com.ard.work.component.netty.command.camera.*; |
| | | import com.ard.work.component.netty.command.manual.*; |
| | | import com.ard.work.component.netty.command.register.*; |
| | | import com.ard.work.component.netty.command.settings.*; |
| | | import com.ard.work.component.netty.command.speaker.*; |
| | | import com.ard.work.component.netty.command.wayline.*; |
| | | import com.ard.work.domain.dto.camera.*; |
| | | import com.ard.work.domain.dto.manual.*; |
| | | import com.ard.work.domain.dto.speaker.*; |
| | | import com.ard.work.domain.dto.wayline.*; |
| | | import com.ard.work.domain.entity.ArdDroneInfo; |
| | | import com.ard.work.domain.entity.ArdServerInfo; |
| | | import com.ard.work.domain.receiver.*; |
| | | import com.ard.work.component.forest.TyClient; |
| | | import com.ard.work.domain.cache.UavSignalInfoCache; |
| | | import com.ard.work.domain.cache.UavStateCache; |
| | | import com.ard.work.domain.cache.UavTelemetryDataCache; |
| | | import com.ard.work.domain.dto.BoxHistoryDTO; |
| | | import com.ard.work.domain.dto.BoxPhotosDTO; |
| | | import com.ard.work.domain.dto.BoxSettingsDTO; |
| | | import com.ard.work.domain.dto.control.AbsAngleControlDTO; |
| | | import com.ard.work.domain.dto.control.AngleControlDTO; |
| | | import com.ard.work.domain.dto.control.SpeedControlDTO; |
| | | import com.ard.work.domain.dto.converter.wayline.PlanLineDataConverter; |
| | | import com.ard.work.domain.dto.settings.ReturnHeightSettingDTO; |
| | | import com.ard.work.domain.dto.settings.ReturnPointSettingDTO; |
| | | import com.ard.work.domain.dto.settings.VideoStreamSettingDTO; |
| | | import com.ard.work.domain.dto.signal.SignalInfo; |
| | | import com.ard.work.domain.dto.state.UavState; |
| | | import com.ard.work.domain.dto.telemetry.TelemetryData; |
| | | import com.ard.work.component.netty.command.control.AbsAngleControlCmd; |
| | | import com.ard.work.component.netty.command.control.AngleControlCmd; |
| | | import com.ard.work.component.netty.command.control.SpeedControlCmd; |
| | | import com.ard.work.component.netty.command.manual.*; |
| | | import com.ard.work.component.netty.command.notice.BoxPowerStatusReplyCmd; |
| | | import com.ard.work.component.netty.command.notice.FlyWayEndReplyCmd; |
| | | import com.ard.work.component.netty.command.notice.UavReadyReplyCmd; |
| | | import com.ard.work.component.netty.command.real.SignalInfoCmd; |
| | | import com.ard.work.component.netty.command.real.UavStateCmd; |
| | | import com.ard.work.component.netty.command.real.UavTelemetryDataCmd; |
| | | import com.ard.work.component.netty.command.register.*; |
| | | import com.ard.work.component.netty.command.settings.*; |
| | | import com.ard.work.component.netty.command.speaker.*; |
| | | import com.ard.work.component.netty.command.wayline.*; |
| | | import com.ard.work.component.netty.handler.ErrorCodeEnum; |
| | | import com.ard.work.component.netty.protobuf.TelemetryDataPB; |
| | | import com.ard.work.domain.cache.UavSignalInfoCache; |
| | | import com.ard.work.domain.cache.UavStateCache; |
| | | import com.ard.work.domain.cache.UavTelemetryDataCache; |
| | | import com.ard.work.domain.dto.BoxHistoryDTO; |
| | | import com.ard.work.domain.dto.BoxPhotosDTO; |
| | | import com.ard.work.domain.dto.BoxSettingsDTO; |
| | | import com.ard.work.domain.dto.camera.*; |
| | | import com.ard.work.domain.dto.control.AbsAngleControlDTO; |
| | | import com.ard.work.domain.dto.control.AngleControlDTO; |
| | | import com.ard.work.domain.dto.control.SpeedControlDTO; |
| | | import com.ard.work.domain.dto.converter.wayline.PlanLineDataConverter; |
| | | import com.ard.work.domain.dto.manual.*; |
| | | import com.ard.work.domain.dto.settings.ReturnHeightSettingDTO; |
| | | import com.ard.work.domain.dto.settings.ReturnPointSettingDTO; |
| | | import com.ard.work.domain.dto.settings.VideoStreamSettingDTO; |
| | | import com.ard.work.domain.dto.signal.SignalInfo; |
| | | import com.ard.work.domain.dto.speaker.*; |
| | | import com.ard.work.domain.dto.state.UavState; |
| | | import com.ard.work.domain.dto.telemetry.TelemetryData; |
| | | import com.ard.work.domain.dto.wayline.*; |
| | | import com.ard.work.domain.entity.ArdDroneInfo; |
| | | import com.ard.work.domain.entity.ArdServerInfo; |
| | | import com.ard.work.domain.message.UavControlType; |
| | | import com.ard.work.domain.receiver.*; |
| | | import com.ard.work.service.IArdDroneInfoService; |
| | | import com.ard.work.service.IArdServerInfoService; |
| | | import com.ard.work.service.ITyService; |
| | | import com.ard.work.domain.message.UavControlType; |
| | | import io.netty.channel.Channel; |
| | | import io.netty.channel.ChannelFuture; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.net.InetSocketAddress; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | import java.util.stream.Stream; |
| | | |
| | | import static com.ard.work.component.netty.TyTcpClientManager.channels; |
| | | |
| | | /** |
| | | * TyServiceImpl |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取云盒列表 |
| | | * 获取全部云盒列表 |
| | | * |
| | | * @return List<BoxInfoReceiver> |
| | | */ |
| | |
| | | List<ArdServerInfo> ardServerInfos = ardServerService.selectArdServerInfoList(new ArdServerInfo()); |
| | | return getBoxList(ardServerInfos); |
| | | } |
| | | |
| | | /** |
| | | * 获取指定服务器云盒列表 |
| | | * |
| | | * @return List<BoxInfoReceiver> |
| | | */ |
| | | @Override |
| | | public List<BoxInfoReceiver> getBoxList(String serverId) { |
| | | TyHttpCommonReceiver<List<BoxInfoReceiver>> boxList = tyClient.getBoxList(serverId); |
| | | return boxList.getContent(); |
| | | } |
| | | /** |
| | | * 获取直播地址 |
| | | * |
| | |
| | | |
| | | private List<BoxInfoReceiver> getBoxList(List<ArdServerInfo> ls) { |
| | | return ls.stream() |
| | | .filter(ardServerInfo -> ardServerInfo.getServerStatus().equals("1")) |
| | | .flatMap(ardServerInfo -> { |
| | | try { |
| | | List<BoxInfoReceiver> boxes = tyClient.getBoxList(ardServerInfo.getId()).getContent(); |
| | |
| | | ErrorCodeEnum.getDescByCode(errorCode)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 发送消息 |
| | | */ |
| | | private void sendMessage(String key, BaseCmd msg) { |
| | | try { |
| | | ChannelFuture future = channels.get(key).getChannelFuture(); |
| | | Channel channel = future.channel(); |
| | | if (channel.isActive()) { |
| | | InetSocketAddress ipSocket = (InetSocketAddress) channel.remoteAddress(); |
| | | String host = ipSocket.getHostString(); |
| | | int port = ipSocket.getPort(); |
| | | channel.writeAndFlush(msg); // 发送编码后的数据 |
| | | log.debug("向六合服务器【{}:{}】发送数据,命令码:{}", host, port, String.format("0x%02X", msg.getCmdCode() & 0xFF)); |
| | | } else { |
| | | log.error("与六合服务器链接断开,无法发送数据!"); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("发送消息异常:{}", e.getMessage()); |
| | | } |
| | | TcpClientUtil.sendMessage(key, msg); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.ard.system.authorize.service.AuthorizeService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | |
| | | * @date: 2025/3/4 |
| | | * @description: |
| | | */ |
| | | @Service |
| | | public class UtilMethod { |
| | | |
| | | @Resource |
| | | @Autowired |
| | | private AuthorizeService authorizeService; |
| | | |
| | | /** |
| | | * 根据授权数量,验证授权信息是否合规 |
| | | */ |
| | | public void authorizationVerification(int ls){ |
| | | JSONObject data = authorizeService.getAuthorizationInfo(); |
| | | if ("normal".equals(data.get("Type"))){ |
| | | int droneNum = (int) data.get("droneNum"); |
| | | if (ls > droneNum){ |
| | | throw new RuntimeException("添加无人机数量超过当前授权数量!!!"); |
| | | } |
| | | }else if ("expired".equals(data.get("Type"))){ |
| | | throw new RuntimeException("授权文件过期!!!"); |
| | | }else if ("mismatch".equals(data.get("Type"))){ |
| | | throw new RuntimeException("授权文件不匹配!!!"); |
| | | |
| | | JSONObject authInfo = authorizeService.getAuthorizationInfo(); |
| | | String authType = authInfo.getString("Type"); |
| | | |
| | | switch (authType) { |
| | | case "normal": |
| | | int allowedDroneNum = authInfo.getIntValue("droneNum"); |
| | | if (ls > allowedDroneNum) { |
| | | throw new RuntimeException(String.format( |
| | | "授权数量%d,当前操作将导致总数达到%d,超过授权限制", |
| | | allowedDroneNum, ls)); |
| | | } |
| | | break; |
| | | case "expired": |
| | | throw new RuntimeException("授权文件已过期"); |
| | | case "mismatch": |
| | | throw new RuntimeException("授权文件不匹配"); |
| | | default: |
| | | throw new RuntimeException("未知的授权类型: " + authType); |
| | | } |
| | | } |
| | | |
| | |
| | | <result property="lastTaskId" column="last_task_id" /> |
| | | <result property="userId" column="user_id" /> |
| | | <result property="deptId" column="dept_id" /> |
| | | <result property="boxAliasName" column="box_alias_name" /> |
| | | <result property="createBy" column="create_by" /> |
| | | <result property="createTime" column="create_time" /> |
| | | <result property="updateBy" column="update_by" /> |
| | |
| | | |
| | | <sql id="selectArdDroneVo"> |
| | | select id, |
| | | box_sn, |
| | | box_name, |
| | | box_version, |
| | | start_time, |
| | | on_line, |
| | | video_server, |
| | | video_port, |
| | | last_task_id, |
| | | server_id, |
| | | user_id, |
| | | dept_id |
| | | box_sn, |
| | | box_name, |
| | | box_version, |
| | | start_time, |
| | | on_line, |
| | | video_server, |
| | | video_port, |
| | | last_task_id, |
| | | server_id, |
| | | user_id, |
| | | dept_id, |
| | | box_alias_name |
| | | from ard_drone_info |
| | | </sql> |
| | | |
| | | <select id="selectArdDroneList" parameterType="ArdDroneInfo" resultMap="ArdDroneInfoResult"> |
| | | <include refid="selectArdDroneVo"/> |
| | | <where> |
| | | <if test="boxSn != null and boxSn != ''"> and box_sn = #{boxSn}</if> |
| | | <where> |
| | | <if test="boxSn != null and boxSn != ''"> and box_sn like concat('%', #{boxSn}, '%')</if> |
| | | <if test="boxName != null and boxName != ''"> and box_name like concat('%', #{boxName}, '%')</if> |
| | | <if test="boxVersion != null and boxVersion != ''"> and box_version = #{boxVersion}</if> |
| | | <if test="onLine != null "> and onLine = #{onLine}</if> |
| | |
| | | <if test="videoServer != null">video_server,</if> |
| | | <if test="videoPort != null">video_port,</if> |
| | | <if test="lastTaskId != null">last_task_id,</if> |
| | | <if test="serverId != null">server_id,</if> |
| | | <if test="userId != null">user_id,</if> |
| | | <if test="deptId != null">dept_id,</if> |
| | | <if test="boxAliasName != null">box_alias_name,</if> |
| | | <if test="createBy != null">create_by,</if> |
| | | <if test="createTime != null">create_time,</if> |
| | | <if test="updateBy != null">update_by,</if> |
| | |
| | | <if test="videoServer != null">#{videoServer},</if> |
| | | <if test="videoPort != null">#{videoPort},</if> |
| | | <if test="lastTaskId != null">#{lastTaskId},</if> |
| | | <if test="serverId != null">#{serverId},</if> |
| | | <if test="userId != null">#{userId},</if> |
| | | <if test="deptId != null">#{deptId},</if> |
| | | <if test="boxAliasName != null">#{boxAliasName},</if> |
| | | <if test="createBy != null">#{createBy},</if> |
| | | <if test="createTime != null">#{createTime},</if> |
| | | <if test="updateBy != null">#{updateBy},</if> |
| | |
| | | <if test="videoServer != null">video_server = #{videoServer},</if> |
| | | <if test="videoPort != null">video_port = #{videoPort},</if> |
| | | <if test="lastTaskId != null">last_task_id = #{lastTaskId},</if> |
| | | <if test="serverId != null">server_id = #{serverId},</if> |
| | | <if test="userId != null">user_id = #{userId},</if> |
| | | <if test="deptId != null">dept_id = #{deptId},</if> |
| | | <if test="boxAliasName != null">box_alias_name = #{boxAliasName},</if> |
| | | <if test="createBy != null">create_by = #{createBy},</if> |
| | | <if test="createTime != null">create_time = #{createTime},</if> |
| | | <if test="updateBy != null">update_by = #{updateBy},</if> |
| | |
| | | </foreach> |
| | | </delete> |
| | | |
| | | <insert id="insertArdDroneLs"> |
| | | <insert id="insertArdDroneLs" parameterType="java.util.List"> |
| | | insert into ard_drone_info( |
| | | id, |
| | | box_sn, |
| | |
| | | server_id, |
| | | user_id, |
| | | dept_id, |
| | | box_alias_name, |
| | | create_by, |
| | | create_time, |
| | | update_by, |
| | |
| | | ) values |
| | | <foreach item="item" index="index" collection="list" separator=","> |
| | | (#{item.id}, |
| | | #{item.boxSn}), |
| | | #{item.boxName}), |
| | | #{item.boxVersion}), |
| | | #{item.startTime}), |
| | | #{item.videoServer}), |
| | | #{item.videoPort}), |
| | | #{item.lastTaskId}), |
| | | #{item.userId}), |
| | | #{item.deptId}), |
| | | #{item.createBy}), |
| | | #{item.createTime}), |
| | | #{item.updateBy}), |
| | | #{item.updateTime}), |
| | | #{item.boxSn}, |
| | | #{item.boxName}, |
| | | #{item.boxVersion}, |
| | | #{item.startTime}, |
| | | #{item.onLine}, |
| | | #{item.videoServer}, |
| | | #{item.videoPort}, |
| | | #{item.lastTaskId}, |
| | | #{item.serverId}, |
| | | #{item.userId}, |
| | | #{item.deptId}, |
| | | #{item.boxAliasName}, |
| | | #{item.createBy}, |
| | | #{item.createTime}, |
| | | #{item.updateBy}, |
| | | #{item.updateTime}, |
| | | #{item.remark}) |
| | | </foreach> |
| | | </insert> |
| | |
| | | <include refid="selectArdServerInfoVo"/> |
| | | <where> |
| | | <if test="id != null and id != ''"> and c.id = #{id}</if> |
| | | <if test="serverName != null and serverName != ''"> and server_name like c.concat('%', #{serverName}, '%')</if> |
| | | <if test="serverName != null and serverName != ''"> and c.server_name like concat('%', #{serverName}, '%')</if> |
| | | <if test="accessKeyId != null and accessKeyId != ''"> and c.access_key_id = #{accessKeyId}</if> |
| | | <if test="accessKeySecret != null and accessKeySecret != ''"> and c.access_key_secret = #{accessKeySecret}</if> |
| | | <if test="serverStatus != null and serverStatus != ''"> and c.server_status = #{serverStatus}</if> |
| | |
| | | where id = #{id} |
| | | </update> |
| | | |
| | | <update id="updateServerStatus" parameterType="String"> |
| | | <update id="updateServerStatus"> |
| | | update ard_server_info |
| | | <trim prefix="SET" suffixOverrides=","> |
| | | <if test="serverStatus != null">server_status = #{serverStatus},</if> |
| | | <if test="status != null">server_status = #{status},</if> |
| | | </trim> |
| | | where id = #{id} |
| | | </update> |
| | |
| | | <okhttp.version>4.8.1</okhttp.version> |
| | | <mqtt.version>5.5.5</mqtt.version> |
| | | <minio.version>8.3.7</minio.version> |
| | | <forest.version>1.8.0</forest.version> |
| | | <spring-boot-admin.version>2.5.1</spring-boot-admin.version> |
| | | </properties> |
| | | |
| | |
| | | <version>${minio.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- Forest --> |
| | | <dependency> |
| | | <groupId>com.dtflys.forest</groupId> |
| | | <artifactId>forest-spring-boot-starter</artifactId> |
| | | <version>${forest.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- SpringBootAdmin管控台 --> |
| | | <dependency> |
| | | <groupId>de.codecentric</groupId> |