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

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
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自定义

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@@ -62,4 +62,18 @@ public class VehicleInfo implements Serializable {
*/
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 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();
}

View File

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

View File

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

View File

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

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