add:解析地图相关数据

This commit is contained in:
2025-07-18 18:00:08 +08:00
parent ea8c26f9bb
commit 74c955d4c5
30 changed files with 1200 additions and 68 deletions

43
pom.xml
View File

@@ -48,6 +48,18 @@
<artifactId>spring-boot-starter-websocket</artifactId> <artifactId>spring-boot-starter-websocket</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Protocol Buffers -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.21.12</version>
</dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
@@ -116,6 +128,13 @@
</dependencyManagement> </dependencyManagement>
<build> <build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@@ -144,6 +163,30 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<!-- Protobuf 自动生成插件 -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version> <!-- 使用最新版本 -->
<configuration>
<!-- Proto 文件目录 -->
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
<!-- Java 输出目录 -->
<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>
<!-- 显式指定 protoc 版本自动下载exe:根据不同系统来填写 -->
<protocArtifact>com.google.protobuf:protoc:3.21.12:exe:windows-x86_64</protocArtifact>
<!-- 是否清空输出目录 -->
<clearOutputDirectory>true</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View 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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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> {
}

View 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;
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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> {
}

View File

@@ -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);
}
}

View File

@@ -11,11 +11,12 @@ import lombok.Getter;
@AllArgsConstructor @AllArgsConstructor
public enum StationTypeEnum { public enum StationTypeEnum {
//站点类型 Station工位点 Charge充电点 Breaks休息点 Docking对接点 //站点类型 Station工位点 Charge充电点 Breaks休息点 Docking对接点 System系统自动生成点
STATION(1, "STATION", "工位点"), STATION(1, "STATION", "工位点"),
CHARGE(2, "CHARGE", "充电点"), CHARGE(2, "CHARGE", "充电点"),
BREAKS(3, "BREAKS", "休息点"), BREAKS(3, "BREAKS", "休息点"),
DOCKING(4, "DOCKING", "对接点"), DOCKING(4, "DOCKING", "对接点"),
SYSTEM(5, "SYSTEM", "系统自动生成点"),
//动作类型 Ascend升叉 Descend降叉 Move移动 Customize自定义 //动作类型 Ascend升叉 Descend降叉 Move移动 Customize自定义

View File

@@ -16,10 +16,12 @@ import java.io.Serializable;
public class Station implements Serializable { public class Station implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@TableId
private Integer station_id;
/** /**
* 站点编码 对应地图上站点 * 站点编码 对应地图上站点
*/ */
@TableId
private String station_code; private String station_code;
/** /**
@@ -28,7 +30,7 @@ public class Station implements Serializable {
private String station_name; private String station_name;
/** /**
* 站点类型 (Station工位点 Charge充电点 Breaks休息点 Docking对接点) * 站点类型 (Station工位点 Charge充电点 Breaks休息点 Docking对接点 System系统自动生成点)
*/ */
private String station_type; private String station_type;

View File

@@ -1,7 +1,9 @@
package org.nl.apt15e.apt.station.service.impl; package org.nl.apt15e.apt.station.service.impl;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 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.dao.Station;
import org.nl.apt15e.apt.station.service.StationService; import org.nl.apt15e.apt.station.service.StationService;
import org.nl.apt15e.apt.station.service.mapper.StationMapper; import org.nl.apt15e.apt.station.service.mapper.StationMapper;
@@ -25,6 +27,7 @@ public class StationServiceImpl extends ServiceImpl<StationMapper, Station> impl
@Override @Override
public List<Station> queryAllStation() { public List<Station> queryAllStation() {
return stationMapper.selectList(null); return stationMapper.selectList(stationMapper.selectList(new LambdaQueryWrapper<>(Station.class)
.eq(Station::getStation_type, StationTypeEnum.STATION.getCode())));
} }
} }

View File

@@ -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;
}

View File

