add:解析地图相关数据
This commit is contained in:
55
src/main/java/org/nl/apt15e/apt/map/dao/MapInfo.java
Normal file
55
src/main/java/org/nl/apt15e/apt/map/dao/MapInfo.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package org.nl.apt15e.apt.map.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/17
|
||||
*/
|
||||
@Data
|
||||
@TableName("map_info")
|
||||
public class MapInfo {
|
||||
|
||||
/**
|
||||
* 地图编号
|
||||
*/
|
||||
@TableId
|
||||
private String mapCode;
|
||||
|
||||
/**
|
||||
* 地图名称
|
||||
*/
|
||||
private String mapName;
|
||||
|
||||
/**
|
||||
* 图片像素宽
|
||||
*/
|
||||
private Double width;
|
||||
|
||||
/**
|
||||
* 图片像素高
|
||||
*/
|
||||
private Double height;
|
||||
|
||||
/**
|
||||
* 点云图像素比例 如:一个像素0.05 就是5cm
|
||||
*/
|
||||
private Double resolution;
|
||||
|
||||
/**
|
||||
* 左下角x坐标
|
||||
*/
|
||||
private Double x;
|
||||
|
||||
/**
|
||||
* 左下角y坐标
|
||||
*/
|
||||
private Double y;
|
||||
|
||||
/**
|
||||
* 左下角 角度
|
||||
*/
|
||||
private Double angle;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.nl.apt15e.apt.map.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/17
|
||||
*/
|
||||
@Data
|
||||
public class ProcessMapYamlDto {
|
||||
|
||||
/**
|
||||
* 点云图名称
|
||||
*/
|
||||
private String image;
|
||||
|
||||
/**
|
||||
* 点云图像素比例 如:一个像素0.05 就是5cm
|
||||
*/
|
||||
private Double resolution;
|
||||
|
||||
/**
|
||||
* 地图左下角像素的坐标 [x,y,angle]
|
||||
*/
|
||||
private Double[] origin;
|
||||
|
||||
private Double negate;
|
||||
private Double occupied_thresh;
|
||||
private Double free_thresh;
|
||||
private Double main_normal;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.nl.apt15e.apt.map.rest;
|
||||
|
||||
import org.nl.apt15e.apt.map.dao.MapInfo;
|
||||
import org.nl.apt15e.apt.map.service.MapInfoService;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/17
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/mapInfo")
|
||||
public class MapInfoController {
|
||||
|
||||
@Resource
|
||||
private MapInfoService mapInfoService;
|
||||
|
||||
@RequestMapping("/getMapInfoByCode")
|
||||
public ResponseEntity<Object> getMapInfoByCode(@RequestParam("mapCode") String mapCode) {
|
||||
return new ResponseEntity<>(mapInfoService.getMapInfoByCode(mapCode), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.nl.apt15e.apt.map.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.nl.apt15e.apt.map.dao.MapInfo;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/17
|
||||
*/
|
||||
public interface MapInfoService extends IService<MapInfo> {
|
||||
|
||||
JSONObject getMapInfoByCode(String mapCode);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.nl.apt15e.apt.map.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nl.apt15e.apt.map.dao.MapInfo;
|
||||
import org.nl.apt15e.apt.map.service.MapInfoService;
|
||||
import org.nl.apt15e.apt.map.service.mapper.MapInfoMapper;
|
||||
import org.nl.apt15e.common.BadRequestException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/17
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MapInfoServiceImpl extends ServiceImpl<MapInfoMapper, MapInfo> implements MapInfoService {
|
||||
|
||||
@Override
|
||||
public JSONObject getMapInfoByCode(String mapCode) {
|
||||
if (StrUtil.isEmpty(mapCode)) {
|
||||
throw new BadRequestException("mapCode is null");
|
||||
}
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", HttpStatus.OK.value());
|
||||
jsonObject.put("data", this.getOne(new LambdaQueryWrapper<>(MapInfo.class).eq(MapInfo::getMapCode,mapCode)));
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.nl.apt15e.apt.map.service.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.nl.apt15e.apt.map.dao.MapInfo;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/17
|
||||
*/
|
||||
@Mapper
|
||||
public interface MapInfoMapper extends BaseMapper<MapInfo> {
|
||||
}
|
||||
61
src/main/java/org/nl/apt15e/apt/route/dao/RouteInfo.java
Normal file
61
src/main/java/org/nl/apt15e/apt/route/dao/RouteInfo.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package org.nl.apt15e.apt.route.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/17
|
||||
*/
|
||||
@Data
|
||||
@TableName("route_info")
|
||||
public class RouteInfo {
|
||||
|
||||
/**
|
||||
* 路径标识
|
||||
*/
|
||||
@TableId
|
||||
private Integer route_id;
|
||||
|
||||
/**
|
||||
* 起点ID
|
||||
*/
|
||||
private Integer start_id;
|
||||
|
||||
/**
|
||||
* 终点ID
|
||||
*/
|
||||
private Integer end_id;
|
||||
|
||||
/**
|
||||
* 起点x坐标
|
||||
*/
|
||||
private Double start_x;
|
||||
|
||||
/**
|
||||
* 起点y坐标
|
||||
*/
|
||||
private Double start_y;
|
||||
|
||||
/**
|
||||
* 终点x坐标
|
||||
*/
|
||||
private Double end_x;
|
||||
|
||||
/**
|
||||
* 终点y坐标
|
||||
*/
|
||||
private Double end_y;
|
||||
|
||||
/**
|
||||
* 导航模式 前进模式, 后退模式 ,无头模式
|
||||
*/
|
||||
private Integer navigation_mode;
|
||||
|
||||
/**
|
||||
* 路线类型 直行,转弯
|
||||
*/
|
||||
private Integer route_type;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.nl.apt15e.apt.route.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.nl.apt15e.apt.route.dao.RouteInfo;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/18
|
||||
*/
|
||||
public interface RouteInfoService extends IService<RouteInfo> {
|
||||
|
||||
JSONObject getAllRouteInfo();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.nl.apt15e.apt.route.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.nl.apt15e.apt.route.dao.RouteInfo;
|
||||
import org.nl.apt15e.apt.route.service.RouteInfoService;
|
||||
import org.nl.apt15e.apt.route.service.mapper.RouteInfoMapper;
|
||||
import org.nl.apt15e.apt.station.StationTypeEnum;
|
||||
import org.nl.apt15e.apt.station.dao.Station;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/18
|
||||
*/
|
||||
@Service
|
||||
public class RouteInfoServiceImpl extends ServiceImpl<RouteInfoMapper, RouteInfo> implements RouteInfoService {
|
||||
|
||||
@Resource
|
||||
private RouteInfoMapper routeInfoMapper;
|
||||
|
||||
@Override
|
||||
public JSONObject getAllRouteInfo() {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", HttpStatus.OK.value());
|
||||
jsonObject.put("data", routeInfoMapper.selectList(null));
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.nl.apt15e.apt.route.service.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.nl.apt15e.apt.route.dao.RouteInfo;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/18
|
||||
*/
|
||||
@Mapper
|
||||
public interface RouteInfoMapper extends BaseMapper<RouteInfo> {
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.nl.apt15e.apt.route.service.rest;
|
||||
|
||||
import org.nl.apt15e.apt.route.service.RouteInfoService;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/18
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/routeInfo")
|
||||
public class RouteInfoController {
|
||||
|
||||
@Resource
|
||||
private RouteInfoService routeInfoService;
|
||||
|
||||
@RequestMapping("/getRouteInfo")
|
||||
public ResponseEntity<Object> getRouteInfo(){
|
||||
return new ResponseEntity<>(routeInfoService.getAllRouteInfo(), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,12 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public enum StationTypeEnum {
|
||||
|
||||
//站点类型 Station工位点 Charge充电点 Breaks休息点 Docking对接点
|
||||
//站点类型 Station工位点 Charge充电点 Breaks休息点 Docking对接点 System系统自动生成点
|
||||
STATION(1, "STATION", "工位点"),
|
||||
CHARGE(2, "CHARGE", "充电点"),
|
||||
BREAKS(3, "BREAKS", "休息点"),
|
||||
DOCKING(4, "DOCKING", "对接点"),
|
||||
SYSTEM(5, "SYSTEM", "系统自动生成点"),
|
||||
|
||||
|
||||
//动作类型 Ascend升叉 Descend降叉 Move移动 Customize自定义
|
||||
|
||||
@@ -16,10 +16,12 @@ import java.io.Serializable;
|
||||
public class Station implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId
|
||||
private Integer station_id;
|
||||
|
||||
/**
|
||||
* 站点编码 对应地图上站点
|
||||
*/
|
||||
@TableId
|
||||
private String station_code;
|
||||
|
||||
/**
|
||||
@@ -28,7 +30,7 @@ public class Station implements Serializable {
|
||||
private String station_name;
|
||||
|
||||
/**
|
||||
* 站点类型 (Station工位点 Charge充电点 Breaks休息点 Docking对接点)
|
||||
* 站点类型 (Station工位点 Charge充电点 Breaks休息点 Docking对接点 System系统自动生成点)
|
||||
*/
|
||||
private String station_type;
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.nl.apt15e.apt.station.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.nl.apt15e.apt.station.StationTypeEnum;
|
||||
import org.nl.apt15e.apt.station.dao.Station;
|
||||
import org.nl.apt15e.apt.station.service.StationService;
|
||||
import org.nl.apt15e.apt.station.service.mapper.StationMapper;
|
||||
@@ -25,6 +27,7 @@ public class StationServiceImpl extends ServiceImpl<StationMapper, Station> impl
|
||||
|
||||
@Override
|
||||
public List<Station> queryAllStation() {
|
||||
return stationMapper.selectList(null);
|
||||
return stationMapper.selectList(stationMapper.selectList(new LambdaQueryWrapper<>(Station.class)
|
||||
.eq(Station::getStation_type, StationTypeEnum.STATION.getCode())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.nl.apt15e.apt.teaching.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/14
|
||||
*/
|
||||
@Data
|
||||
public class TeachingRequest {
|
||||
|
||||
/**
|
||||
* 地图名称
|
||||
*/
|
||||
private String mapName;
|
||||
|
||||
/**
|
||||
* 站点名称
|
||||
*/
|
||||
private String stationName;
|
||||
|
||||
/**
|
||||
* x坐标
|
||||
*/
|
||||
private Double x;
|
||||
|
||||
/**
|
||||
* y坐标
|
||||
*/
|
||||
private Double y;
|
||||
|
||||
/**
|
||||
* 角度
|
||||
*/
|
||||
private Double z;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.nl.apt15e.apt.teaching.rest;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.nl.apt15e.apt.teaching.service.TeachingService;
|
||||
@@ -51,8 +52,10 @@ public class TeachingController {
|
||||
}
|
||||
|
||||
@PostMapping("/getLocalMaps")
|
||||
private ResponseEntity<Object> getLocalMaps() {
|
||||
return new ResponseEntity<>(teachingService.getLocalMaps(), HttpStatus.OK);
|
||||
// @Log("获取地图列表")
|
||||
private JSONObject getLocalMaps() {
|
||||
// return new ResponseEntity<>(teachingService.getLocalMaps(), HttpStatus.OK);
|
||||
return teachingService.getLocalMaps();
|
||||
}
|
||||
|
||||
@PostMapping("/getRunMapZip")
|
||||
@@ -74,4 +77,9 @@ public class TeachingController {
|
||||
private ResponseEntity<Object> relocate(@RequestParam("x") Double x, @RequestParam("y") Double y, @RequestParam("angle") Double angle) {
|
||||
return new ResponseEntity<>(teachingService.relocate(x, y, angle), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/oneClickDeployment")
|
||||
private ResponseEntity<Object> oneClickDeployment(@RequestParam("mapName") String mapName) {
|
||||
return new ResponseEntity<>(teachingService.oneClickDeployment(mapName), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.nl.apt15e.apt.teaching.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -37,7 +39,7 @@ public interface TeachingService {
|
||||
/**
|
||||
* 获取后台地图列表
|
||||
*/
|
||||
Map<String, Object> getLocalMaps();
|
||||
JSONObject getLocalMaps();
|
||||
|
||||
/**
|
||||
* 部署地图
|
||||
@@ -78,4 +80,10 @@ public interface TeachingService {
|
||||
* 重启车辆后台程序
|
||||
*/
|
||||
Map<String, Object> restart();
|
||||
|
||||
/**
|
||||
* 一键部署地图
|
||||
* @return
|
||||
*/
|
||||
Map<String, Object> oneClickDeployment(String mapName);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,16 @@ package org.nl.apt15e.apt.teaching.service.impl;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nl.apt15e.apt.station.dao.Station;
|
||||
import org.nl.apt15e.apt.station.service.StationService;
|
||||
import org.nl.apt15e.apt.teaching.service.TeachingService;
|
||||
import org.nl.apt15e.apt.vehicle.ProcessZip;
|
||||
import org.nl.apt15e.apt.vehicle.service.impl.VehicleInfoServiceImpl;
|
||||
@@ -16,7 +21,9 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
@@ -29,9 +36,12 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
@Resource
|
||||
private ProcessZip processZip;
|
||||
|
||||
@Resource
|
||||
private StationService stationService;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> startMapping(String mapName) {
|
||||
if ("".equals(mapName)){
|
||||
if (StrUtil.isBlank(mapName)){
|
||||
throw new BadRequestException("mapName is empty");
|
||||
}
|
||||
JSONObject params = new JSONObject();
|
||||
@@ -50,8 +60,10 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
log.info("开始建图:{}",body);
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body =(JSONObject) this.startManual();
|
||||
body.put("message","开始建图成功");
|
||||
return body;
|
||||
}
|
||||
return body;
|
||||
|
||||
}
|
||||
log.info("开始建图失败");
|
||||
throw new BadRequestException("开始建图失败");
|
||||
@@ -67,11 +79,14 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("切手动失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("切手动:{}",body);
|
||||
return body;
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body.put("message","切自动成功");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.info("切手动失败");
|
||||
throw new BadRequestException("切手动失败");
|
||||
@@ -87,10 +102,13 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("切自动失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("切自动:{}",body);
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body.put("message","切自动成功");
|
||||
}
|
||||
return body;
|
||||
}
|
||||
log.info("切自动失败");
|
||||
@@ -107,14 +125,15 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("结束建图失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("结束建图:{}",body);
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body =(JSONObject) this.stopManual();
|
||||
body.put("message","结束建图成功");
|
||||
return body;
|
||||
}
|
||||
return body;
|
||||
}
|
||||
log.info("结束建图失败");
|
||||
throw new BadRequestException("结束建图失败");
|
||||
@@ -122,7 +141,7 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> setStation(String stationName) {
|
||||
if ("".equals(stationName)){
|
||||
if (StrUtil.isBlank(stationName)){
|
||||
throw new BadRequestException("spotCode is empty");
|
||||
}
|
||||
JSONObject params = new JSONObject();
|
||||
@@ -135,18 +154,21 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("设置站点失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("设置站点:{}",body);
|
||||
return body;
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body.put("message","设置站点成功");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.info("设置站点失败");
|
||||
throw new BadRequestException("设置站点失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getLocalMaps() {
|
||||
public JSONObject getLocalMaps() {
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
response = HTTPUtil.get("http://192.168.100.82:9998","/tool/editor/getLocalMaps", new JSONObject());
|
||||
@@ -155,11 +177,14 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("获取地图列表失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("获取地图列表:{}",body);
|
||||
return body;
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body.put("message","获取地图列表成功");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.info("获取地图列表失败");
|
||||
throw new BadRequestException("获取地图列表失败");
|
||||
@@ -167,7 +192,7 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> deployRunMap(String mapName) {
|
||||
if ("".equals(mapName)){
|
||||
if (StrUtil.isBlank(mapName)){
|
||||
throw new BadRequestException("mapName is empty");
|
||||
}
|
||||
JSONObject params = new JSONObject();
|
||||
@@ -180,11 +205,14 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("部署地图失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("部署地图:{}",body);
|
||||
return body;
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body.put("message","部署地图成功");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.info("部署地图失败");
|
||||
throw new BadRequestException("部署地图失败");
|
||||
@@ -192,7 +220,7 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> changeCurrentRunMap(String mapName) {
|
||||
if ("".equals(mapName)){
|
||||
if (StrUtil.isBlank(mapName)){
|
||||
throw new BadRequestException("mapName is empty");
|
||||
}
|
||||
JSONObject params = new JSONObject();
|
||||
@@ -205,11 +233,14 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("应用地图失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("应用地图:{}",body);
|
||||
return body;
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body.put("message","应用地图成功");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.info("应用地图失败");
|
||||
throw new BadRequestException("应用地图失败");
|
||||
@@ -217,7 +248,7 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
|
||||
@Override
|
||||
public byte[] getRunMapZip(String mapName) {
|
||||
if ("".equals(mapName)){
|
||||
if (StrUtil.isBlank(mapName)){
|
||||
throw new BadRequestException("mapName is empty");
|
||||
}
|
||||
JSONObject params = new JSONObject();
|
||||
@@ -230,12 +261,11 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("获取地图包失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取zip包
|
||||
byte[] zipBytes = response.bodyBytes();
|
||||
log.info("解析地图包数据");
|
||||
processZip.processZipResponse(zipBytes);
|
||||
|
||||
log.info("获取地图包");
|
||||
return zipBytes;
|
||||
}
|
||||
log.info("获取地图包失败");
|
||||
@@ -254,7 +284,7 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
.header("token", "admin123")
|
||||
.header("name", "lx-script")
|
||||
.header("Content-Type", "multipart/form-data;charset=UTF-8")
|
||||
.form("areaId", VehicleInfoServiceImpl.vehicleInfo.getAreaId())
|
||||
.form("areaId", 1)
|
||||
.form("file",zipFile,mapName+".zip")
|
||||
.execute();
|
||||
} catch (Exception e) {
|
||||
@@ -262,11 +292,14 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("同步地图失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("同步地图:{}",body);
|
||||
return body;
|
||||
if (body.getBoolean("state")){
|
||||
body.put("message","同步地图成功");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.info("同步地图失败");
|
||||
throw new BadRequestException("同步地图失败");
|
||||
@@ -277,6 +310,9 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
if (ObjectUtil.isEmpty(x) || ObjectUtil.isEmpty(y) || ObjectUtil.isEmpty(angle)){
|
||||
throw new BadRequestException("params is empty");
|
||||
}
|
||||
log.info(angle.toString());
|
||||
angle = (angle * 180) / Math.PI;
|
||||
log.info(angle.toString());
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("x", x);
|
||||
params.put("y", y);
|
||||
@@ -284,22 +320,26 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
params.put("noisyX", 0.5);
|
||||
params.put("noisyY", 0.5);
|
||||
params.put("noisyAngle", Math.PI);
|
||||
log.info(params.getString("angle"));
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/relocate", params);
|
||||
} catch (Exception e) {
|
||||
log.info("访问车体重定位接口报错:{}",e.getMessage());
|
||||
throw new BadRequestException("重定位失败");
|
||||
throw new BadRequestException("重定位指令下发失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("重定位:{}",body);
|
||||
return body;
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body.put("message","重定位指令下发成功,正在重新定位中。");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.info("重定位失败");
|
||||
throw new BadRequestException("重定位失败");
|
||||
log.info("重定位指令下发失败");
|
||||
throw new BadRequestException("重定位指令下发失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -312,13 +352,61 @@ public class TeachingServiceImpl implements TeachingService {
|
||||
throw new BadRequestException("车体程序重启失败");
|
||||
}
|
||||
// 检查响应状态码
|
||||
if (response.isOk()) {
|
||||
if (response.isOk() && response.body() != null) {
|
||||
// 获取响应体内容
|
||||
JSONObject body = JSON.parseObject(response.body());
|
||||
log.info("车体程序重启:{}",body);
|
||||
return body;
|
||||
if ("200".equals(body.getString("code"))){
|
||||
body.put("message","车体程序重启成功");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.info("车体程序重启失败");
|
||||
throw new BadRequestException("车体程序重启失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> oneClickDeployment(String mapName) {
|
||||
if (StrUtil.isBlank(mapName)){
|
||||
throw new BadRequestException("mapName is empty");
|
||||
}
|
||||
JSONObject response = new JSONObject();
|
||||
// 部署地图
|
||||
this.deployRunMap(mapName);
|
||||
// 应用地图
|
||||
this.changeCurrentRunMap(mapName);
|
||||
// 解析地图包并同步到调度
|
||||
this.synchronizeMap(mapName);
|
||||
// 重启本体程序
|
||||
this.restart();
|
||||
|
||||
// 重定位,需要等待一会
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Station station = stationService.getOne(new LambdaQueryWrapper<>(Station.class)
|
||||
.eq(Station::getStation_code,"C")
|
||||
);
|
||||
this.relocate(station.getX(),station.getY(),station.getAngle());
|
||||
response.put("code", 200);
|
||||
response.put("message", "部署成功");
|
||||
// try {
|
||||
// Thread.sleep(10000);
|
||||
// } catch (InterruptedException e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// Station station = stationService.getOne(new LambdaQueryWrapper<>(Station.class)
|
||||
// .eq(Station::getStation_code,"C")
|
||||
// );
|
||||
// log.info("station:{}",station);
|
||||
// response = (JSONObject) this.relocate(station.getX(), station.getY(), station.getAngle());
|
||||
// if (response.getIntValue("code") != 200){
|
||||
// response.put("code", response.getIntValue("code"));
|
||||
// response.put("message","重定位失败");
|
||||
// return response;
|
||||
// }
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
package org.nl.apt15e.apt.vehicle;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nl.apt15e.apt.map.dao.MapInfo;
|
||||
import org.nl.apt15e.apt.map.dto.ProcessMapYamlDto;
|
||||
import org.nl.apt15e.apt.map.service.MapInfoService;
|
||||
import org.nl.apt15e.apt.route.dao.RouteInfo;
|
||||
import org.nl.apt15e.apt.route.service.RouteInfoService;
|
||||
import org.nl.apt15e.apt.station.StationTypeEnum;
|
||||
import org.nl.apt15e.apt.station.dao.Station;
|
||||
import org.nl.apt15e.apt.station.service.StationService;
|
||||
import org.nl.apt15e.apt.station.service.impl.StationServiceImpl;
|
||||
import org.nl.apt15e.common.BadRequestException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
@@ -22,31 +35,46 @@ import java.util.zip.ZipInputStream;
|
||||
* @author dsh
|
||||
* 2025/7/7
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ProcessZip {
|
||||
|
||||
@Resource
|
||||
private StationServiceImpl stationService;
|
||||
private StationService stationService;
|
||||
|
||||
@Resource
|
||||
private MapInfoService mapInfoService;
|
||||
|
||||
@Resource
|
||||
private RouteInfoService routeInfoService;
|
||||
|
||||
|
||||
private static final String PGM_MAGIC_NUMBER = "P5";
|
||||
private static final String OUTPUT_DIR = "/logs";
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void processZipResponse(byte[] zipBytes) {
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(zipBytes);
|
||||
ZipInputStream zis = new ZipInputStream(bais, StandardCharsets.UTF_8)) {
|
||||
|
||||
MapInfo mapInfo = new MapInfo();
|
||||
|
||||
ZipEntry entry;
|
||||
// 遍历ZIP文件中的每个条目
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
String entryName = entry.getName();
|
||||
|
||||
// 3. 筛选.lxmap文件
|
||||
// 处理.lxmap文件,获取点位信息和路径信息
|
||||
if (entryName.toLowerCase().endsWith(".lxmap")) {
|
||||
System.out.println("解析文件: " + entryName);
|
||||
log.info("解析文件: {}", entryName);
|
||||
|
||||
// 4. 读取文件内容(使用BufferedReader按行处理)
|
||||
// 读取文件内容(使用BufferedReader按行处理)
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(zis, StandardCharsets.UTF_8));
|
||||
|
||||
//存储站点之前先清除站点表中数据
|
||||
//存储站点之前先清除站点表中数据和路径表中的数据
|
||||
stationService.remove(new LambdaQueryWrapper<>());
|
||||
routeInfoService.remove(new LambdaQueryWrapper<>());
|
||||
|
||||
String line;
|
||||
int lineNumber = 0;
|
||||
@@ -55,11 +83,93 @@ public class ProcessZip {
|
||||
// 5. 处理每一行数据
|
||||
processLine(line, lineNumber);
|
||||
}
|
||||
if (lineNumber == 0) {
|
||||
throw new BadRequestException("地图包数据为空,扫描失败。请重新建图");
|
||||
}
|
||||
}
|
||||
// 点云图 .pgm 文件转换 .png
|
||||
if (entryName.toLowerCase().endsWith(".pgm")) {
|
||||
log.info("处理PGM文件: {}",entryName);
|
||||
|
||||
// 读取整个PGM文件到字节数组
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = zis.read(buffer)) > 0) {
|
||||
baos.write(buffer, 0, len);
|
||||
}
|
||||
byte[] pgmData = baos.toByteArray();
|
||||
|
||||
// 转换PGM为PNG
|
||||
BufferedImage pngImage = convertPgmToPng(pgmData);
|
||||
// 记录地图图片像素高、宽
|
||||
mapInfo.setWidth((double) pngImage.getWidth());
|
||||
mapInfo.setHeight((double) pngImage.getHeight());
|
||||
if (ObjectUtil.isEmpty(pngImage)) {
|
||||
log.info("PGM转换失败: {}", entryName);
|
||||
throw new BadRequestException("地图PGM转换失败: ");
|
||||
}
|
||||
|
||||
// 保存PNG文件
|
||||
String pngFileName = entryName.substring(0, entryName.lastIndexOf('.')) + ".png";
|
||||
// 记录地图编码、名称
|
||||
mapInfo.setMapCode(pngFileName);
|
||||
mapInfo.setMapName(pngFileName);
|
||||
savePngImage(pngImage, pngFileName);
|
||||
}
|
||||
}
|
||||
// 解析.yaml点云图信息(像素比例,点云图左下角像素坐标信息)
|
||||
if (entryName.toLowerCase().endsWith(".yaml")) {
|
||||
log.info("处理.yaml文件地图信息: {}",entryName);
|
||||
|
||||
try{
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(zis, StandardCharsets.UTF_8));
|
||||
// 检查并处理YAML头部
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
String line = reader.readLine();
|
||||
// 读取内容标识
|
||||
boolean flag = false;
|
||||
// 跳过以%开头的指令行
|
||||
if (line != null && line.startsWith("%")) {
|
||||
log.info("跳过YAML指令: {}" , line);
|
||||
// 读取下一行
|
||||
line = reader.readLine();
|
||||
}
|
||||
|
||||
// 读取剩余内容
|
||||
while (line != null) {
|
||||
flag = true;
|
||||
contentBuilder.append(line).append("\n");
|
||||
line = reader.readLine();
|
||||
}
|
||||
if (!flag){
|
||||
throw new BadRequestException(".YAML文件内容为空");
|
||||
}
|
||||
// 解析YAML内容
|
||||
String yamlContent = contentBuilder.toString();
|
||||
ProcessMapYamlDto processMapYamlDto = new Yaml().loadAs(yamlContent, ProcessMapYamlDto.class);
|
||||
log.info(".yaml数据: {}", processMapYamlDto);
|
||||
if (ObjectUtil.isEmpty(processMapYamlDto) || processMapYamlDto.getOrigin().length<3){
|
||||
throw new BadRequestException(".yaml数据信息格式错误");
|
||||
}
|
||||
// 记录像素比例和图片左下角像素坐标
|
||||
mapInfo.setResolution(processMapYamlDto.getResolution());
|
||||
mapInfo.setX(processMapYamlDto.getOrigin()[0]);
|
||||
mapInfo.setY(processMapYamlDto.getOrigin()[1]);
|
||||
mapInfo.setAngle(processMapYamlDto.getOrigin()[2]);
|
||||
} catch (IOException e) {
|
||||
throw new BadRequestException("YAML解析失败:"+ e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 保存地图信息
|
||||
if (ObjectUtil.isNotEmpty(mapInfo)) {
|
||||
log.info("地图信息:{}",mapInfo);
|
||||
mapInfoService.save(mapInfo);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("处理ZIP文件失败", e);
|
||||
throw new BadRequestException("处理地图包数据失败:"+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +179,7 @@ public class ProcessZip {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否以"Cairn: Goal"开头
|
||||
// 检查是否以"Cairn"开头
|
||||
if (line.startsWith("Cairn:")) {
|
||||
// 截取开头后面的数据(6个字符长度)
|
||||
String dataPart = line.substring(6).trim();
|
||||
@@ -78,20 +188,44 @@ public class ProcessZip {
|
||||
// 按空格分割字段(多个连续空格视为一个分隔符)
|
||||
String[] processData = dataPart.split("\\s+");
|
||||
|
||||
//判断数据是否是点位数据 && 数据长度正常 && 点位类型是1工位点(站点)
|
||||
//满足以上条件 目前只记录站点
|
||||
if ("Goal".equals(processData[0]) && processData.length > 9 && "1".equals(processData[8])) {
|
||||
System.out.printf("行号 %d - 有效数据: %s%n", lineNumber, Arrays.toString(processData));
|
||||
//判断数据是否是点位数据 && 数据长度正常
|
||||
// Goal点位标识
|
||||
if ("Goal".equals(processData[0]) && processData.length > 9) {
|
||||
System.out.printf("行号 %d - 有效点位数据: %s%n", lineNumber, Arrays.toString(processData));
|
||||
Station station = new Station();
|
||||
station.setStation_id(Integer.valueOf(processData[1]));
|
||||
station.setStation_code(processData[2]);
|
||||
station.setStation_name(processData[2]);
|
||||
station.setX(Double.valueOf(processData[5]));
|
||||
station.setY(Double.valueOf(processData[6]));
|
||||
station.setAngle(Double.valueOf(processData[7]));
|
||||
station.setAction_type(StationTypeEnum.CUSTOMIZE.getCode());
|
||||
station.setStation_type(StationTypeEnum.STATION.getCode());
|
||||
// 1类型手动设置站点: 工位点、自定义
|
||||
if ("1".equals(processData[8])) {
|
||||
station.setAction_type(StationTypeEnum.CUSTOMIZE.getCode());
|
||||
station.setStation_type(StationTypeEnum.STATION.getCode());
|
||||
// 0类型系统自动生成: 系统自动生成点、只移动
|
||||
}else if ("0".equals(processData[8])) {
|
||||
station.setAction_type(StationTypeEnum.MOVE.getCode());
|
||||
station.setStation_type(StationTypeEnum.SYSTEM.getCode());
|
||||
}
|
||||
stationService.save(station);
|
||||
System.out.printf("行号 %d - 有效数据: %s%n", lineNumber, station);
|
||||
//Route路径标识
|
||||
}else if ("Route".equals(processData[0]) && processData.length > 23){
|
||||
System.out.printf("行号 %d - 有效路径数据: %s%n", lineNumber, Arrays.toString(processData));
|
||||
RouteInfo routeInfo = new RouteInfo();
|
||||
routeInfo.setRoute_id(Integer.valueOf(processData[1]));
|
||||
routeInfo.setStart_id(Integer.valueOf(processData[2]));
|
||||
routeInfo.setEnd_id(Integer.valueOf(processData[3]));
|
||||
routeInfo.setStart_x(Double.valueOf(processData[4]));
|
||||
routeInfo.setStart_y(Double.valueOf(processData[5]));
|
||||
routeInfo.setEnd_x(Double.valueOf(processData[6]));
|
||||
routeInfo.setEnd_y(Double.valueOf(processData[7]));
|
||||
routeInfo.setNavigation_mode(Integer.valueOf(processData[16]));
|
||||
routeInfo.setRoute_type(Integer.valueOf(processData[17]));
|
||||
routeInfoService.save(routeInfo);
|
||||
|
||||
System.out.printf("行号 %d - 有效数据: %s%n", lineNumber, routeInfo);
|
||||
}
|
||||
} else {
|
||||
System.out.printf("行号 %d - 警告: 'Cairn: Goal'后无内容%n", lineNumber);
|
||||
@@ -101,4 +235,69 @@ public class ProcessZip {
|
||||
System.out.printf("行号 %d - 忽略: %s%n", lineNumber, line);
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedImage convertPgmToPng(byte[] pgmData) {
|
||||
try {
|
||||
// 使用字节缓冲区方便解析
|
||||
ByteBuffer buffer = ByteBuffer.wrap(pgmData);
|
||||
|
||||
// 解析魔术数字 (P5)
|
||||
StringBuilder magic = new StringBuilder();
|
||||
char c;
|
||||
while ((c = (char) buffer.get()) != '\n') {
|
||||
magic.append(c);
|
||||
}
|
||||
if (!magic.toString().equals(PGM_MAGIC_NUMBER)) {
|
||||
throw new BadRequestException("不支持的PGM格式,仅支持P5二进制格式");
|
||||
}
|
||||
|
||||
// 解析宽度和高度
|
||||
StringBuilder dimensionBuilder = new StringBuilder();
|
||||
while ((c = (char) buffer.get()) != '\n') {
|
||||
dimensionBuilder.append(c);
|
||||
}
|
||||
String[] dimensions = dimensionBuilder.toString().trim().split("\\s+");
|
||||
if (dimensions.length < 2) {
|
||||
throw new BadRequestException("无效的尺寸格式");
|
||||
}
|
||||
int width = Integer.parseInt(dimensions[0]);
|
||||
int height = Integer.parseInt(dimensions[1]);
|
||||
|
||||
// 解析最大灰度值 (通常255)
|
||||
StringBuilder maxValBuilder = new StringBuilder();
|
||||
while ((c = (char) buffer.get()) != '\n') {
|
||||
maxValBuilder.append(c);
|
||||
}
|
||||
int maxVal = Integer.parseInt(maxValBuilder.toString().trim());
|
||||
|
||||
// 创建BufferedImage (灰度图像)
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
|
||||
byte[] imageData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
||||
|
||||
// 复制图像数据 (跳过头部)
|
||||
buffer.get(imageData);
|
||||
|
||||
return image;
|
||||
} catch (Exception e) {
|
||||
log.info("PGM解析错误: {}", e.getMessage());
|
||||
throw new BadRequestException("PGM解析错误: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void savePngImage(BufferedImage image, String fileName) {
|
||||
try {
|
||||
// 确保输出目录存在
|
||||
File outputDir = new File(OUTPUT_DIR);
|
||||
if (!outputDir.exists()) {
|
||||
outputDir.mkdirs();
|
||||
}
|
||||
|
||||
// 写入PNG文件
|
||||
File outputFile = new File(outputDir, fileName);
|
||||
ImageIO.write(image, "png", outputFile);
|
||||
log.info("PNG文件已保存: {}", outputFile.getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
log.info("保存PNG失败: {}",e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,4 +62,18 @@ public class VehicleInfo implements Serializable {
|
||||
*/
|
||||
private VehicleException exceptionInfo;
|
||||
|
||||
/**
|
||||
* 车辆x坐标
|
||||
*/
|
||||
private Double x;
|
||||
|
||||
/**
|
||||
* 车辆y坐标
|
||||
*/
|
||||
private Double y;
|
||||
|
||||
/**
|
||||
* 车辆角度
|
||||
*/
|
||||
private Double theta;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.nl.apt15e.apt.vehicle.rest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nl.apt15e.apt.vehicle.dao.VehicleInfo;
|
||||
import org.nl.apt15e.apt.vehicle.service.VehicleInfoService;
|
||||
import org.nl.apt15e.common.logging.annotation.Log;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -22,7 +23,7 @@ public class VehicleInfoController {
|
||||
private VehicleInfoService vehicleInfoService;
|
||||
|
||||
@GetMapping("/getVehicleInfo")
|
||||
// @Log("获取车辆信息")
|
||||
@Log("获取车辆信息")
|
||||
public VehicleInfo getVehicleInfo() {
|
||||
return vehicleInfoService.getVehicleInfo();
|
||||
}
|
||||
|
||||
@@ -58,6 +58,11 @@ public class VehicleInfoServiceImpl implements VehicleInfoService {
|
||||
//上报的异常信息
|
||||
VehicleException vehicleException = JSONObject.toJavaObject(data.getJSONObject("amrException"), VehicleException.class);
|
||||
vehicleInfo.setExceptionInfo(vehicleException);
|
||||
//x,y,theta
|
||||
JSONObject coordinate = data.getJSONObject("coordinate");
|
||||
vehicleInfo.setX(coordinate.getDouble("x"));
|
||||
vehicleInfo.setY(coordinate.getDouble("y"));
|
||||
vehicleInfo.setTheta(coordinate.getDouble("theta"));
|
||||
System.out.println("Response Body: " + data);
|
||||
System.out.println("vehicleInfo: " + vehicleInfo);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package org.nl.apt15e.common.logging.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
@@ -11,6 +8,7 @@ import java.lang.annotation.Target;
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Log {
|
||||
String value() default "";
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -49,18 +51,16 @@ public class LogAspect {
|
||||
@Around("logPointCut()")
|
||||
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
// MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
// Method method = signature.getMethod();
|
||||
// String className = joinPoint.getTarget().getClass().getName();
|
||||
// 方法路径
|
||||
// String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
|
||||
|
||||
// String params = JSONObject.toJSONString(joinPoint.getArgs());
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
Object result;
|
||||
// log.info("【日志注解】开始执行 -- {}:{} {}", className, "111", params);
|
||||
// log.info("【日志注解】开始执行 -- {}:{} {}", className, methodName, params);
|
||||
result = joinPoint.proceed();
|
||||
// log.info("返回参数:{}" ,JSONObject.toJSONString(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
23
src/main/java/org/nl/apt15e/config/CorsConfig.java
Normal file
23
src/main/java/org/nl/apt15e/config/CorsConfig.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package org.nl.apt15e.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* @author dsh
|
||||
* 2025/7/14
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedHeaders(CorsConfiguration.ALL)
|
||||
.allowedMethods(CorsConfiguration.ALL)
|
||||
.allowCredentials(true)
|
||||
.maxAge(3600);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//package org.nl.apt15e.config.thread;
|
||||
//
|
||||
//import comm_protocol.Robottype;
|
||||
//import comm_protocol.Uidata;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//import org.springframework.web.socket.BinaryMessage;
|
||||
//import org.springframework.web.socket.CloseStatus;
|
||||
//import org.springframework.web.socket.TextMessage;
|
||||
//import org.springframework.web.socket.WebSocketSession;
|
||||
//import org.springframework.web.socket.handler.BinaryWebSocketHandler;
|
||||
//
|
||||
//import static comm_protocol.Uidata.UiDatagram.ItemCase.*;
|
||||
//
|
||||
///**
|
||||
// * @author dsh
|
||||
// * 2025/7/9
|
||||
// */
|
||||
//@Slf4j
|
||||
//@Component
|
||||
//public class ProtobufWebSocketHandler extends BinaryWebSocketHandler {
|
||||
//
|
||||
// @Override
|
||||
// protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
|
||||
// try {
|
||||
// // 解析Protobuf数据
|
||||
// byte[] payload = message.getPayload().array();
|
||||
// Uidata.UiDatagram datagram = Uidata.UiDatagram.parseFrom(payload);
|
||||
//
|
||||
// switch (datagram.getItemCase()) {
|
||||
// case ONLINE_INFO:
|
||||
// Uidata.UiOnline online = datagram.getOnlineInfo();
|
||||
// System.out.println("Received online_info: " + online);
|
||||
// break;
|
||||
//
|
||||
// case HEARTBEAT:
|
||||
// Uidata.UiHeartbeat heartbeat = datagram.getHeartbeat();
|
||||
// System.out.println("Received heartbeat: " + heartbeat);
|
||||
// break;
|
||||
//
|
||||
// case LASER_SCAN:
|
||||
// Robottype.LaserData laser = datagram.getLaserScan();
|
||||
// System.out.println("Received laser_scan: " + laser);
|
||||
// break;
|
||||
//
|
||||
// case IMAGE:
|
||||
// // 处理图像数据
|
||||
// break;
|
||||
//
|
||||
// case CONTROL_SPEED:
|
||||
// // 处理速度控制
|
||||
// break;
|
||||
//
|
||||
// case ITEM_NOT_SET:
|
||||
// System.err.println("UiDatagram item not set!");
|
||||
// break;
|
||||
// }
|
||||
// session.close();
|
||||
// } catch (Exception e) {
|
||||
// log.error("Error processing protobuf message", e);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void afterConnectionEstablished(WebSocketSession session) {
|
||||
// log.info("Connected to WebSocket server: {}", session.getUri());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
||||
// log.info("Connection closed: {}", status);
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,45 @@
|
||||
//package org.nl.apt15e.config.thread;
|
||||
//
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//import org.springframework.web.socket.client.WebSocketClient;
|
||||
//import org.springframework.web.socket.client.WebSocketConnectionManager;
|
||||
//import org.springframework.web.socket.client.standard.StandardWebSocketClient;
|
||||
//
|
||||
//import javax.websocket.ContainerProvider;
|
||||
//import javax.websocket.WebSocketContainer;
|
||||
//
|
||||
///**
|
||||
// * @author dsh
|
||||
// * 2025/7/9
|
||||
// */
|
||||
//@Slf4j
|
||||
//@Configuration
|
||||
//public class WebsocketClientConfig {
|
||||
//
|
||||
// @Bean
|
||||
// public WebSocketClient webSocketClient() {
|
||||
// // 创建WebSocket容器并配置缓冲区大小
|
||||
// WebSocketContainer container = ContainerProvider.getWebSocketContainer();
|
||||
// // 256KB
|
||||
// container.setDefaultMaxBinaryMessageBufferSize(100 * 1024);
|
||||
// container.setDefaultMaxTextMessageBufferSize(100 * 1024);
|
||||
//
|
||||
// return new StandardWebSocketClient(container);
|
||||
// }
|
||||
//
|
||||
// @Bean
|
||||
// public WebSocketConnectionManager webSocketConnectionManager() {
|
||||
// String serverUrl = "ws://192.168.100.82:9998/ws/AGVInfo";
|
||||
//
|
||||
// WebSocketConnectionManager manager = new WebSocketConnectionManager(
|
||||
// this.webSocketClient(),
|
||||
// new ProtobufWebSocketHandler(),
|
||||
// serverUrl
|
||||
// );
|
||||
// manager.setAutoStartup(true);
|
||||
// return manager;
|
||||
// }
|
||||
//}
|
||||
215
src/main/proto/robottype.proto
Normal file
215
src/main/proto/robottype.proto
Normal file
@@ -0,0 +1,215 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package comm_protocol;
|
||||
|
||||
message RobotShape {
|
||||
double front = 1;
|
||||
double back = 2;
|
||||
double left = 3;
|
||||
double right = 4;
|
||||
double radius = 5;
|
||||
}
|
||||
|
||||
message Point {
|
||||
double x = 1;
|
||||
double y = 2;
|
||||
double intensity = 3;
|
||||
}
|
||||
message PointXYZ {
|
||||
sint32 x = 1;
|
||||
sint32 y = 2;
|
||||
sint32 z = 3;
|
||||
}
|
||||
message OrientedPoint {
|
||||
double x = 1;
|
||||
double y = 2;
|
||||
double theta = 3;
|
||||
}
|
||||
|
||||
message RobotSpeed {
|
||||
double vx = 1;
|
||||
double vy = 2;
|
||||
double w = 3;
|
||||
int64 timeStamp = 4;
|
||||
}
|
||||
|
||||
message RobotState {
|
||||
OrientedPoint pose = 1;
|
||||
RobotSpeed speed = 2;
|
||||
int64 timeStamp = 3;
|
||||
}
|
||||
|
||||
message RobotPath {
|
||||
int64 timeStamp = 1;
|
||||
repeated RobotState state = 2;
|
||||
}
|
||||
|
||||
message LaserData {
|
||||
sint32 location = 1;
|
||||
int64 timeStamp = 2;
|
||||
repeated Point scan = 3;
|
||||
}
|
||||
|
||||
message ImageData {
|
||||
sint32 location = 1;
|
||||
sint64 timeStamp = 2;
|
||||
sint32 width = 3;
|
||||
sint32 height = 4;
|
||||
sint32 chanel = 5;
|
||||
sint32 package_index=6;
|
||||
sint32 package_length=7;
|
||||
sint32 imgdata_size=8;
|
||||
sint32 curr_size=9;
|
||||
bytes data = 10;
|
||||
}
|
||||
message PcloudData {
|
||||
sint32 location = 1;
|
||||
sint64 timeStamp = 2;
|
||||
repeated PointXYZ point = 3;
|
||||
}
|
||||
|
||||
message ControlLimit {
|
||||
double max_vel = 1;
|
||||
double max_w = 2;
|
||||
int32 turn_dir_limit = 3;
|
||||
int32 shelf_dir_limit = 4;
|
||||
}
|
||||
message stReachThreshold {
|
||||
double reach_angle = 1;
|
||||
double reach_dist = 2;
|
||||
}
|
||||
|
||||
message PathProperty {
|
||||
int32 move_mode = 1;
|
||||
bool avoid_obstacle = 2;
|
||||
double path_width = 3;
|
||||
double safe_dist = 4;
|
||||
double min_turn_radius = 5;
|
||||
int32 route_mode = 6;
|
||||
string path_extends = 7;
|
||||
}
|
||||
|
||||
|
||||
message RobotTask {
|
||||
|
||||
message WayPoint {
|
||||
// 路径信息,路径a-b,路径信息放在b点中
|
||||
message PathInfo {
|
||||
enum Sensor {
|
||||
NONE = 0;
|
||||
DEPTH_CAMERA = 1; //深度相机
|
||||
ULTRASONIC = 2; //超声
|
||||
}
|
||||
double distance2point = 1; // 到点距离阈值(单位m)
|
||||
double angle2point = 2; // 到点角度阈值(单位rad)
|
||||
double safe_distance = 3; // 安全距离
|
||||
bool avoid_obstacle = 4; // 是否避障
|
||||
double max_velocity = 5; // 最大速度(单位m/s)
|
||||
double path_width = 6; // 路径宽度(单位m)
|
||||
double min_turn_radius = 7; // 最小转弯半径
|
||||
int32 sensor = 8; // 开启laser之外的其他传感器,可以是Sensor的多个组合
|
||||
int32 move_mode = 9;
|
||||
int32 route_mode = 10;
|
||||
string path_extends = 11;
|
||||
}
|
||||
string point_name = 1; // 点名
|
||||
RobotState coordinate = 2; // 位姿
|
||||
PathInfo path_info = 3; // 路径信息
|
||||
// 所有被其他车锁着点中距离该点最近的点,这个信息告诉本体可以让本体进行灵活多车避障
|
||||
RobotState min_dist_point = 4;
|
||||
bool is_lock = 6;
|
||||
stReachThreshold reach_theta = 7;
|
||||
string point_extends = 8;
|
||||
}
|
||||
|
||||
int64 command = 1;
|
||||
int64 sub_command = 2;
|
||||
string params = 3;
|
||||
repeated WayPoint way_point = 4;
|
||||
RobotState init_pose = 5;
|
||||
RobotState target_pose = 6;
|
||||
bool is_final = 7;
|
||||
string robot_id = 8;
|
||||
string robot_task_id = 9;
|
||||
int64 timeStamp = 10;
|
||||
bool need_response = 11; // 是否需要回复
|
||||
// 货架信息,第一位数值代表货架类型,从1开始;第二位代表车与货架长边的关系,0-平行,1-垂直
|
||||
// 位数之间采用空格隔开;第一位是0的话代表不带货架;
|
||||
string shelf_info = 12;
|
||||
RobotPath nav_robot_path = 13;
|
||||
}
|
||||
|
||||
message RobotTaskResult {
|
||||
string task_id = 1;
|
||||
string robot_id = 2;
|
||||
int64 command = 3;
|
||||
int32 result = 4; // 0-success
|
||||
}
|
||||
|
||||
message RobotPoseWithCov {
|
||||
double x = 1;
|
||||
double y = 2;
|
||||
double theta = 3;
|
||||
double noisy_x = 4;
|
||||
double noisy_y = 5;
|
||||
double noisy_theta = 6;
|
||||
}
|
||||
|
||||
message Relocate {
|
||||
RobotPoseWithCov reloc_pose = 1;
|
||||
string robot_id = 2;
|
||||
}
|
||||
|
||||
message SwitchMap {
|
||||
string map_id = 1;
|
||||
string robot_id = 2;
|
||||
}
|
||||
|
||||
message RobotBase {
|
||||
message BatteryInfo {
|
||||
double soc = 1; // 电池剩余电量百分比
|
||||
int32 status = 2; // 电池异常
|
||||
uint64 update_time = 3;
|
||||
}
|
||||
message DeviceInfo {
|
||||
bool stop_button = 1; // 急停按钮是否拍下
|
||||
bool collision = 2; // 是否发生碰撞
|
||||
bool shut_down = 3; // 软关机
|
||||
bool inactive = 4; // 底层硬件触发手动模式
|
||||
uint64 update_time = 5;
|
||||
}
|
||||
message MoveInfo {
|
||||
string heading_spot = 1; // 下一个目标点
|
||||
// double task_complete_percent = 2; // 导航任务完成百分比
|
||||
// RobotSpeed velocity = 3; // 小车速度
|
||||
uint64 update_time = 4;
|
||||
}
|
||||
message Locate {
|
||||
RobotPoseWithCov current_state = 1; // 小车当前位姿
|
||||
int32 locate = 2; // 定位结果
|
||||
uint64 update_time = 3;
|
||||
}
|
||||
BatteryInfo battery_info = 1; // 电池信息
|
||||
DeviceInfo device_info = 2;
|
||||
MoveInfo move_info = 3;
|
||||
Locate locate = 4;
|
||||
string robot_id = 5;
|
||||
// 高32位代表设备类型,低32位数值代表异常值
|
||||
repeated uint64 exception_code = 6;
|
||||
int32 payloads = 7; // 物料信息,不同应用车型有不同含义
|
||||
int32 lift_state = 8; // 顶升状态
|
||||
bool robot_inactive = 9; // 是否手动模式,软件触发
|
||||
int32 digital_status = 10; // 数字输出端口状态,不同应用车型有不同含义
|
||||
string data_pool = 11;
|
||||
map<string, string> runnig_fsm = 12;
|
||||
}
|
||||
|
||||
message RCSInfo {
|
||||
int32 action = 1;
|
||||
string action_params = 2;
|
||||
string fsm_state = 3;
|
||||
string rcs_task_id = 4;
|
||||
int32 rcs_task_state = 5;
|
||||
}
|
||||
47
src/main/proto/uidata.proto
Normal file
47
src/main/proto/uidata.proto
Normal file
@@ -0,0 +1,47 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "robottype.proto";
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package comm_protocol;
|
||||
|
||||
message UiOnline {
|
||||
string uuid = 1;
|
||||
int32 comm_type = 2;
|
||||
int32 codec_pattern = 3;
|
||||
string host = 4;
|
||||
int32 port = 5;
|
||||
}
|
||||
|
||||
message UiHeartbeat {
|
||||
string uuid = 1;
|
||||
}
|
||||
|
||||
message KVState {
|
||||
string key = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message DataPool {
|
||||
map<string, string> kv_state = 1;
|
||||
}
|
||||
|
||||
message UiDatagram {
|
||||
oneof Item{
|
||||
UiOnline online_info = 1; // ui -> robot
|
||||
UiHeartbeat heartbeat = 2; // ui -> robot
|
||||
LaserData laser_scan = 3;
|
||||
ImageData image = 4;
|
||||
RobotSpeed control_speed = 5;
|
||||
RobotState current_state = 6;
|
||||
RobotPath current_path = 7;
|
||||
RobotBase robot_base = 8;
|
||||
RobotTask robot_task = 9; // ui -> robot
|
||||
Relocate relocate = 10; // ui -> robot
|
||||
RobotTaskResult robot_task_result = 11;
|
||||
RCSInfo rcs_info = 12;
|
||||
DataPool data_pool = 13; // ui -> robot
|
||||
PcloudData pcloud = 14;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user