opt:优化示教功能

This commit is contained in:
2025-08-11 13:24:12 +08:00
parent 76c308ba84
commit 1061b07f1e
30 changed files with 860 additions and 160 deletions

View File

@@ -8,6 +8,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
@EnableAsync
@EnableScheduling

View File

@@ -13,6 +13,8 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* <p>
* RCS调用APT控制层

View File

@@ -0,0 +1,42 @@
package org.nl.apt15e.apt.ext.dto;
import lombok.Data;
/**
* @author dsh
* 2025/7/30
* 任务链信息
*/
@Data
public class RcsToAptTaskChainPoDto {
/**
* 车辆 ID
*/
private Long amrId;
/**
* 任务链开始时间
*/
private Long startTime;
/**
* 任务链id
*/
private Long id;
/**
* 区域 ID
*/
private Long areaId;
/**
* 任务链创建时间
*/
private Long createTime;
/**
* 任务链状态 0未执行 1正在执行 2已完成 3取消 4异常 5跳过 6暂停
*/
private Integer status;
}

View File

@@ -14,76 +14,80 @@ import lombok.Data;
@Data
public class RcsToAptTaskDto {
/**
* 任务链 ID
*/
private String taskChainId;
// /**
// * 任务链 ID
// */
// private String taskChainId;
//
// /**
// * 区域 ID
// */
// private String areaId;
//
// /**
// * 任务创建时间
// */
// private String createTime;
//
// /**
// * 任务链开始时间
// */
// private String chainStartTime;
//
// /**
// * 任务链结束时间
// */
// private String chainFinishTime;
//
// /**
// * 车辆 ID
// */
// private String amrId;
//
// /**
// * 任务状态
// * 0-未执行1-子任务正在执行2-子任务已完成3-任务链取消
// * 4-子任务异常5-任务链跳过6-任务链异常7-任务链完成
// */
// private String status;
//
// /**
// * 子任务 ID
// */
// private String taskId;
//
// /**
// * 小车实际动作类型
// */
// private String action;
//
// /**
// * 子任务类型
// */
// private String taskType;
//
// /**
// * 目标点编号
// */
// private String endPointCode;
//
// /**
// * 地图 id
// */
// private String mapId;
//
// /**
// * 小车状态 id
// */
// private String stateId;
//
// /**
// * 小车状态名称
// */
// private String state;
/**
* 区域 ID
*/
private String areaId;
private RcsToAptTaskPo taskPo;
/**
* 任务创建时间
*/
private String createTime;
/**
* 任务链开始时间
*/
private String chainStartTime;
/**
* 任务链结束时间
*/
private String chainFinishTime;
/**
* 车辆 ID
*/
private String amrId;
/**
* 任务状态
* 0-未执行1-子任务正在执行2-子任务已完成3-任务链取消
* 4-子任务异常5-任务链跳过6-任务链异常7-任务链完成
*/
private String status;
/**
* 子任务 ID
*/
private String taskId;
/**
* 小车实际动作类型
*/
private String action;
/**
* 子任务类型
*/
private String taskType;
/**
* 目标点编号
*/
private String endPointCode;
/**
* 地图 id
*/
private String mapId;
/**
* 小车状态 id
*/
private String stateId;
/**
* 小车状态名称
*/
private String state;
private RcsToAptTaskChainPoDto taskChainPo;
}

View File

@@ -0,0 +1,54 @@
package org.nl.apt15e.apt.ext.dto;
import lombok.Data;
/**
* @author dsh
* 2025/7/30
* 单任务信息
*/
@Data
public class RcsToAptTaskPo {
/**
* 动作类型 O0 移动任务 O12 直接上料任务 O13 直接下料任务
*/
private String taskType;
/**
* 小车实际动作类型
*/
private Integer action;
/**
* 开始时间
*/
private Long startTime;
/**
* 任务编号
*/
private Integer id;
/**
* 完成时间
*/
private Long finishTime;
/**
* 目标点编号
*/
private String endPointCode;
/**
* 地图 id
*/
private Long mapId;
/**
* 任务状态
* 0-未执行1-子任务正在执行2-子任务已完成3-任务链取消
* 4-子任务异常5-任务链跳过6-任务链异常7-任务链完成
*/
private Integer status;
}

View File

@@ -0,0 +1,52 @@
package org.nl.apt15e.apt.ext.enums;
import lombok.Data;
import lombok.Getter;
/**
* @author dsh
* 2025/7/30
*/
@Getter
public enum RcsTaskChainStatus {
/**
* 未执行
*/
CREATE("0", "未执行", "未执行"),
/**
* 正在执行
*/
EXECUTING("1", "正在执行", "正在执行"),
/**
* 已完成
*/
FINISHED("2", "已完成", "已完成"),
/**
* 取消
*/
CANCELED("3", "取消", "取消"),
/**
* 异常
*/
UNUSUAL("4", "异常", "异常"),
/**
* 任务链跳过
*/
SKIP("5", "跳过", "跳过"),
/**
* 暂停
*/
PAUSED("6", "暂停", "暂停")
;
RcsTaskChainStatus(String code, String name, String desc) {
this.code = code;
this.name = name;
this.desc = desc;
}
private final String code;
private final String name;
private final String desc;
}

View File