@@ -1,5 +1,6 @@
package org.nl.apt15e.apt.teaching.rest; package org.nl.apt15e.apt.teaching.rest;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.nl.apt15e.apt.teaching.service.TeachingService; import org.nl.apt15e.apt.teaching.service.TeachingService;
@@ -51,8 +52,10 @@ public class TeachingController {
} }
@PostMapping("/getLocalMaps") @PostMapping("/getLocalMaps")
private ResponseEntity<Object> getLocalMaps() { // @Log("获取地图列表")
return new ResponseEntity<>(teachingService.getLocalMaps(), HttpStatus.OK); private JSONObject getLocalMaps() {
// return new ResponseEntity<>(teachingService.getLocalMaps(), HttpStatus.OK);
return teachingService.getLocalMaps();
} }
@PostMapping("/getRunMapZip") @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) { 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); 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);
}
} }

View File

@@ -1,5 +1,7 @@
package org.nl.apt15e.apt.teaching.service; package org.nl.apt15e.apt.teaching.service;
import com.alibaba.fastjson.JSONObject;
import java.io.File; import java.io.File;
import java.util.Map; 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(); Map<String, Object> restart();
/**
* 一键部署地图
* @return
*/
Map<String, Object> oneClickDeployment(String mapName);
} }

View File

