package com.ruoyi.web.controller.robot; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.mqtt.service.MqttSendService; import com.ruoyi.mqtt.util.MqttTopicUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import java.util.*; import java.util.concurrent.TimeUnit; /** * 地图管理Controller * 提供地图列表、缩略图、路网数据、点云等API * 数据源:LD导航系统后端(http://192.168.0.102:8086) */ @Api(tags = "地图管理") @RestController @RequestMapping("/robot/map") public class RobotMapController { @Value("${ld-nav.url:http://192.168.0.102:8086}") private String ldNavUrl; @Autowired private MqttSendService mqttSendService; @Autowired private RedisCache redisCache; private final RestTemplate restTemplate = new RestTemplate(); // ==================== 导入导出API ==================== @ApiOperation("压缩导出地图") @PostMapping("/export/compress") public AjaxResult compressMapExport(@RequestBody Map params) { try { String url = ldNavUrl + "/v1/map/export/compress"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity> request = new HttpEntity<>(params, headers); ResponseEntity response = restTemplate.postForEntity(url, request, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("压缩成功", JSON.parse(response.getBody())); } return AjaxResult.error("压缩失败"); } catch (Exception e) { return AjaxResult.error("压缩异常: " + e.getMessage()); } } @ApiOperation("下载地图导出包") @GetMapping("/export") public ResponseEntity downloadMapExport( @ApiParam("地图名称") @RequestParam("map") String mapName ) { try { String url = ldNavUrl + "/v1/map/export?map=" + mapName; ResponseEntity response = restTemplate.getForEntity(url, byte[].class); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentDisposition(ContentDisposition.builder("attachment") .filename(mapName + ".zip") .build()); return new ResponseEntity<>(response.getBody(), headers, HttpStatus.OK); } catch (Exception e) { return ResponseEntity.notFound().build(); } } @ApiOperation("导入地图") @PostMapping("/import") public AjaxResult importMap( @ApiParam("地图文件") @RequestParam("file") org.springframework.web.multipart.MultipartFile file ) { try { String url = ldNavUrl + "/v1/map/import"; MultiValueMap body = new LinkedMultiValueMap<>(); body.add("filedata", file.getResource()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); HttpEntity> requestEntity = new HttpEntity<>(body, headers); ResponseEntity response = restTemplate.postForEntity(url, requestEntity, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("导入成功", JSON.parse(response.getBody())); } return AjaxResult.error("导入失败"); } catch (Exception e) { return AjaxResult.error("导入异常: " + e.getMessage()); } } // ==================== 路网数据API ==================== @ApiOperation("获取路网GeoJSON数据") @GetMapping("/roadmap/geojson") public AjaxResult getRoadMapGeoJson( @ApiParam("地图名称") @RequestParam("map") String mapName ) { try { String cacheKey = "ld:roadmap:" + mapName; Object cached = redisCache.getCacheObject(cacheKey); if (cached != null) { return AjaxResult.success("获取成功(缓存)", cached); } String url = ldNavUrl + "/v1/roadmap/geojson?map=" + mapName; ResponseEntity response = restTemplate.getForEntity(url, String.class); if (response.getStatusCode() == HttpStatus.OK) { JSONObject geojson = JSON.parseObject(response.getBody()); // 缓存10分钟 redisCache.setCacheObject(cacheKey, geojson, 10, TimeUnit.MINUTES); return AjaxResult.success("获取成功", geojson); } return AjaxResult.error("获取路网数据失败"); } catch (Exception e) { return AjaxResult.error("获取路网数据异常: " + e.getMessage()); } } @ApiOperation("保存路网GeoJSON数据") @PostMapping("/roadmap/geojson") public AjaxResult saveRoadMapGeoJson(@RequestBody JSONObject geoJsonData) { try { String mapName = geoJsonData.getString("map"); if (mapName == null) { return AjaxResult.error("缺少地图名称"); } String url = ldNavUrl + "/v1/roadmap/geojson"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity request = new HttpEntity<>(geoJsonData, headers); ResponseEntity response = restTemplate.postForEntity(url, request, String.class); if (response.getStatusCode() == HttpStatus.OK) { // 清除路网缓存 redisCache.deleteObject("ld:roadmap:" + mapName); return AjaxResult.success("保存成功"); } return AjaxResult.error("保存失败"); } catch (Exception e) { return AjaxResult.error("保存异常: " + e.getMessage()); } } // ==================== 任务路线API ==================== @ApiOperation("获取任务路线列表") @GetMapping("/path/list") public AjaxResult listPath( @ApiParam("地图名称") @RequestParam("map") String mapName ) { try { String url = ldNavUrl + "/v1/path/list?map=" + mapName; ResponseEntity response = restTemplate.getForEntity(url, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("获取成功", JSON.parse(response.getBody())); } return AjaxResult.error("获取路线列表失败"); } catch (Exception e) { return AjaxResult.error("获取路线列表异常: " + e.getMessage()); } } @ApiOperation("获取任务路线详情") @GetMapping("/path") public AjaxResult getPath( @ApiParam("地图名称") @RequestParam("map") String mapName, @ApiParam("路线名称") @RequestParam("path") String pathName ) { try { String url = ldNavUrl + "/v1/path?map=" + mapName + "&path=" + pathName; ResponseEntity response = restTemplate.getForEntity(url, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("获取成功", JSON.parse(response.getBody())); } return AjaxResult.error("获取路线详情失败"); } catch (Exception e) { return AjaxResult.error("获取路线详情异常: " + e.getMessage()); } } @ApiOperation("添加任务路线") @PostMapping("/path") public AjaxResult addPath(@RequestBody Map params) { try { String url = ldNavUrl + "/v1/path"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity> request = new HttpEntity<>(params, headers); ResponseEntity response = restTemplate.postForEntity(url, request, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("添加成功"); } return AjaxResult.error("添加失败"); } catch (Exception e) { return AjaxResult.error("添加异常: " + e.getMessage()); } } @ApiOperation("删除任务路线") @DeleteMapping("/path") public AjaxResult deletePath(@RequestBody Map params) { try { String map = params.get("map"); String path = params.get("path"); String url = ldNavUrl + "/v1/path?map=" + map + "&path=" + path; HttpHeaders headers = new HttpHeaders(); HttpEntity request = new HttpEntity<>(headers); ResponseEntity response = restTemplate.exchange(url, HttpMethod.DELETE, request, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("删除成功"); } return AjaxResult.error("删除失败"); } catch (Exception e) { return AjaxResult.error("删除异常: " + e.getMessage()); } } // ==================== 标定API ==================== @ApiOperation("获取标定历史") @GetMapping("/calibration/history") public AjaxResult getCalibrationHistory( @ApiParam("地图名称") @RequestParam("map") String mapName ) { try { String url = ldNavUrl + "/v1/map/file/" + mapName + "/shapefile/calibration.json"; ResponseEntity response = restTemplate.getForEntity(url, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("获取成功", JSON.parse(response.getBody())); } return AjaxResult.error("获取标定历史失败"); } catch (Exception e) { return AjaxResult.error("获取标定历史异常: " + e.getMessage()); } } // ==================== VSLAM API ==================== @ApiOperation("获取VSLAM统计信息") @GetMapping("/vslam/statistics") public AjaxResult getVSlamStatistics( @ApiParam("地图名称") @RequestParam("map") String mapName ) { try { String url = ldNavUrl + "/v1/vslam/statistics?map=" + mapName; ResponseEntity response = restTemplate.getForEntity(url, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("获取成功", JSON.parse(response.getBody())); } return AjaxResult.error("获取VSLAM统计信息失败"); } catch (Exception e) { return AjaxResult.error("获取VSLAM统计信息异常: " + e.getMessage()); } } @ApiOperation("获取关键帧点云") @GetMapping("/vslam/keyframe/cloud") public ResponseEntity getKeyframeCloud( @ApiParam("地图名称") @RequestParam("map") String mapName, @ApiParam("关键帧索引") @RequestParam("idx") int idx ) { try { String url = ldNavUrl + "/v1/vslam/keyframe/cloud?map=" + mapName + "&idx=" + idx; ResponseEntity response = restTemplate.getForEntity(url, byte[].class); return ResponseEntity.ok() .headers(headers -> headers.setContentType(MediaType.APPLICATION_OCTET_STREAM)) .body(response.getBody()); } catch (Exception e) { return ResponseEntity.notFound().build(); } } @ApiOperation("获取关键帧变换矩阵") @GetMapping("/vslam/keyframe/trans") public ResponseEntity getKeyframeTrans( @ApiParam("地图名称") @RequestParam("map") String mapName, @ApiParam("关键帧索引") @RequestParam("idx") int idx ) { try { String url = ldNavUrl + "/v1/vslam/keyframe/trans?map=" + mapName + "&idx=" + idx; ResponseEntity response = restTemplate.getForEntity(url, byte[].class); return ResponseEntity.ok() .headers(headers -> headers.setContentType(MediaType.APPLICATION_OCTET_STREAM)) .body(response.getBody()); } catch (Exception e) { return ResponseEntity.notFound().build(); } } @ApiOperation("获取闭环详情") @GetMapping("/vslam/closure/details") public AjaxResult getClosureDetails( @ApiParam("地图名称") @RequestParam("map") String mapName, @ApiParam("闭环索引") @RequestParam("idx") int idx ) { try { String url = ldNavUrl + "/v1/vslam/closure/details?map=" + mapName + "&idx=" + idx; ResponseEntity response = restTemplate.getForEntity(url, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("获取成功", JSON.parse(response.getBody())); } return AjaxResult.error("获取闭环详情失败"); } catch (Exception e) { return AjaxResult.error("获取闭环详情异常: " + e.getMessage()); } } // ==================== 传感器API ==================== @ApiOperation("获取点云数据") @GetMapping("/sensor/pointcloud") public ResponseEntity getPointcloud() { try { String url = ldNavUrl + "/v1/sensor/pointcloud/3d"; ResponseEntity response = restTemplate.getForEntity(url, byte[].class); return ResponseEntity.ok() .headers(headers -> headers.setContentType(MediaType.APPLICATION_OCTET_STREAM)) .body(response.getBody()); } catch (Exception e) { return ResponseEntity.notFound().build(); } } // ==================== 地图瓦片API ==================== @ApiOperation("获取地图配置信息") @GetMapping("/tilemap/details") public AjaxResult getTilemapDetails( @ApiParam("地图名称") @RequestParam("map") String mapName ) { try { String url = ldNavUrl + "/v1/tilemap/details?map=" + mapName; ResponseEntity response = restTemplate.getForEntity(url, String.class); if (response.getStatusCode() == HttpStatus.OK) { return AjaxResult.success("获取成功", JSON.parse(response.getBody())); } return AjaxResult.error("获取地图配置失败"); } catch (Exception e) { return AjaxResult.error("获取地图配置异常: " + e.getMessage()); } } // ==================== 设备状态API ==================== @ApiOperation("获取机器人实时位姿(从Redis缓存)") @GetMapping("/robot/pose") public AjaxResult getRobotPose( @ApiParam("设备ID") @RequestParam(required = false) String deviceId ) { try { String cacheKey = "ld:pose:" + (deviceId != null ? deviceId : "default"); Object cached = redisCache.getCacheObject(cacheKey); if (cached != null) { return AjaxResult.success("获取成功(实时)", cached); } return AjaxResult.error("未获取到位姿数据"); } catch (Exception e) { return AjaxResult.error("获取位姿异常: " + e.getMessage()); } } @ApiOperation("获取任务实时信息(从Redis缓存)") @GetMapping("/robot/task") public AjaxResult getTaskRealtimeInfo( @ApiParam("设备ID") @RequestParam(required = false) String deviceId ) { try { String cacheKey = "ld:task:realtime:" + (deviceId != null ? deviceId : "default"); Object cached = redisCache.getCacheObject(cacheKey); if (cached != null) { return AjaxResult.success("获取成功(实时)", cached); } return AjaxResult.error("未获取到任务数据"); } catch (Exception e) { return AjaxResult.error("获取任务信息异常: " + e.getMessage()); } } }