| | |
| | | package com.zy.acs.manager.core.service; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.zy.acs.common.enums.AgvDirectionType; |
| | | import com.zy.acs.common.enums.ActuatorDirectionType; |
| | | import com.zy.acs.framework.common.Cools; |
| | | import com.zy.acs.framework.exception.CoolException; |
| | | import com.zy.acs.manager.core.constant.MapDataConstant; |
| | | import com.zy.acs.manager.core.domain.DirectionDto; |
| | | import com.zy.acs.manager.core.domain.LaneDto; |
| | | import com.zy.acs.manager.core.domain.SortCodeDto; |
| | | import com.zy.acs.manager.core.domain.UnlockPathTask; |
| | | import com.zy.acs.manager.core.domain.VehicleFootprint; |
| | | import com.zy.acs.manager.core.service.astart.*; |
| | | import com.zy.acs.manager.core.service.astart.domain.AStarNavigateNode; |
| | | import com.zy.acs.manager.core.service.astart.domain.DynamicNode; |
| | |
| | | import com.zy.acs.manager.manager.entity.Segment; |
| | | import com.zy.acs.manager.manager.entity.Sta; |
| | | import com.zy.acs.manager.manager.enums.CodeSpinType; |
| | | import com.zy.acs.manager.manager.service.ActionService; |
| | | import com.zy.acs.manager.manager.service.CodeService; |
| | | import com.zy.acs.manager.system.service.ConfigService; |
| | | import com.zy.acs.manager.manager.service.LaneService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | |
| | | * Created by vincent on 2023/6/14 |
| | | */ |
| | | @Slf4j |
| | | @Component("mapService") |
| | | @Component |
| | | public class MapService { |
| | | |
| | | private static final double EPS = 1e-7; |
| | |
| | | @Autowired |
| | | private AStarNavigateService aStarNavigateService; |
| | | @Autowired |
| | | private ConfigService configService; |
| | | private LaneService laneService; |
| | | @Autowired |
| | | private ActionService actionService; |
| | | private LaneBuilder laneBuilder; |
| | | @Autowired |
| | | private LinkedBlockingQueue<UnlockPathTask> unlockTaskQueue; |
| | | |
| | |
| | | return angle; |
| | | } |
| | | |
| | | // 坐标货架阈值 todo:luxiaotao |
| | | public AgvDirectionType calculateAgvWorkDirectionByShelf(Loc loc, Code code) { |
| | | Integer compDirect = loc.getCompDirect(); |
| | | return AgvDirectionType.fromVal(compDirect); |
| | | // 坐标货架阈值 |
| | | public ActuatorDirectionType calculateAgvWorkDirectionByShelf(Loc loc, Code code) { |
| | | Integer compDirect = loc.getCompDirect(); |
| | | return ActuatorDirectionType.fromVal(compDirect); |
| | | } |
| | | |
| | | public Double getStaAngle(Sta sta, Double workDirection) { |
| | | if (null == sta) { |
| | | return null; |
| | | public Double getStaAngle(Sta sta) { |
| | | if (null == sta.getCode()) { |
| | | throw new CoolException(sta.getStaNo() + "号接驳站未设置地码!"); |
| | | } |
| | | if (Cools.isEmpty(sta.getAngle())) { |
| | | return workDirection; |
| | | Code code = codeService.getCacheById(sta.getCode()); |
| | | if (code.getCornerBool()) { |
| | | if (!Cools.isEmpty(sta.getAngle())) { |
| | | return Double.parseDouble(sta.getAngle()); |
| | | } else { |
| | | throw new CoolException(sta.getStaNo() + "号接驳站未设置车体作业角度!"); |
| | | } |
| | | } else { |
| | | LaneDto laneDto = laneBuilder.search(code.getData()); |
| | | Double laneDir = laneService.getLaneDirection(laneDto); |
| | | |
| | | if (!Cools.isEmpty(laneDir)) { |
| | | if (!Cools.isEmpty(sta.getAngle()) && !laneDir.equals(Double.parseDouble(sta.getAngle()))) { |
| | | throw new CoolException(sta.getStaNo() + "号接驳站车体作业角度与巷道进入角度不一致!"); |
| | | } |
| | | return laneDir; |
| | | } else { |
| | | if (!Cools.isEmpty(sta.getAngle())) { |
| | | return Double.parseDouble(sta.getAngle()); |
| | | } |
| | | throw new CoolException("未设置" + sta.getStaNo() + "号接驳站所处巷道的进入角度"); |
| | | } |
| | | } |
| | | return Double.parseDouble(sta.getAngle()); |
| | | } |
| | | |
| | | public Double calculateAgvWorkDirectionByStation(Double staWorkDirection, Double lastDirection) { |
| | |
| | | return includeList; |
| | | } |
| | | |
| | | public List<NavigateNode> getWaveScopeByCode(Integer lev, String code, VehicleFootprint footprint, double headingRad, double buffer) { |
| | | int[] centerIdx = mapDataDispatcher.getCodeMatrixIdx(lev, code); |
| | | if (centerIdx == null) { |
| | | throw new CoolException(code + " does not exist in codeMatrix"); |
| | | } |
| | | Double[][][] cdaMatrix = mapDataDispatcher.getCdaMatrix(lev); |
| | | Double centerX = cdaMatrix[centerIdx[0]][centerIdx[1]][0]; |
| | | Double centerY = cdaMatrix[centerIdx[0]][centerIdx[1]][1]; |
| | | if (centerX == null || centerY == null) { |
| | | throw new CoolException(code + " does not exist in cdaMatrix"); |
| | | } |
| | | |
| | | double searchRadius = footprint.maxExtent() + buffer; |
| | | |
| | | List<NavigateNode> candidates = this.getWaveScopeByCode(lev, code, searchRadius); |
| | | |
| | | List<NavigateNode> includeList = new ArrayList<>(); |
| | | for (NavigateNode node : candidates) { |
| | | Double px = cdaMatrix[node.getX()][node.getY()][0]; |
| | | Double py = cdaMatrix[node.getX()][node.getY()][1]; |
| | | |
| | | if (isInsideExpandedFootprint(px, py, centerX, centerY, footprint, headingRad, buffer)) { |
| | | includeList.add(node); |
| | | } |
| | | |
| | | } |
| | | return includeList; |
| | | } |
| | | |
| | | public void spreadWaveNode(NavigateNode originNode, NavigateNode currNode |
| | | , String[][] codeMatrix, Double[][][] cdaMatrix, Double radiusLenSquared |
| | | , List<NavigateNode> includeList, Set<NavigateNode> existNodes) { |
| | |
| | | |
| | | } |
| | | |
| | | private boolean isInsideExpandedFootprint(double px, double py, double centerX, double centerY |
| | | , VehicleFootprint footprint, double headingRad, double buffer) { |
| | | // 一、重置成以车辆为原点且车头为x、y正方向的坐标系 |
| | | // 向量平移,地图坐标系转到以车辆中心为原点的坐标系,使车辆中心变成新的原点(方便计算) |
| | | double dx = px - centerX; |
| | | double dy = py - centerY; |
| | | |
| | | // 再把坐标旋转到车辆坐标系(localX 指向车头,localY 指向车辆左侧),取消原地图的车辆旋转方向 |
| | | double localX = dx * Math.cos(headingRad) + dy * Math.sin(headingRad); |
| | | double localY = -dx * Math.sin(headingRad) + dy * Math.cos(headingRad); |
| | | |
| | | // 二、车辆在自身坐标系中的矩形范围 |
| | | double xMax = footprint.getHead(); // localX方向, 向前(车头方向)是正值 |
| | | double xMin = -footprint.getTail(); // localX方向, 向后(车尾)是负值 |
| | | |
| | | double yMax = footprint.getHalfWidth(); // localY 方向, 左侧为正 |
| | | double yMin = -footprint.getHalfWidth(); // localY 方向, 右侧为负 |
| | | |
| | | // 三、计算 点 是否在 “矩形外扩 + buffer 的区域”里 |
| | | // 计算点距离 矩形边界 的‘超出量’,落在矩形内时超出量为 0 |
| | | double excessX = 0D; // 默认 0 表示还在矩形里面 |
| | | if (localX < xMin) { |
| | | excessX = xMin - localX; |
| | | } else if (localX > xMax) { |
| | | excessX = localX - xMax; |
| | | } |
| | | |
| | | double excessY = 0D; // 默认 0 表示还在矩形里面 |
| | | if (localY < yMin) { |
| | | excessY = yMin - localY; |
| | | } else if (localY > yMax) { |
| | | excessY = localY - yMax; |
| | | } |
| | | |
| | | // (excessX, excessY) 表示距离向量 |
| | | |
| | | // 通过 欧氏距离平方算法, 若到矩形的最短距离不超过 buffer,说明在“车辆形状 + 缓冲半径”内 |
| | | return excessX * excessX + excessY * excessY <= buffer * buffer; |
| | | } |
| | | |
| | | // v2 BFS ------------------------------------------------------------------------------ |
| | | // public List<NavigateNode> getWaveScopeByCode0(Integer lev, String code, Double radiusLen) { |
| | | // String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(lev); |