@@ -2,11 +2,16 @@ package org.nl.apt15e.apt.teaching.service.impl;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j; 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.teaching.service.TeachingService;
import org.nl.apt15e.apt.vehicle.ProcessZip; import org.nl.apt15e.apt.vehicle.ProcessZip;
import org.nl.apt15e.apt.vehicle.service.impl.VehicleInfoServiceImpl; import org.nl.apt15e.apt.vehicle.service.impl.VehicleInfoServiceImpl;
@@ -16,7 +21,9 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.File; import java.io.File;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.stream.IntStream;
/** /**
* @author dsh * @author dsh
@@ -29,9 +36,12 @@ public class TeachingServiceImpl implements TeachingService {
@Resource @Resource
private ProcessZip processZip; private ProcessZip processZip;
@Resource
private StationService stationService;
@Override @Override
public Map<String, Object> startMapping(String mapName) { public Map<String, Object> startMapping(String mapName) {
if ("".equals(mapName)){ if (StrUtil.isBlank(mapName)){
throw new BadRequestException("mapName is empty"); throw new BadRequestException("mapName is empty");
} }
JSONObject params = new JSONObject(); JSONObject params = new JSONObject();
@@ -50,8 +60,10 @@ public class TeachingServiceImpl implements TeachingService {
log.info("开始建图:{}",body); log.info("开始建图:{}",body);
if ("200".equals(body.getString("code"))){ if ("200".equals(body.getString("code"))){
body =(JSONObject) this.startManual(); body =(JSONObject) this.startManual();
body.put("message","开始建图成功");
return body;
} }
return body;
} }
log.info("开始建图失败"); log.info("开始建图失败");
throw new BadRequestException("开始建图失败"); throw new BadRequestException("开始建图失败");
@@ -67,11 +79,14 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("切手动失败"); throw new BadRequestException("切手动失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("切手动:{}",body); log.info("切手动:{}",body);
return body; if ("200".equals(body.getString("code"))){
body.put("message","切自动成功");
return body;
}
} }
log.info("切手动失败"); log.info("切手动失败");
throw new BadRequestException("切手动失败"); throw new BadRequestException("切手动失败");
@@ -87,10 +102,13 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("切自动失败"); throw new BadRequestException("切自动失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("切自动:{}",body); log.info("切自动:{}",body);
if ("200".equals(body.getString("code"))){
body.put("message","切自动成功");
}
return body; return body;
} }
log.info("切自动失败"); log.info("切自动失败");
@@ -107,14 +125,15 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("结束建图失败"); throw new BadRequestException("结束建图失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("结束建图:{}",body); log.info("结束建图:{}",body);
if ("200".equals(body.getString("code"))){ if ("200".equals(body.getString("code"))){
body =(JSONObject) this.stopManual(); body =(JSONObject) this.stopManual();
body.put("message","结束建图成功");
return body;
} }
return body;
} }
log.info("结束建图失败"); log.info("结束建图失败");
throw new BadRequestException("结束建图失败"); throw new BadRequestException("结束建图失败");
@@ -122,7 +141,7 @@ public class TeachingServiceImpl implements TeachingService {
@Override @Override
public Map<String, Object> setStation(String stationName) { public Map<String, Object> setStation(String stationName) {
if ("".equals(stationName)){ if (StrUtil.isBlank(stationName)){
throw new BadRequestException("spotCode is empty"); throw new BadRequestException("spotCode is empty");
} }
JSONObject params = new JSONObject(); JSONObject params = new JSONObject();
@@ -135,18 +154,21 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("设置站点失败"); throw new BadRequestException("设置站点失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("设置站点:{}",body); log.info("设置站点:{}",body);
return body; if ("200".equals(body.getString("code"))){
body.put("message","设置站点成功");
return body;
}
} }
log.info("设置站点失败"); log.info("设置站点失败");
throw new BadRequestException("设置站点失败"); throw new BadRequestException("设置站点失败");
} }
@Override @Override
public Map<String, Object> getLocalMaps() { public JSONObject getLocalMaps() {
HttpResponse response = null; HttpResponse response = null;
try { try {
response = HTTPUtil.get("http://192.168.100.82:9998","/tool/editor/getLocalMaps", new JSONObject()); 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("获取地图列表失败"); throw new BadRequestException("获取地图列表失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("获取地图列表:{}",body); log.info("获取地图列表:{}",body);
return body; if ("200".equals(body.getString("code"))){
body.put("message","获取地图列表成功");
return body;
}
} }
log.info("获取地图列表失败"); log.info("获取地图列表失败");
throw new BadRequestException("获取地图列表失败"); throw new BadRequestException("获取地图列表失败");
@@ -167,7 +192,7 @@ public class TeachingServiceImpl implements TeachingService {
@Override @Override
public Map<String, Object> deployRunMap(String mapName) { public Map<String, Object> deployRunMap(String mapName) {
if ("".equals(mapName)){ if (StrUtil.isBlank(mapName)){
throw new BadRequestException("mapName is empty"); throw new BadRequestException("mapName is empty");
} }
JSONObject params = new JSONObject(); JSONObject params = new JSONObject();
@@ -180,11 +205,14 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("部署地图失败"); throw new BadRequestException("部署地图失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("部署地图:{}",body); log.info("部署地图:{}",body);
return body; if ("200".equals(body.getString("code"))){
body.put("message","部署地图成功");
return body;
}
} }
log.info("部署地图失败"); log.info("部署地图失败");
throw new BadRequestException("部署地图失败"); throw new BadRequestException("部署地图失败");
@@ -192,7 +220,7 @@ public class TeachingServiceImpl implements TeachingService {
@Override @Override
public Map<String, Object> changeCurrentRunMap(String mapName) { public Map<String, Object> changeCurrentRunMap(String mapName) {
if ("".equals(mapName)){ if (StrUtil.isBlank(mapName)){
throw new BadRequestException("mapName is empty"); throw new BadRequestException("mapName is empty");
} }
JSONObject params = new JSONObject(); JSONObject params = new JSONObject();
@@ -205,11 +233,14 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("应用地图失败"); throw new BadRequestException("应用地图失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("应用地图:{}",body); log.info("应用地图:{}",body);
return body; if ("200".equals(body.getString("code"))){
body.put("message","应用地图成功");
return body;
}
} }
log.info("应用地图失败"); log.info("应用地图失败");
throw new BadRequestException("应用地图失败"); throw new BadRequestException("应用地图失败");
@@ -217,7 +248,7 @@ public class TeachingServiceImpl implements TeachingService {
@Override @Override
public byte[] getRunMapZip(String mapName) { public byte[] getRunMapZip(String mapName) {
if ("".equals(mapName)){ if (StrUtil.isBlank(mapName)){
throw new BadRequestException("mapName is empty"); throw new BadRequestException("mapName is empty");
} }
JSONObject params = new JSONObject(); JSONObject params = new JSONObject();
@@ -230,12 +261,11 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("获取地图包失败"); throw new BadRequestException("获取地图包失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取zip包 // 获取zip包
byte[] zipBytes = response.bodyBytes(); byte[] zipBytes = response.bodyBytes();
log.info("解析地图包数据");
processZip.processZipResponse(zipBytes); processZip.processZipResponse(zipBytes);
log.info("获取地图包");
return zipBytes; return zipBytes;
} }
log.info("获取地图包失败"); log.info("获取地图包失败");
@@ -254,7 +284,7 @@ public class TeachingServiceImpl implements TeachingService {
.header("token", "admin123") .header("token", "admin123")
.header("name", "lx-script") .header("name", "lx-script")
.header("Content-Type", "multipart/form-data;charset=UTF-8") .header("Content-Type", "multipart/form-data;charset=UTF-8")
.form("areaId", VehicleInfoServiceImpl.vehicleInfo.getAreaId()) .form("areaId", 1)
.form("file",zipFile,mapName+".zip") .form("file",zipFile,mapName+".zip")
.execute(); .execute();
} catch (Exception e) { } catch (Exception e) {
@@ -262,11 +292,14 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("同步地图失败"); throw new BadRequestException("同步地图失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("同步地图:{}",body); log.info("同步地图:{}",body);
return body; if (body.getBoolean("state")){
body.put("message","同步地图成功");
return body;
}
} }
log.info("同步地图失败"); log.info("同步地图失败");
throw new BadRequestException("同步地图失败"); throw new BadRequestException("同步地图失败");
@@ -277,6 +310,9 @@ public class TeachingServiceImpl implements TeachingService {
if (ObjectUtil.isEmpty(x) || ObjectUtil.isEmpty(y) || ObjectUtil.isEmpty(angle)){ if (ObjectUtil.isEmpty(x) || ObjectUtil.isEmpty(y) || ObjectUtil.isEmpty(angle)){
throw new BadRequestException("params is empty"); throw new BadRequestException("params is empty");
} }
log.info(angle.toString());
angle = (angle * 180) / Math.PI;
log.info(angle.toString());
JSONObject params = new JSONObject(); JSONObject params = new JSONObject();
params.put("x", x); params.put("x", x);
params.put("y", y); params.put("y", y);
@@ -284,22 +320,26 @@ public class TeachingServiceImpl implements TeachingService {
params.put("noisyX", 0.5); params.put("noisyX", 0.5);
params.put("noisyY", 0.5); params.put("noisyY", 0.5);
params.put("noisyAngle", Math.PI); params.put("noisyAngle", Math.PI);
log.info(params.getString("angle"));
HttpResponse response = null; HttpResponse response = null;
try { try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/relocate", params); response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/relocate", params);
} catch (Exception e) { } catch (Exception e) {
log.info("访问车体重定位接口报错:{}",e.getMessage()); 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()); JSONObject body = JSON.parseObject(response.body());
log.info("重定位:{}",body); log.info("重定位:{}",body);
return body; if ("200".equals(body.getString("code"))){
body.put("message","重定位指令下发成功,正在重新定位中。");
return body;
}
} }
log.info("重定位失败"); log.info("重定位指令下发失败");
throw new BadRequestException("重定位失败"); throw new BadRequestException("重定位指令下发失败");
} }
@Override @Override
@@ -312,13 +352,61 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("车体程序重启失败"); throw new BadRequestException("车体程序重启失败");
} }
// 检查响应状态码 // 检查响应状态码
if (response.isOk()) { if (response.isOk() && response.body() != null) {
// 获取响应体内容 // 获取响应体内容
JSONObject body = JSON.parseObject(response.body()); JSONObject body = JSON.parseObject(response.body());
log.info("车体程序重启:{}",body); log.info("车体程序重启:{}",body);
return body; if ("200".equals(body.getString("code"))){
body.put("message","车体程序重启成功");
return body;
}
} }
log.info("车体程序重启失败"); log.info("车体程序重启失败");
throw new BadRequestException("车体程序重启失败"); 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;
}
} }

View File

@@ -1,19 +1,32 @@
package org.nl.apt15e.apt.vehicle; package org.nl.apt15e.apt.vehicle;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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.StationTypeEnum;
import org.nl.apt15e.apt.station.dao.Station; import org.nl.apt15e.apt.station.dao.Station;
import org.nl.apt15e.apt.station.service.StationService; import org.nl.apt15e.apt.station.service.StationService;
import org.nl.apt15e.apt.station.service.impl.StationServiceImpl; import org.nl.apt15e.apt.station.service.impl.StationServiceImpl;
import org.nl.apt15e.common.BadRequestException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.yaml.snakeyaml.Yaml;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.BufferedReader; import javax.imageio.ImageIO;
import java.io.ByteArrayInputStream; import java.awt.image.BufferedImage;
import java.io.InputStreamReader; import java.awt.image.DataBufferByte;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
@@ -22,31 +35,46 @@ import java.util.zip.ZipInputStream;
* @author dsh * @author dsh
* 2025/7/7 * 2025/7/7
*/ */
@Slf4j
@Service @Service
public class ProcessZip { public class ProcessZip {
@Resource @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) { public void processZipResponse(byte[] zipBytes) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(zipBytes); try (ByteArrayInputStream bais = new ByteArrayInputStream(zipBytes);
ZipInputStream zis = new ZipInputStream(bais, StandardCharsets.UTF_8)) { ZipInputStream zis = new ZipInputStream(bais, StandardCharsets.UTF_8)) {
MapInfo mapInfo = new MapInfo();
ZipEntry entry; ZipEntry entry;
// 遍历ZIP文件中的每个条目 // 遍历ZIP文件中的每个条目
while ((entry = zis.getNextEntry()) != null) { while ((entry = zis.getNextEntry()) != null) {
String entryName = entry.getName(); String entryName = entry.getName();
// 3. 筛选.lxmap文件 // 处理.lxmap文件,获取点位信息和路径信息
if (entryName.toLowerCase().endsWith(".lxmap")) { if (entryName.toLowerCase().endsWith(".lxmap")) {
System.out.println("解析文件: " + entryName); log.info("解析文件: {}", entryName);
// 4. 读取文件内容使用BufferedReader按行处理 // 读取文件内容使用BufferedReader按行处理
BufferedReader reader = new BufferedReader( BufferedReader reader = new BufferedReader(
new InputStreamReader(zis, StandardCharsets.UTF_8)); new InputStreamReader(zis, StandardCharsets.UTF_8));
//存储站点之前先清除站点表中数据 //存储站点之前先清除站点表中数据和路径表中的数据
stationService.remove(new LambdaQueryWrapper<>()); stationService.remove(new LambdaQueryWrapper<>());
routeInfoService.remove(new LambdaQueryWrapper<>());
String line; String line;
int lineNumber = 0; int lineNumber = 0;
@@ -55,11 +83,93 @@ public class ProcessZip {
// 5. 处理每一行数据 // 5. 处理每一行数据
processLine(line, lineNumber); 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) { catch (Exception e) {
throw new RuntimeException("处理ZIP文件失败", e); throw new BadRequestException("处理地图包数据失败:"+e.getMessage());
} }
} }
@@ -69,7 +179,7 @@ public class ProcessZip {
return; return;
} }
// 检查是否以"Cairn: Goal"开头 // 检查是否以"Cairn"开头
if (line.startsWith("Cairn:")) { if (line.startsWith("Cairn:")) {
// 截取开头后面的数据6个字符长度 // 截取开头后面的数据6个字符长度
String dataPart = line.substring(6).trim(); String dataPart = line.substring(6).trim();
@@ -78,20 +188,44 @@ public class ProcessZip {
// 按空格分割字段(多个连续空格视为一个分隔符) // 按空格分割字段(多个连续空格视为一个分隔符)
String[] processData = dataPart.split("\\s+"); String[] processData = dataPart.split("\\s+");
//判断数据是否是点位数据 && 数据长度正常 && 点位类型是1工位点(站点) //判断数据是否是点位数据 && 数据长度正常
//满足以上条件 目前只记录站点 // Goal点位标识
if ("Goal".equals(processData[0]) && processData.length > 9 && "1".equals(processData[8])) { if ("Goal".equals(processData[0]) && processData.length > 9) {
System.out.printf("行号 %d - 有效数据: %s%n", lineNumber, Arrays.toString(processData)); System.out.printf("行号 %d - 有效点位数据: %s%n", lineNumber, Arrays.toString(processData));
Station station = new Station(); Station station = new Station();
station.setStation_id(Integer.valueOf(processData[1]));
station.setStation_code(processData[2]); station.setStation_code(processData[2]);
station.setStation_name(processData[2]); station.setStation_name(processData[2]);
station.setX(Double.valueOf(processData[5])); station.setX(Double.valueOf(processData[5]));
station.setY(Double.valueOf(processData[6])); station.setY(Double.valueOf(processData[6]));
station.setAngle(Double.valueOf(processData[7])); station.setAngle(Double.valueOf(processData[7]));
station.setAction_type(StationTypeEnum.CUSTOMIZE.getCode()); // 1类型手动设置站点 工位点、自定义
station.setStation_type(StationTypeEnum.STATION.getCode()); 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); stationService.save(station);
System.out.printf("行号 %d - 有效数据: %s%n", lineNumber, 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 { } else {
System.out.printf("行号 %d - 警告: 'Cairn: Goal'后无内容%n", lineNumber); System.out.printf("行号 %d - 警告: 'Cairn: Goal'后无内容%n", lineNumber);
@@ -101,4 +235,69 @@ public class ProcessZip {
System.out.printf("行号 %d - 忽略: %s%n", lineNumber, line); 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());
}
}
} }

View File

@@ -62,4 +62,18 @@ public class VehicleInfo implements Serializable {
*/ */
private VehicleException exceptionInfo; private VehicleException exceptionInfo;
/**
* 车辆x坐标
*/
private Double x;
/**
* 车辆y坐标
*/
private Double y;
/**
* 车辆角度
*/
private Double theta;
} }

View File

@@ -3,6 +3,7 @@ package org.nl.apt15e.apt.vehicle.rest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nl.apt15e.apt.vehicle.dao.VehicleInfo; import org.nl.apt15e.apt.vehicle.dao.VehicleInfo;
import org.nl.apt15e.apt.vehicle.service.VehicleInfoService; 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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -22,7 +23,7 @@ public class VehicleInfoController {
private VehicleInfoService vehicleInfoService; private VehicleInfoService vehicleInfoService;
@GetMapping("/getVehicleInfo") @GetMapping("/getVehicleInfo")
// @Log("获取车辆信息") @Log("获取车辆信息")
public VehicleInfo getVehicleInfo() { public VehicleInfo getVehicleInfo() {
return vehicleInfoService.getVehicleInfo(); return vehicleInfoService.getVehicleInfo();
} }

View File

@@ -58,6 +58,11 @@ public class VehicleInfoServiceImpl implements VehicleInfoService {
//上报的异常信息 //上报的异常信息
VehicleException vehicleException = JSONObject.toJavaObject(data.getJSONObject("amrException"), VehicleException.class); VehicleException vehicleException = JSONObject.toJavaObject(data.getJSONObject("amrException"), VehicleException.class);
vehicleInfo.setExceptionInfo(vehicleException); vehicleInfo.setExceptionInfo(vehicleException);
//xytheta
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("Response Body: " + data);
System.out.println("vehicleInfo: " + vehicleInfo); System.out.println("vehicleInfo: " + vehicleInfo);
} }

View File

@@ -1,9 +1,6 @@
package org.nl.apt15e.common.logging.annotation; package org.nl.apt15e.common.logging.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* @author dsh * @author dsh
@@ -11,6 +8,7 @@ import java.lang.annotation.Target;
*/ */
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log { public @interface Log {
String value() default ""; String value() default "";

View File

@@ -4,10 +4,12 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature; import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -49,18 +51,16 @@ public class LogAspect {
@Around("logPointCut()") @Around("logPointCut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 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()); // String params = JSONObject.toJSONString(joinPoint.getArgs());
//
//
Object result; Object result;
// log.info("【日志注解】开始执行 -- {}:{} {}", className, "111", params); // log.info("【日志注解】开始执行 -- {}:{} {}", className, methodName, params);
result = joinPoint.proceed(); result = joinPoint.proceed();
// log.info("返回参数:{}" ,JSONObject.toJSONString(result)); // log.info("返回参数:{}" ,JSONObject.toJSONString(result));
return result; return result;
} }
} }

View 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);
}
}

View File

@@ -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);
// }
//}

View File

@@ -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;
// }
//}

View 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;
}

View 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;
}
}