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.common.Cools; import com.zy.acs.manager.common.config.RedisProperties; import com.zy.acs.manager.common.utils.MapDataUtils; import com.zy.acs.manager.core.constant.MapDataConstant; 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.enums.AgvModelType; 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.apache.commons.lang.time.StopWatch; import org.springframework.beans.factory.annotation.Autowired; 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.*; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.List; 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 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(200); } catch (InterruptedException ignore) {} this.calcDynamicNodeWhenBoot(); // while (!Thread.currentThread().isInterrupted()) { // // this.calcWaveScope(); // // try { Thread.sleep(500); } catch (InterruptedException ignore) {} // } }); } public boolean calcWaveScope() { Integer lev = MapDataDispatcher.MAP_DEFAULT_LEV; boolean lockAcquired = false; StopWatch stopWatch = new StopWatch(); stopWatch.start(); 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); } catch (Exception e) { log.error(this.getClass().getSimpleName(), e); return false; } finally { if (lockAcquired) { lock.unlock(); } stopWatch.stop(); if (stopWatch.getTime() > 0) { if (stopWatch.getTime() > 50) { log.info("滤波函数花费时间为:{}毫秒......", stopWatch.getTime()); } } } } private boolean calcWaveScopeByPython(Integer lev) throws Exception { // python AgvModel agvModel = agvModelService.selectByType(AgvModelType.CTU_BOX_TRANSPORT_AGV.toString()); // can be optimized 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) throws Exception { AgvModel agvModel = agvModelService.selectByType(AgvModelType.CTU_BOX_TRANSPORT_AGV.toString()); // can be optimized Double avoidDistance = MapDataUtils.getVehicleWaveSafeDistance(agvModel.getDiameter(), MapDataConstant.MAX_DISTANCE_BETWEEN_ADJACENT_AGV_FACTOR); // java String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(lev); String[][] waveMatrix = mapDataDispatcher.initWaveMatrix(lev); // lock path DynamicNode[][] dynamicMatrix = mapDataDispatcher.getDynamicMatrix(lev); 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)) { List includeList = mapService.getWaveScopeByCode(lev, codeMatrix[i][j], avoidDistance); for (NavigateNode navigateNode : includeList) { String waveNode = waveMatrix[navigateNode.getX()][navigateNode.getY()]; // overlay waveMatrix[navigateNode.getX()][navigateNode.getY()] = MapDataUtils.generateWaveNode(waveNode, vehicle); } } } } // mapDataDispatcher.printMatrix(waveMatrix); mapDataDispatcher.setWaveMatrix(lev, waveMatrix); return true; } 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 syncWaveBySingleVeh(String agvNo, String codeData) { if (Cools.isEmpty(agvNo, codeData)) { return; } boolean lockAcquired = false; Integer lev = MapDataDispatcher.MAP_DEFAULT_LEV; try { if (!(lockAcquired = lock.tryLock(LOCK_TIMEOUT, TimeUnit.SECONDS))) { log.warn("AvoidWaveCalculator syncWaveBySingleVeh fail, cause can not acquire lock ..."); return; } Agv agv = agvService.selectByUuid(agvNo); AgvModel agvModel = agvModelService.getByAgvNo(agvNo); Double avoidDistance = MapDataUtils.getVehicleWaveSafeDistance(agvModel.getDiameter(), MapDataConstant.MAX_DISTANCE_BETWEEN_ADJACENT_AGV_FACTOR); String[][] waveMatrix = mapDataDispatcher.getWaveMatrix(lev); List includeList = mapService.getWaveScopeByCode(lev, codeData, avoidDistance); for (NavigateNode navigateNode : includeList) { String waveNode = waveMatrix[navigateNode.getX()][navigateNode.getY()]; waveMatrix[navigateNode.getX()][navigateNode.getY()] = MapDataUtils.generateWaveNode(waveNode, agv.getUuid()); } mapDataDispatcher.setWaveMatrix(lev, waveMatrix); } catch (Exception e) { log.error("AvoidWaveCalculator.syncWaveBySingleVeh fail", e); } finally { if (lockAcquired) { lock.unlock(); } } } 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.selectByAgvId(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()); } } @PreDestroy public void destroy() { this.singleThreadExecutor.shutdownNow(); } }