@@ -2,9 +2,13 @@ package org.nl.apt15e.apt.ext.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.nl.apt15e.apt.ext.dto.RcsToAptTaskChainPoDto;
import org.nl.apt15e.apt.ext.dto.RcsToAptTaskDto;
import org.nl.apt15e.apt.ext.dto.RcsToAptTaskPo;
import org.nl.apt15e.apt.ext.enums.RcsTaskChainStatus;
import org.nl.apt15e.apt.ext.enums.RcsTaskStatus;
import org.nl.apt15e.apt.ext.service.RcsToAptService;
import org.nl.apt15e.apt.station.dao.Station;
@@ -43,31 +47,44 @@ public class RcsToAptServiceImpl implements RcsToAptService {
public JSONObject reportTaskInfo(RcsToAptTaskDto dto) {
log.info("RCS上报任务信息输入参数{}", dto.toString());
JSONObject result = new JSONObject();
RcsToAptTaskPo rcsToAptTaskPo = dto.getTaskPo();
RcsToAptTaskChainPoDto rcsToAptTaskChainPoDto = dto.getTaskChainPo();
Task task = iTaskService.getOne(
new QueryWrapper<Task>().lambda()
.eq(Task::getTask_id, dto.getTaskChainId())
.eq(Task::getTask_id, rcsToAptTaskChainPoDto.getId().toString())
);
if (ObjectUtil.isEmpty(task)) {
result.put("receive", "1");
result.put("message", "任务不存在!【"+dto.getTaskChainId()+"");
result.put("message", "任务不存在!【"+rcsToAptTaskChainPoDto.getId()+"");
log.info("RCS上报任务信息输出参数{}", result.toString());
return result;
}
// 更新任务状态
String status = dto.getStatus();
if (status.equals(RcsTaskStatus.SON_EXECUTING.getCode()) || status.equals(RcsTaskStatus.SON_FINISHED.getCode())) {
task.setTask_status(TaskStatus.EXECUTING.getCode());
} else if (status.equals(RcsTaskStatus.TASK_FINISHED.getCode())) {
String taskChainPoStatus = rcsToAptTaskChainPoDto.getStatus().toString();
// if (taskPoStatus.equals(RcsTaskStatus.SON_EXECUTING.getCode()) || taskPoStatus.equals(RcsTaskStatus.SON_FINISHED.getCode())) {
// task.setTask_status(TaskStatus.EXECUTING.getCode());
// } else if (taskChainPoStatus.equals(RcsTaskStatus.TASK_FINISHED.getCode())) {
// task.setTask_status(TaskStatus.FINISHED.getCode());
// }
if (RcsTaskChainStatus.FINISHED.getCode().equals(taskChainPoStatus)) {
task.setTask_status(TaskStatus.FINISHED.getCode());
} else if (RcsTaskChainStatus.EXECUTING.getCode().equals(taskChainPoStatus)) {
task.setTask_status(TaskStatus.EXECUTING.getCode());
} else if (RcsTaskChainStatus.CANCELED.getCode().equals(taskChainPoStatus)) {
task.setTask_status(TaskStatus.CANCELED.getCode());
}
// 更新当前执行点位
String endPointCode = dto.getEndPointCode();
if (ObjectUtil.isNotEmpty(endPointCode)) {
Station staDao = stationService.getById(endPointCode);
task.setTask_point(staDao.getStation_name());
if (ObjectUtil.isNotEmpty(rcsToAptTaskPo)){
String endPointCode = rcsToAptTaskPo.getEndPointCode();
if (ObjectUtil.isNotEmpty(endPointCode)) {
Station staDao = stationService.getOne(new LambdaQueryWrapper<>(Station.class)
.eq(Station::getStation_code, endPointCode));
task.setTask_point(staDao.getStation_name());
}
}
iTaskService.updateById(task);
result.put("receive", "1");

View File

@@ -0,0 +1,26 @@
package org.nl.apt15e.apt.map.dto;
import lombok.Data;
/**
* @author dsh
* 2025/7/25
*/
@Data
public class PointCloudDataDto {
/**
* 点云x坐标
*/
private double x;
/**
* 点云y坐标
*/
private double y;
/**
* 点云强度
*/
private double intensity;
}

View File

@@ -39,6 +39,8 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements IT
JSONObject taskChain = new JSONObject();
taskChain.put("areaId", vehicleInfo.getAreaId());
taskChain.put("amrId", vehicleInfo.getId());
// 1需要RCS回传,0不需要回传
taskChain.put("isReturn", 1);
result.put("taskChain",taskChain);
// 组织站点信息

View File

@@ -37,8 +37,8 @@ public class TeachingController {
}
@PostMapping("/setStation")
private ResponseEntity<Object> setStation(@RequestParam("stationName") String stationName) {
return new ResponseEntity<>(teachingService.setStation(stationName), HttpStatus.OK);
private ResponseEntity<Object> setStation(@RequestParam("stationName") String stationName,@RequestParam("stationCode") String stationCode) {
return new ResponseEntity<>(teachingService.setStation(stationName,stationCode), HttpStatus.OK);
}
@PostMapping("/deployRunMap")
@@ -63,10 +63,10 @@ public class TeachingController {
return new ResponseEntity<>(teachingService.getRunMapZip(mapName), HttpStatus.OK);
}
@PostMapping("/synchronizeMap")
private ResponseEntity<Object> synchronizeMap(@RequestParam("mapName") String mapName) {
return new ResponseEntity<>(teachingService.synchronizeMap(mapName), HttpStatus.OK);
}
// @PostMapping("/synchronizeMap")
// private ResponseEntity<Object> synchronizeMap(@RequestParam("mapName") String mapName) {
// return new ResponseEntity<>(teachingService.synchronizeMap(mapName), HttpStatus.OK);
// }
@PostMapping("/restart")
private ResponseEntity<Object> restart() {
@@ -87,4 +87,19 @@ public class TeachingController {
private ResponseEntity<Object> abandonMapping() {
return new ResponseEntity<>(teachingService.abandonMapping(),HttpStatus.OK);
}
@PostMapping("/startManual")
private ResponseEntity<Object> startManual() {
return new ResponseEntity<>(teachingService.startManual(), HttpStatus.OK);
}
@PostMapping("/stopManual")
private ResponseEntity<Object> stopManual() {
return new ResponseEntity<>(teachingService.stopManual(), HttpStatus.OK);
}
@PostMapping("/getMappingStatus")
private ResponseEntity<Object> getMappingStatus() {
return new ResponseEntity<>(teachingService.getMappingStatus(), HttpStatus.OK);
}
}

View File

@@ -17,12 +17,12 @@ public interface TeachingService {
Map<String, Object> startMapping(String mapName);
/**
* 切手动
* 切手动,可远程控制
*/
Map<String, Object> startManual();
/**
* 切自动
* 停止软手动,关闭远程控制
*/
Map<String, Object> stopManual();
@@ -34,7 +34,7 @@ public interface TeachingService {
/**
* 建图过程中设置站点
*/
Map<String, Object> setStation(String stationName);
Map<String, Object> setStation(String stationName,String stationCode);
/**
* 获取后台地图列表
@@ -65,7 +65,7 @@ public interface TeachingService {
/**
* 同步地图到调度
*/
Map<String, Object> synchronizeMap(String mapName);
Map<String, Object> synchronizeMap(String mapName, byte[] zipFile);
/**
* 重定位
@@ -92,4 +92,9 @@ public interface TeachingService {
* @return
*/
Map<String, Object> abandonMapping();
/**
* 获取后台地图列表
*/
Map<String,String> getMappingStatus();
}

View File

@@ -16,15 +16,20 @@ import org.nl.apt15e.apt.teaching.service.TeachingService;
import org.nl.apt15e.apt.vehicle.ProcessZip;
import org.nl.apt15e.apt.vehicle.service.impl.VehicleInfoServiceImpl;
import org.nl.apt15e.common.BadRequestException;
import org.nl.apt15e.config.thread.ProtobufWebSocketHandler;
import org.nl.apt15e.util.HTTPUtil;
import org.nl.apt15e.util.URLConstant;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;
import static org.nl.apt15e.util.URLConstant.RCS_IP_PORT;
/**
* @author dsh
* 2025/7/3
@@ -39,16 +44,28 @@ public class TeachingServiceImpl implements TeachingService {
@Resource
private StationService stationService;
/**
* 建图状态和进度条
*/
public static Map<String,String> teachingMappingStatus = new HashMap<>();
/**
* 打点 站点编号:站点名称
*/
public static Map<String,String> teachingStationMap = new HashMap<>();
@Override
public Map<String, Object> startMapping(String mapName) {
if (StrUtil.isBlank(mapName)){
throw new BadRequestException("mapName is empty");
}
// 开始建图时清除一下 打点记录
teachingStationMap.clear();
JSONObject params = new JSONObject();
params.put("name", mapName);
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/startMapping", params);
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/startMapping", params);
} catch (Exception e) {
log.info("访问车体开始建图接口报错:{}",e.getMessage());
throw new BadRequestException("开始建图失败");
@@ -59,7 +76,7 @@ public class TeachingServiceImpl implements TeachingService {
JSONObject body = JSON.parseObject(response.body());
log.info("开始建图:{}",body);
if ("200".equals(body.getString("code"))){
body =(JSONObject) this.startManual();
// body =(JSONObject) this.startManual();
body.put("message","开始建图成功");
return body;
}
@@ -73,7 +90,7 @@ public class TeachingServiceImpl implements TeachingService {
public Map<String, Object> startManual() {
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/startManual", new JSONObject());
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/startManual", new JSONObject());
} catch (Exception e) {
log.info("访问车体切手动接口报错:{}",e.getMessage());
throw new BadRequestException("切手动失败");
@@ -96,7 +113,7 @@ public class TeachingServiceImpl implements TeachingService {
public Map<String, Object> stopManual() {
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/stopManual", new JSONObject());
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/stopManual", new JSONObject());
} catch (Exception e) {
log.info("访问车体切自动接口报错:{}",e.getMessage());
throw new BadRequestException("切自动失败");
@@ -119,7 +136,7 @@ public class TeachingServiceImpl implements TeachingService {
public Map<String, Object> stopMapping() {
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/stopMapping", new JSONObject());
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/stopMapping", new JSONObject());
} catch (Exception e) {
log.info("访问车体结束建图接口报错:{}",e.getMessage());
throw new BadRequestException("结束建图失败");
@@ -130,7 +147,7 @@ public class TeachingServiceImpl implements TeachingService {
JSONObject body = JSON.parseObject(response.body());
log.info("结束建图:{}",body);
if ("200".equals(body.getString("code"))){
body =(JSONObject) this.stopManual();
// body =(JSONObject) this.stopManual();
body.put("message","结束建图成功");
return body;
}
@@ -140,15 +157,17 @@ public class TeachingServiceImpl implements TeachingService {
}
@Override
public Map<String, Object> setStation(String stationName) {
if (StrUtil.isBlank(stationName)){
public Map<String, Object> setStation(String stationName,String stationCode) {
if (StrUtil.isBlank(stationCode) || StrUtil.isBlank(stationName)){
throw new BadRequestException("spotCode is empty");
}
// 记录打点时站点数据
teachingStationMap.put(stationCode,stationName);
JSONObject params = new JSONObject();
params.put("spotCode", stationName);
params.put("spotCode", stationCode);
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/setStates", params);
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/setStates", params);
} catch (Exception e) {
log.info("访问车体设置站点接口报错:{}",e.getMessage());
throw new BadRequestException("设置站点失败");
@@ -171,7 +190,7 @@ public class TeachingServiceImpl implements TeachingService {
public JSONObject getLocalMaps() {
HttpResponse response = null;
try {
response = HTTPUtil.get("http://192.168.100.82:9998","/tool/editor/getLocalMaps", new JSONObject());
response = HTTPUtil.get(URLConstant.VEHICLE_IP_PORT,"/tool/editor/getLocalMaps", new JSONObject());
} catch (Exception e) {
log.info("访问车体地图列表接口报错:{}",e.getMessage());
throw new BadRequestException("获取地图列表失败");
@@ -199,7 +218,7 @@ public class TeachingServiceImpl implements TeachingService {
params.put("id", mapName);
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/deployRunMap", params);
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/deployRunMap", params);
} catch (Exception e) {
log.info("访问车体部署地图接口报错:{}",e.getMessage());
throw new BadRequestException("部署地图失败");
@@ -227,7 +246,7 @@ public class TeachingServiceImpl implements TeachingService {
params.put("name", mapName);
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/changeCurrentRunMap", params);
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/changeCurrentRunMap", params);
} catch (Exception e) {
log.info("访问车体应用地图接口报错:{}",e.getMessage());
throw new BadRequestException("应用地图失败");
@@ -255,7 +274,7 @@ public class TeachingServiceImpl implements TeachingService {
params.put("name", mapName);
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/getRunMapZip", params);
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/getRunMapZip", params);
} catch (Exception e) {
log.info("访问车体地图包接口报错:{}",e.getMessage());
throw new BadRequestException("获取地图包失败");
@@ -275,12 +294,11 @@ public class TeachingServiceImpl implements TeachingService {
}
@Override
public Map<String, Object> synchronizeMap(String mapName) {
byte[] zipFile = this.getRunMapZip(mapName);
public Map<String, Object> synchronizeMap(String mapName,byte[] zipFile) {
HttpResponse response = null;
try {
response = HttpRequest.post("http://192.168.100.82:8081/map/uploadFile")
response = HttpRequest.post(URLConstant.RCS_IP_PORT+"/map/uploadFile")
.setConnectionTimeout(3000)
.setReadTimeout(3000)
.header("token", "admin123")
@@ -325,7 +343,7 @@ public class TeachingServiceImpl implements TeachingService {
log.info(params.getString("angle"));
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/relocate", params);
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/relocate", params);
} catch (Exception e) {
log.info("访问车体重定位接口报错:{}",e.getMessage());
throw new BadRequestException("重定位指令下发失败");
@@ -348,7 +366,7 @@ public class TeachingServiceImpl implements TeachingService {
public Map<String, Object> restart() {
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/restart", new JSONObject());
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/restart", new JSONObject());
} catch (Exception e) {
log.info("访问车体程序重启接口报错:{}",e.getMessage());
throw new BadRequestException("车体程序重启失败");
@@ -373,42 +391,35 @@ public class TeachingServiceImpl implements TeachingService {
throw new BadRequestException("mapName is empty");
}
JSONObject response = new JSONObject();
// 获取地图列表
this.getLocalMaps();
// 解析地图数据
byte[] zipFile = this.getRunMapZip(mapName);
// 部署地图
this.deployRunMap(mapName);
// 应用地图
this.changeCurrentRunMap(mapName);
// 解析地图包并同步到调度
this.synchronizeMap(mapName);
this.synchronizeMap(mapName,zipFile);
// 重启本体程序
this.restart();
// 重定位,需要等待一会
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
while (!"1".equals(VehicleInfoServiceImpl.vehicleInfo.getReady())){
log.info("建图 重启本体程序中,还未接收到本体重启信号");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
log.info("接收到本体重启信号,发送重定位指令");
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;
}
@@ -423,7 +434,7 @@ public class TeachingServiceImpl implements TeachingService {
params.put("attach", JSON.parseObject(attach));
HttpResponse response = null;
try {
response = HTTPUtil.post("http://192.168.100.82:9998","/tool/rob/sendCMD", params);
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/sendCMD", params);
} catch (Exception e) {
log.info("访问车体放弃示教建图接口报错:{}",e.getMessage());
throw new BadRequestException("车体放弃示教建图失败");
@@ -441,4 +452,9 @@ public class TeachingServiceImpl implements TeachingService {
log.info("放弃示教建图失败");
throw new BadRequestException("放弃示教建图失败");
}
@Override
public Map<String, String> getMappingStatus() {
return teachingMappingStatus;
}
}

View File

@@ -1,6 +1,7 @@
package org.nl.apt15e.apt.vehicle;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.nl.apt15e.apt.map.dao.MapInfo;
@@ -12,6 +13,7 @@ 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.apt.teaching.service.impl.TeachingServiceImpl;
import org.nl.apt15e.common.BadRequestException;
import org.nl.apt15e.config.file.FileProperties;
import org.springframework.beans.factory.annotation.Autowired;
@@ -64,7 +66,7 @@ public class ProcessZip {
MapInfo mapInfo = new MapInfo();
//存储之前先清除站点表中数据和路径表中的数据
//存储之前先清除站点表、路径表、地图信息表的数据
stationService.remove(new LambdaQueryWrapper<>());
routeInfoService.remove(new LambdaQueryWrapper<>());
mapInfoService.remove(new LambdaQueryWrapper<>());
@@ -269,8 +271,21 @@ public class ProcessZip {
System.out.printf("行号 %d - 有效点位数据: %s%n", lineNumber, Arrays.toString(processData));
Station station = new Station();
station.setStation_id(Integer.valueOf(processData[1]));
String stationName = "";
if ("A".equals(processData[2])){
stationName = "起点";
}else if ("C".equals(processData[2])){
stationName = "终点";
}else {
// 判断是否存在打点记录里
if (TeachingServiceImpl.teachingStationMap.containsKey(processData[2])){
stationName = TeachingServiceImpl.teachingStationMap.get(processData[2]);
}else {
stationName = processData[2];
}
}
station.setStation_code(processData[2]);
station.setStation_name(processData[2]);
station.setStation_name(stationName);
station.setX(Double.valueOf(processData[5]));
station.setY(Double.valueOf(processData[6]));
station.setAngle(Double.valueOf(processData[7]));

View File

@@ -55,7 +55,7 @@ public class VehicleInfo implements Serializable {
/**
* 电量
*/
private Double batteryPower;
private Integer batteryPower;
/**
* 异常信息
@@ -65,20 +65,35 @@ public class VehicleInfo implements Serializable {
/**
* 车辆x坐标
*/
private Double x;
private double x;
/**
* 车辆y坐标
*/
private Double y;
private double y;
/**
* 车辆角度
*/
private Double theta;
private double theta;
/**
* 是否是手动模式
*/
private Boolean isManual;
/**
* 车辆当前执行的任务链
*/
private String task_seq;
/**
* 车辆任务链中当前点位
*/
private String task_point;
/**
* 是否重启完成 1重启完成
*/
private String ready;
}

View File

@@ -27,4 +27,10 @@ public class VehicleInfoController {
public VehicleInfo getVehicleInfo() {
return vehicleInfoService.getVehicleInfo();
}
@GetMapping("/rebootVehicle")
@Log("重启车辆系统")
public void rebootVehicle() {
vehicleInfoService.rebootVehicle();
}
}

View File

@@ -12,4 +12,6 @@ public interface VehicleInfoService {
void setVehicleInfo(VehicleInfo vehicleInfo);
void rebootVehicle();
}

View File

@@ -1,22 +1,36 @@
package org.nl.apt15e.apt.vehicle.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import comm_protocol.Robottype;
import lombok.extern.slf4j.Slf4j;
import org.nl.apt15e.apt.map.dto.PointCloudDataDto;
import org.nl.apt15e.apt.task.enums.TaskStatus;
import org.nl.apt15e.apt.task.service.ITaskService;
import org.nl.apt15e.apt.task.service.dao.Task;
import org.nl.apt15e.apt.task.service.dao.mapper.TaskMapper;
import org.nl.apt15e.apt.task.service.impl.TaskServiceImpl;
import org.nl.apt15e.apt.vehicle.dao.VehicleException;
import org.nl.apt15e.apt.vehicle.dao.VehicleInfo;
import org.nl.apt15e.apt.vehicle.service.VehicleInfoService;
import org.nl.apt15e.apt.websocket.WebSocketPointCloudDataServer;
import org.nl.apt15e.apt.websocket.WebSocketVehicleServer;
import org.nl.apt15e.common.BadRequestException;
import org.nl.apt15e.system.enums.ParamCodeConstant;
import org.nl.apt15e.system.service.ParamService;
import org.nl.apt15e.util.HTTPUtil;
import org.nl.apt15e.util.URLConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@@ -30,8 +44,16 @@ public class VehicleInfoServiceImpl implements VehicleInfoService {
@Resource
private TaskScheduler scheduler;
@Resource
private TaskMapper taskMapper;
@Resource
private ParamService paramService;
public static VehicleInfo vehicleInfo = new VehicleInfo();
public static Set<PointCloudDataDto> pointCloudData = new HashSet<>();
@Override
public VehicleInfo getVehicleInfo() {
return vehicleInfo;
@@ -42,11 +64,22 @@ public class VehicleInfoServiceImpl implements VehicleInfoService {
}
@Override
public void rebootVehicle() {
HttpResponse response = null;
try {
response = HTTPUtil.post(URLConstant.VEHICLE_IP_PORT,"/tool/rob/reboot", new JSONObject());
} catch (Exception e) {
log.info("重启系统失败:{}",e.getMessage());
throw new BadRequestException("重启系统失败");
}
}
@Async("asynchronousTasks")
public void queryVehicleInfo() {
HttpResponse response = null;
try {
response = HTTPUtil.get("http://192.168.100.82:8081","/amr/onlineAmr",null);
response = HTTPUtil.get(URLConstant.RCS_IP_PORT,"/amr/onlineAmr",null);
// 检查响应状态码
if (response!=null && response.isOk()) {
// 获取响应体内容
@@ -86,6 +119,14 @@ public class VehicleInfoServiceImpl implements VehicleInfoService {
CopyOnWriteArraySet<WebSocketVehicleServer> webSocketSet =
WebSocketVehicleServer.getWebSocketSet();
if (webSocketSet.size() > 0) {
Task task = taskMapper.selectOne(new LambdaQueryWrapper<>(Task.class).eq(Task::getTask_status, TaskStatus.CREATE.getCode()).or().eq(Task::getTask_status, TaskStatus.EXECUTING.getCode()));
if (ObjectUtil.isNotEmpty(task)){
vehicleInfo.setTask_seq(task.getTask_seq());
vehicleInfo.setTask_point(task.getTask_point());
}else {
vehicleInfo.setTask_seq("");
vehicleInfo.setTask_point("");
}
webSocketSet.forEach(c -> {
Map<String, Object> vehicleInfoMap = new HashMap<>();
vehicleInfoMap.put("data", vehicleInfo);
@@ -94,9 +135,26 @@ public class VehicleInfoServiceImpl implements VehicleInfoService {
}
}
@Async("asynchronousTasks")
public void sendPointCloudData() {
CopyOnWriteArraySet<WebSocketPointCloudDataServer> webSocketSet =
WebSocketPointCloudDataServer.getWebSocketSet();
if (webSocketSet.size() > 0) {
webSocketSet.forEach(c -> {
Map<String, Object> vehicleInfoMap = new HashMap<>();
vehicleInfoMap.put("data", pointCloudData);
c.sendDataToClient(vehicleInfoMap);
});
}
}
@PostConstruct
public void init() {
scheduler.scheduleAtFixedRate(this::queryVehicleInfo,4000);
scheduler.scheduleAtFixedRate(this::sendVehicleInfo, 4000);
// 初始化车辆IP和调度IP
URLConstant.RCS_IP_PORT = paramService.queryParamObjectByCode(ParamCodeConstant.RCS_URL).getValue();
URLConstant.VEHICLE_IP_PORT = paramService.queryParamObjectByCode(ParamCodeConstant.VEHICLE_URL).getValue();
scheduler.scheduleAtFixedRate(this::queryVehicleInfo,1500);
scheduler.scheduleAtFixedRate(this::sendVehicleInfo, 2000);
scheduler.scheduleAtFixedRate(this::sendPointCloudData, 1000);
}
}

View File

@@ -0,0 +1,126 @@
package org.nl.apt15e.apt.websocket;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @author dsh
* 2025/7/25
*/
@Slf4j
@ServerEndpoint("/webSocket/PointCloudData/{sid}")
@Component
public class WebSocketPointCloudDataServer {
/**
* concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象。
*/
private static CopyOnWriteArraySet<WebSocketPointCloudDataServer> webSocketSet = new CopyOnWriteArraySet<>();
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
*/
private static int onlineCount = 0;
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收userId
*/
private String sid = "";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
this.session = session;
//如果存在就先删除一个,防止重复推送消息
webSocketSet.removeIf(webSocket -> webSocket.sid.equals(sid));
webSocketSet.add(this);
//在线数加1
addOnlineCount();
log.info("PointCloudDataWS:sid{}连接成功,当前在线人数为{}", sid, getOnlineCount());
this.sid = sid;
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this);
//在线数减1
subOnlineCount();
log.info("PointCloudDataWS:sid{}关闭连接!当前在线人数为{}", sid, getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
//System.out.println(webSocketSet.size() + "_接收到消息_" + session.getId());
}
@OnError
public void onError(Session session, Throwable error) {
//log.error("发生错误");
webSocketSet.remove(session);
error.printStackTrace();
}
public Session getSession() {
return session;
}
// 发送消息,在定时任务中会调用此方法
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public void sendDataToClient(Map<String, Object> data) {
try {
if (this.session != null&& MapUtil.isNotEmpty(data)) {
this.session.getBasicRemote().sendText(JSON.toJSONString(data));
}
} catch (IOException e) {
log.error("发送消息给客户端失败", e);
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static CopyOnWriteArraySet<WebSocketPointCloudDataServer> getWebSocketSet() {
return webSocketSet;
}
public void setSession(Session session) {
this.session = session;
}
public static synchronized void addOnlineCount() {
WebSocketPointCloudDataServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketPointCloudDataServer.onlineCount--;
}
}

View File

@@ -1,8 +1,11 @@
package org.nl.apt15e.config.thread;
import com.alibaba.fastjson.JSONObject;
import comm_protocol.Robottype;
import comm_protocol.Uidata;
import lombok.extern.slf4j.Slf4j;
import org.nl.apt15e.apt.map.dto.PointCloudDataDto;
import org.nl.apt15e.apt.teaching.service.impl.TeachingServiceImpl;
import org.nl.apt15e.apt.vehicle.service.impl.VehicleInfoServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -15,10 +18,11 @@ import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.WebSocketConnectionManager;
import org.springframework.web.socket.handler.BinaryWebSocketHandler;
import javax.annotation.Resource;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.HashSet;
import java.util.Set;
import static comm_protocol.Uidata.UiDatagram.ItemCase.*;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
/**
* @author dsh
@@ -29,7 +33,7 @@ import static comm_protocol.Uidata.UiDatagram.ItemCase.*;
@EnableScheduling
public class ProtobufWebSocketHandler extends BinaryWebSocketHandler {
private static Boolean isConnected = false;
public static Boolean isConnected = false;
@Autowired
private WebSocketConnectionManager connectionManager;
private WebSocketSession currentSession;
@@ -45,8 +49,16 @@ public class ProtobufWebSocketHandler extends BinaryWebSocketHandler {
switch (datagram.getItemCase()) {
case ROBOT_BASE:
Robottype.RobotBase robotBase = datagram.getRobotBase();
JSONObject data_pool = JSONObject.parseObject(robotBase.getDataPool());
String mapping_return = data_pool.getString("mapping_return");
String mapping_percent = data_pool.getString("mapping_percent");
String ready = data_pool.getString("ready");
TeachingServiceImpl.teachingMappingStatus.put("mapping_return", mapping_return);
TeachingServiceImpl.teachingMappingStatus.put("mapping_percent", mapping_percent);
// log.info("建图状态mapping_return:{}建图进度条mapping_percent:{},teachingMappingStatus:{}", mapping_return, mapping_percent,TeachingServiceImpl.teachingMappingStatus);
VehicleInfoServiceImpl.vehicleInfo.setReady(ready);
// 电量
VehicleInfoServiceImpl.vehicleInfo.setBatteryPower(robotBase.getBatteryInfo().getSoc());
VehicleInfoServiceImpl.vehicleInfo.setBatteryPower((int)robotBase.getBatteryInfo().getSoc());
//xytheta
VehicleInfoServiceImpl.vehicleInfo.setX(robotBase.getLocate().getCurrentState().getX());
VehicleInfoServiceImpl.vehicleInfo.setY(robotBase.getLocate().getCurrentState().getY());
@@ -62,8 +74,20 @@ public class ProtobufWebSocketHandler extends BinaryWebSocketHandler {
break;
case LASER_SCAN:
// Robottype.LaserData laser = datagram.getLaserScan();
// System.out.println("Received laser_scan: " + laser.getScanList());
Robottype.LaserData laser = datagram.getLaserScan();
Set<PointCloudDataDto> data = new HashSet<>();
for (Robottype.Point point: laser.getScanList()){
double cosYaw = cos(VehicleInfoServiceImpl.vehicleInfo.getTheta());
double sinYaw = sin(VehicleInfoServiceImpl.vehicleInfo.getTheta());
double x_global = VehicleInfoServiceImpl.vehicleInfo.getX() + cosYaw * point.getX() - sinYaw * point.getY();
double y_global = VehicleInfoServiceImpl.vehicleInfo.getY() + sinYaw * point.getX() + cosYaw * point.getY();
PointCloudDataDto pointCloudDataDto = new PointCloudDataDto();
pointCloudDataDto.setX(x_global);
pointCloudDataDto.setY(y_global);
data.add(pointCloudDataDto);
}
VehicleInfoServiceImpl.pointCloudData = data;
// System.out.println("Received laser_scan: " + list);
break;
}
} catch (Exception e) {
@@ -92,8 +116,8 @@ public class ProtobufWebSocketHandler extends BinaryWebSocketHandler {
currentSession = null;
}
@Scheduled(fixedDelay = 5000)
public synchronized void reconnectTask() {
@Scheduled(fixedDelay = 3000)
public void reconnectTask() {
if (!isConnected) {
try {
log.info("websocket 正在进行重连..");

View File

@@ -1,6 +1,7 @@
package org.nl.apt15e.config.thread;
import lombok.extern.slf4j.Slf4j;
import org.nl.apt15e.util.URLConstant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -37,7 +38,7 @@ public class WebsocketClientConfig {
@Bean
public WebSocketConnectionManager webSocketConnectionManager() {
String serverUrl = "ws://192.168.100.82:9998/ws/AGVInfo";
String serverUrl = "ws://"+ URLConstant.VEHICLE_IP_PORT+"/ws/AGVInfo";
WebSocketConnectionManager manager = new WebSocketConnectionManager(
this.webSocketClient(),

View File

@@ -0,0 +1,29 @@
package org.nl.apt15e.system.controller;
import org.nl.apt15e.system.dao.Param;
import org.nl.apt15e.system.service.ParamService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @author dsh
* 2025/8/7
*/
@RestController
@RequestMapping("/param")
public class ParamController {
@Resource
private ParamService paramService;
@GetMapping("/queryParamObjectByCode")
public Param queryParamObjectByCode(@RequestParam("code") String code){
return paramService.queryParamObjectByCode(code);
}
@PostMapping("/updateByCode")
public Boolean updateByCode(@RequestBody Param param){
return paramService.updateByCode(param);
}
}

View File

@@ -0,0 +1,93 @@
package org.nl.apt15e.system.dao;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* @author dsh
* 2025/8/4
*/
@Data
@TableName("sys_param")
public class Param implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 标识
*/
@TableId
private String id;
/**
* 编码
*/
private String code;
/**
* 名称
*/
private String name;
/**
* 中文名称
*/
private String zh_name;
/**
* us名称
*/
private String en_name;
/**
* 值
*/
private String value;
/**
* 备注
*/
private String remark;
/**
* 是否启用
*/
private Boolean is_active;
/**
* 是否删除
*/
private Boolean is_delete;
/**
* 创建者ID
*/
private String create_id;
/**
* 创建者
*/
private String create_name;
/**
* 创建时间
*/
private String create_time;
/**
* 修改者ID
*/
private String update_id;
/**
* 修改者
*/
private String update_name;
/**
* 修改时间
*/
private String update_time;
}

View File

@@ -0,0 +1,13 @@
package org.nl.apt15e.system.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.nl.apt15e.system.dao.Param;
/**
* @author dsh
* 2025/8/7
*/
@Mapper
public interface ParamMapper extends BaseMapper<Param> {
}

View File

@@ -0,0 +1,12 @@
package org.nl.apt15e.system.enums;
/**
* @author dsh
* 2025/8/7
*/
public class ParamCodeConstant {
public static final String RCS_URL = "rcs_url";
public static final String VEHICLE_URL = "vehicle_url";
}

View File

@@ -0,0 +1,15 @@
package org.nl.apt15e.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.nl.apt15e.system.dao.Param;
/**
* @author dsh
* 2025/8/7
*/
public interface ParamService extends IService<Param> {
Param queryParamObjectByCode(String code);
Boolean updateByCode(Param param);
}

View File

@@ -0,0 +1,42 @@
package org.nl.apt15e.system.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.nl.apt15e.common.BadRequestException;
import org.nl.apt15e.system.dao.Param;
import org.nl.apt15e.system.dao.mapper.ParamMapper;
import org.nl.apt15e.system.service.ParamService;
import org.springframework.stereotype.Service;
/**
* @author dsh
* 2025/8/7
*/
@Service
public class ParamServiceImpl extends ServiceImpl<ParamMapper, Param> implements ParamService {
@Override
public Param queryParamObjectByCode(String code) {
if (StrUtil.isBlank(code)) {
throw new BadRequestException("code is Null");
}
return this.getOne(new LambdaQueryWrapper<>(Param.class).eq(Param::getCode, code));
}
@Override
public Boolean updateByCode(Param param) {
if(ObjectUtil.isNull(param)){
throw new BadRequestException("参数不能为空");
}
boolean results = this.update(new LambdaUpdateWrapper<>(Param.class)
.set(StrUtil.isNotBlank(param.getName()),Param::getName,param.getName())
.eq(Param::getCode,param.getCode()));
return results;
}
}

View File

@@ -15,8 +15,8 @@ public class HTTPUtil {
public static HttpResponse post(String url, String method, JSONObject params)throws Exception{
String sendUrl = url + method;
return HttpRequest.post(sendUrl)
.setConnectionTimeout(3000)
.setReadTimeout(3000)
.setConnectionTimeout(10000)
.setReadTimeout(10000)
.header("token", "admin123")
.header("name", "lx-script")
.header("Content-Type", "application/json")
@@ -28,8 +28,8 @@ public class HTTPUtil {
public static HttpResponse get(String url, String method, JSONObject params)throws Exception{
String sendUrl = url + method;
return HttpRequest.get(sendUrl)
.setConnectionTimeout(3000)
.setReadTimeout(3000)
.setConnectionTimeout(10000)
.setReadTimeout(10000)
.header("token", "admin123")
.header("name", "lx-script")
.header("Content-Type", "application/json")

View File

@@ -13,7 +13,12 @@ public class URLConstant {
/**
* RCS调度IP及端口
*/
public final static String RCS_IP_PORT = "192.168.100.82:8081";
public static String RCS_IP_PORT = "192.168.100.82:8081";
/**
* 车体后台IP及端口
*/
public static String VEHICLE_IP_PORT = "192.168.100.82:9998";
/**
* 下发任务URL

View File

@@ -1,12 +1,12 @@
server:
# 端口
port: 8081
port: 8011
spring:
datasource:
druid:
db-type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/apt_data?serverTimezone=GMT%2B8&characterEncoding=utf-8&userSSL=false
url: jdbc:mysql://192.168.100.201:3307/nl_apt15e?serverTimezone=GMT%2B8&characterEncoding=utf-8&userSSL=false
username: root
password: 123456
# 初始连接数
@@ -57,8 +57,8 @@ file:
path: ~/file/
avatar: ~/avatar/
linux:
path: /home/eladmin/file/
avatar: /home/eladmin/avatar/
path: /home/fr1511b/nlapt15e/file/
avatar: /home/fr1511b/nlapt15e/avatar/
windows:
path: C:\eladmin\file\
avatar: C:\eladmin\avatar\