package com.zy.acs.manager.core.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.zy.acs.common.utils.Utils; import com.zy.acs.framework.exception.CoolException; import com.zy.acs.manager.common.config.RedisProperties; import com.zy.acs.manager.common.utils.MapDataUtils; import com.zy.acs.manager.core.domain.PathDto; import com.zy.acs.manager.core.domain.VehicleFootprint; import com.zy.acs.manager.core.service.astart.CodeNodeType; import com.zy.acs.manager.core.service.astart.DynamicNodeType; import com.zy.acs.manager.core.service.astart.MapDataDispatcher; import com.zy.acs.manager.core.service.astart.NavigateNode; import com.zy.acs.manager.core.service.astart.domain.DynamicNode; import com.zy.acs.manager.manager.entity.Agv; import com.zy.acs.manager.manager.entity.AgvDetail; import com.zy.acs.manager.manager.entity.AgvModel; import com.zy.acs.manager.manager.entity.Code; import com.zy.acs.manager.manager.service.AgvDetailService; import com.zy.acs.manager.manager.service.AgvModelService; import com.zy.acs.manager.manager.service.AgvService; import com.zy.acs.manager.manager.service.CodeService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.File; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; /** * Created by vincent on 8/6/2024 */ @Slf4j @Component @DependsOn("mapDataDispatcher") public class AvoidWaveCalculator { private static final ReentrantLock lock = new ReentrantLock(Boolean.TRUE); private static final int LOCK_TIMEOUT = 5; private ExecutorService singleThreadExecutor; private File pythonFile = null; @Autowired private MapDataDispatcher mapDataDispatcher; @Autowired private AgvService agvService; @Autowired private AgvDetailService agvDetailService; @Autowired private AgvModelService agvModelService; @Autowired private CodeService codeService; @Autowired private MapService mapService; @Autowired private RedisProperties redisProperties; @PostConstruct @SuppressWarnings("all") public void execute() { this.singleThreadExecutor = Executors.newSingleThreadExecutor(); this.singleThreadExecutor.execute(() -> { try { Thread.sleep(500); } catch (InterruptedException ignore) {} this.calcDynamicNodeWhenBoot(); }); } public boolean calcWaveScope(AgvModel agvModel) { Integer lev = MapDataDispatcher.MAP_DEFAULT_LEV; boolean lockAcquired = false; long startTime = System.currentTimeMillis(); try { if (!(lockAcquired = lock.tryLock(LOCK_TIMEOUT, TimeUnit.SECONDS))) { log.warn("AvoidWaveCalculator execute fail, cause can not acquire lock ..."); return false; } // return this.calcWaveScopeByPython(lev); return this.calcWaveScopeByJava(lev, agvModel); } catch (Exception e) { log.error(this.getClass().getSimpleName(), e); return false; } finally { if (lockAcquired) { lock.unlock(); } long during = System.currentTimeMillis() - startTime; if (during > 50) { log.info("滤波函数花费时间为:{}毫秒......", during); } } } // private boolean calcWaveScopeByPython(Integer lev, AgvModel agvModel) throws Exception { // if (null == agvModel.getDiameter() || agvModel.getDiameter() <= 0) { // log.warn("There is no diameter or diameter value was wrong..."); // } // // // python // Double avoidDistance = MapDataUtils.getVehicleWaveSafeDistance(agvModel.getDiameter(), MapDataConstant.MAX_DISTANCE_BETWEEN_ADJACENT_AGV_FACTOR); // // if (null == pythonFile) { // pythonFile = loadPythonFile(); // } // // ProcessBuilder processBuilder = new ProcessBuilder( // "python" // 或者 "python3" 取决于系统配置 // , pythonFile.getAbsolutePath() // , String.valueOf(avoidDistance) // , redisProperties.getHost() // , redisProperties.getPassword() // , String.valueOf(redisProperties.getPort()) // , String.valueOf(redisProperties.getIndex()) // ); // // processBuilder.redirectErrorStream(true); // // Process process = processBuilder.start(); // // BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); // String line; // StringBuilder builder = new StringBuilder(); // while ((line = reader.readLine()) != null) { // builder.append(line); // } // // int exitCode = process.waitFor(); // if (exitCode != 0) { // log.error("Python script exited with error code: {}", exitCode); // log.error("python error:{}", builder.toString()); // return false; // } // reader.close(); // // if (builder.length() <= 0) { // return false; // } // // String result = builder.toString(); // // if (Cools.isEmpty(result)) { // return false; // } // if (!"1".equals(result)) { // log.error("Failed to call python"); // return false; // } // // return true; // } private boolean calcWaveScopeByJava(Integer lev, AgvModel agvModel) { // assert null != agvModel.getDiameter(); String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(lev); String[][] waveMatrix = mapDataDispatcher.initWaveMatrix(lev); DynamicNode[][] dynamicMatrix = mapDataDispatcher.getDynamicMatrix(lev); // 当前调度车型的最大旋转半径 double bufferRadius = MapDataUtils.buildFootprint(agvModel).maxExtent(); Map footprintCache = new HashMap<>(); for (int i = 0; i < dynamicMatrix.length; i++) { for (int j = 0; j < dynamicMatrix[i].length; j++) { DynamicNode dynamicNode = dynamicMatrix[i][j]; String vehicle = dynamicNode.getVehicle(); if (DynamicNodeType.ACCESS.val.equals(vehicle) || DynamicNodeType.BLOCK.val.equals(vehicle)) { continue; } String codeData = codeMatrix[i][j]; if (CodeNodeType.NONE.val.equals(codeData)) { continue; } VehicleFootprint footprint = footprintCache.computeIfAbsent(vehicle, this::loadFootprint); double radius = footprint.maxExtent() + bufferRadius; List includeList = mapService.getWaveScopeByCode(lev, codeData, radius); // List includeList; // Double nodeDirection = dynamicNode.getDirection(); // if (dynamicNode.isTurn()) { // double radius = footprint.maxExtent() + bufferRadius; // includeList = mapService.getWaveScopeByCode(lev, codeData, radius); // } else if (nodeDirection != null) { // double headingRad = Math.toRadians(90 - nodeDirection); // includeList = mapService.getWaveScopeByCode(lev, codeData, footprint, headingRad, bufferRadius); // } else { // double radius = footprint.maxExtent() + bufferRadius; // includeList = mapService.getWaveScopeByCode(lev, codeData, radius); // } for (NavigateNode navigateNode : includeList) { String waveNode = waveMatrix[navigateNode.getX()][navigateNode.getY()]; waveMatrix[navigateNode.getX()][navigateNode.getY()] = MapDataUtils.generateWaveNode(waveNode, vehicle); } } } mapDataDispatcher.setWaveMatrix(lev, waveMatrix); return true; } private VehicleFootprint loadFootprint(String agvNo) { AgvModel model = agvModelService.getByAgvNo(agvNo); if (model == null) { throw new CoolException(agvNo + " does not have an model."); } return MapDataUtils.buildFootprint(model); } private File loadPythonFile() { File scriptFile = null; try { Resource resource = new ClassPathResource("agv.py"); InputStream is = resource.getInputStream(); scriptFile = File.createTempFile("agv", ".py"); scriptFile.deleteOnExit(); Files.copy(is, scriptFile.toPath(), StandardCopyOption.REPLACE_EXISTING); boolean executable = scriptFile.setExecutable(true); } catch (Exception e) { throw new RuntimeException(e); } return scriptFile; } public void calcDynamicNodeWhenBoot() { List agvList = agvService.list(new LambdaQueryWrapper<>()); DynamicNode[][] dynamicMatrix = mapDataDispatcher.getDynamicMatrix(null); for (Agv agv : agvList) { calcDynamicNodeByVehicle(agv, dynamicMatrix); } } public void calcDynamicNodeByVehicle(Agv agv, DynamicNode[][] dynamicMatrix) { if (null == dynamicMatrix) { dynamicMatrix = mapDataDispatcher.getDynamicMatrix(null); } AgvDetail agvDetail = agvDetailService.selectMajorByAgvId(agv.getId()); if (null == agvDetail) { return; } if (agvDetail.getPos() == null) { return; } Long recentCode = agvDetail.getRecentCode(); if (null == recentCode) { return; } Code code = codeService.getById(recentCode); if (null == code) { return; } int[] codeMatrixIdx = mapDataDispatcher.getCodeMatrixIdx(null, code.getData()); DynamicNode dynamicNode = dynamicMatrix[codeMatrixIdx[0]][codeMatrixIdx[1]]; String vehicle = dynamicNode.getVehicle(); if (vehicle.equals(DynamicNodeType.ACCESS.val)) { mapDataDispatcher.modifyDynamicMatrix( null, Utils.singletonList(codeMatrixIdx), agv.getUuid(), Utils.singletonList(new PathDto(code.getData(), MapService.mapToNearest(agvDetail.getAgvAngle()))) ); } } @PreDestroy public void destroy() { this.singleThreadExecutor.shutdownNow(); } }