#
Junjie
2024-05-05 dad3cd8ed8afa86b651bb1a053421e7aa2b8c7ac
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/service/impl/MainServiceImpl.java
@@ -1,19 +1,41 @@
package com.zy.asrs.wcs.core.service.impl;
import com.zy.asrs.wcs.core.entity.Task;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.asrs.framework.common.Cools;
import com.zy.asrs.framework.common.SnowflakeIdWorker;
import com.zy.asrs.wcs.core.domain.dto.RedisMapDto;
import com.zy.asrs.wcs.core.entity.*;
import com.zy.asrs.wcs.core.kernel.AnalyzeService;
import com.zy.asrs.wcs.core.model.MapNode;
import com.zy.asrs.wcs.core.model.enums.DeviceCtgType;
import com.zy.asrs.wcs.core.model.enums.MotionStsType;
import com.zy.asrs.wcs.core.model.enums.TaskStsType;
import com.zy.asrs.wcs.core.service.TaskService;
import com.zy.asrs.wcs.core.service.*;
import com.zy.asrs.wcs.core.utils.RedisUtil;
import com.zy.asrs.wcs.core.utils.ShuttleDispatcher;
import com.zy.asrs.wcs.core.utils.Utils;
import com.zy.asrs.wcs.rcs.News;
import com.zy.asrs.wcs.core.entity.Motion;
import com.zy.asrs.wcs.core.service.MotionService;
import com.zy.asrs.wcs.rcs.cache.SlaveConnection;
import com.zy.asrs.wcs.rcs.constant.DeviceRedisConstant;
import com.zy.asrs.wcs.rcs.entity.Device;
import com.zy.asrs.wcs.rcs.model.enums.ShuttleProtocolStatusType;
import com.zy.asrs.wcs.rcs.model.enums.SlaveType;
import com.zy.asrs.wcs.rcs.model.protocol.ShuttleProtocol;
import com.zy.asrs.wcs.rcs.model.protocol.StaProtocol;
import com.zy.asrs.wcs.rcs.service.DeviceService;
import com.zy.asrs.wcs.rcs.thread.DevpThread;
import com.zy.asrs.wcs.rcs.thread.ShuttleThread;
import com.zy.asrs.wcs.system.entity.Dict;
import com.zy.asrs.wcs.system.service.DictService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.*;
/**
 * 立体仓库WCS系统主流程业务
@@ -30,6 +52,22 @@
    private AnalyzeService analyzeService;
    @Autowired
    private MotionService motionService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private LocCtgService locCtgService;
    @Autowired
    private LocService locService;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private TaskCtgService taskCtgService;
    @Autowired
    private DictService dictService;
    @Autowired
    private ShuttleDispatcher shuttleDispatcher;
    @Autowired
    private RedisUtil redisUtil;
    /**
     * 组托
@@ -37,6 +75,122 @@
     */
    public synchronized void generateInboundWrk() {
    }
    /**
     * 初始化实时地图
     */
    public synchronized void initRealtimeBasMap() {
        try {
            List<Dict> dicts = dictService.list(new LambdaQueryWrapper<Dict>()
                    .like(Dict::getFlag, "map")
                    .eq(Dict::getStatus, 1));
            TreeMap<Integer, ArrayList<ArrayList<MapNode>>> levData = new TreeMap<>();
            for (Dict dict : dicts) {
                String[] split = dict.getFlag().split("-");
                int lev = Integer.parseInt(split[1]);
                TreeMap<Integer, List<JSONObject>> rows = new TreeMap<>();
                //排序Row
                JSONArray value = JSON.parseArray(dict.getValue());
                for (Object o : value) {
                    JSONObject item = JSON.parseObject(o.toString());
                    if (item.getString("type").equals("SHELF")) {
                        JSONObject property = JSON.parseObject(item.getString("property"));
                        Integer row1 = property.getInteger("row");
                        ArrayList<JSONObject> bays = new ArrayList<>();
                        if (rows.containsKey(row1)) {
                            bays.addAll(rows.get(row1));
                        }
                        bays.add(property);
                        rows.put(row1, bays);
                    }
                }
                ArrayList<ArrayList<MapNode>> list = new ArrayList<>();
                //排序Bay
                for (Map.Entry<Integer, List<JSONObject>> entry : rows.entrySet()) {
                    ArrayList<MapNode> nodes = new ArrayList<>();
                    for (JSONObject object : entry.getValue()) {
                        MapNode mapNode = new MapNode();
                        mapNode.setValue(object.getInteger("shelfType"));
                        mapNode.setTop(object.getInteger("top"));
                        mapNode.setBottom(object.getInteger("bottom"));
                        mapNode.setLeft(object.getInteger("left"));
                        mapNode.setRight(object.getInteger("right"));
                        mapNode.setRow(object.getInteger("row"));
                        mapNode.setBay(object.getInteger("bay"));
                        mapNode.setNo(object.getString("row") + "-" + object.getString("bay"));
                        mapNode.setXBase(object.getInteger("refx"));
                        mapNode.setYBase(object.getInteger("refy"));
                        nodes.add(mapNode);
                    }
                    Collections.sort(nodes, new Comparator<MapNode>() {
                        @Override
                        public int compare(MapNode o1, MapNode o2) {
                            return Integer.compare(o1.getBay(), o2.getBay());
                        }
                    });
                    list.add(nodes);
                }
                levData.put(lev, list);
            }
            for (Map.Entry<Integer, ArrayList<ArrayList<MapNode>>> entry : levData.entrySet()) {
                ArrayList<ArrayList<MapNode>> lists = entry.getValue();//获取地图
                MapNode mapNode = new MapNode();
                mapNode.setValue(-1);
                mapNode.setTop(1000);
                mapNode.setBottom(1000);
                mapNode.setLeft(1000);
                mapNode.setRight(1000);
                mapNode.setRow(0);
                mapNode.setBay(0);
                mapNode.setNo("0-0");
                mapNode.setXBase(0);
                mapNode.setYBase(0);
                //获取最长row
                int row = 0;
                //给每个row首尾增加-1节点
                for (ArrayList<MapNode> list : lists) {
                    if (list.size() > row) {
                        row = list.size();
                    }
                    list.add(0, mapNode.clone());
                    list.add(mapNode.clone());
                }
                ArrayList<MapNode> headNodes = new ArrayList<>();
                ArrayList<MapNode> footerNodes = new ArrayList<>();
                for (int i = 0; i < row+2; i++) {
                    headNodes.add(mapNode.clone());
                    footerNodes.add(mapNode.clone());
                }
                lists.add(0, headNodes);
                lists.add(footerNodes);
                Integer lev = entry.getKey();
                Date now = new Date();
                RedisMapDto map = new RedisMapDto();
                map.setData(JSON.toJSONString(lists));
                map.setCreateTime(now);
                map.setUpdateTime(now);
                map.setLev(lev);
                //将地图数据存入redis
                redisUtil.set(DeviceRedisConstant.MAP + lev, JSON.toJSONString(map));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 解析入库工作档
@@ -58,4 +212,306 @@
        }
    }
    /**
     * 出库 ====>> 同一时间一台穿梭车只能有一个出库任务
     */
    public synchronized void generateOutboundWrkMast() {
        List<Task> tasks = taskService.selectPakOut();
        if (tasks.isEmpty()) {
            return;
        }
        for (Task task : tasks) {
            DevpThread devpThread = (DevpThread) SlaveConnection.get(SlaveType.Devp, 1);
            StaProtocol staProtocol = devpThread.getStation().get(Integer.parseInt(task.getOriginSite()));//源站
            StaProtocol staProtocol1 = devpThread.getStation().get(Integer.parseInt(task.getDestSite()));//目标站
            if (staProtocol == null || staProtocol1 == null) {
                continue;
            } else {
                staProtocol = staProtocol.clone();
                staProtocol1 = staProtocol1.clone();
            }
//            // 查询站点详细信息
//            BasDevp staDetl = basDevpService.selectById(outSta.getStaNo());
//            if (staDetl == null) {
//                log.error("出库 ===>> 站点在数据库不存在, 站点编号={}", outSta.getStaNo());
//                continue;
//            }
            // 判断堆垛机出库站状态
            if (staProtocol.isAutoing() && !staProtocol.isLoading() && staProtocol.getWorkNo() == 0 && staProtocol.isOutEnable()) {
                if (!(staProtocol1.isAutoing() && !staProtocol1.isLoading() && staProtocol1.getWorkNo() == 0 && staProtocol1.isOutEnable())) {
                    continue;
                }
//                //同库位组校验
//                List<String> outerLoc = Utils.getGroupOuterLoc(wrkMast.getSourceLocNo());
//                List<LocMast> outerLocMasts = locMastService.selectNotEmptyLocNos(outerLoc);
//                if (!outerLocMasts.isEmpty()) {
//                    News.info("{}任务,浅库位存在货物,系统等待中", wrkMast.getWrkNo());
//                    continue;//浅库位存在未执行任务
//                }
                // generate motion list
                List<Motion> motionList = analyzeService.generateMotion(task);
                if (Cools.isEmpty(motionList)) {
                    log.error("出库 ===>> 暂时没有空闲小车, 任务号={}", task.getTaskNo());
                    continue;
                }
                motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()));
                // 更新工作主档
                task.setTaskSts(TaskStsType.ANALYZE_OUTBOUND.sts); // 工作状态
                task.setUpdateTime(new Date());
                if (!taskService.updateById(task)) {
                    News.error("更新工作档失败!!! [工作号:{}]", task.getTaskNo());
                }
            }
        }
    }
    /**
     * 四向穿梭车电量检测 ===>> 发起充电
     */
    public synchronized void loopShuttleCharge() {
        // 获取充电桩库位类型
        LocCtg locCtg = locCtgService.getOne(new LambdaQueryWrapper<LocCtg>()
                .eq(LocCtg::getFlag, "CHARGE")
                .eq(LocCtg::getStatus, 1));
        if (locCtg == null) {
            return;
        }
        //获取充电任务类型
        TaskCtg taskCtg = taskCtgService.getOne(new LambdaQueryWrapper<TaskCtg>()
                .eq(TaskCtg::getFlag, "CHARGE")
                .eq(TaskCtg::getStatus, 1));
        if (taskCtg == null) {
            return;
        }
        List<Device> list = deviceService.list(new LambdaQueryWrapper<Device>()
                .eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val())
                .eq(Device::getStatus, 1));
        for (Device device : list) {
            //获取四向穿梭车线程
            ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
            if (shuttleThread == null) {
                continue;
            }
            ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
            if (shuttleProtocol == null) {
                continue;
            }
            if (!shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.IDLE)) {
                continue;
            }
            if (!shuttleThread.isRequireCharge()) {
                continue;
            }
            String currentLocNo = shuttleProtocol.getCurrentLocNo();
            int lev = Utils.getLev(currentLocNo);//获取小车楼层
            //搜索小车当前楼层充电桩
            ArrayList<Loc> allChargeLoc = new ArrayList<>();
            List<Loc> list1 = locService.list(new LambdaQueryWrapper<Loc>()
                    .eq(Loc::getLocCtg, locCtg.getId())
                    .eq(Loc::getStatus, 1)
                    .eq(Loc::getLev, lev));
            if (!list1.isEmpty()) {
                allChargeLoc.addAll(list1);
            }
            //搜索其他楼层充电桩
            List<Loc> list2 = locService.list(new LambdaQueryWrapper<Loc>()
                    .eq(Loc::getLocCtg, locCtg.getId())
                    .eq(Loc::getStatus, 1)
                    .notIn(Loc::getLev, lev));
            if (!list2.isEmpty()) {
                allChargeLoc.addAll(list2);
            }
            //没有找到充电桩
            if (allChargeLoc.isEmpty()) {
                continue;
            }
            //选择空闲充电桩
            Loc chargeLoc = null;
            for (Loc loc : allChargeLoc) {
                // 判断充电位是否被占用(车辆位置)
                if (Utils.hasShuttleInLoc(loc.getLocNo(), device.getId())) {
                    continue;
                }
                // 盘点充电位是否存在任务档
                List<Task> tasks = taskService.hasChargeInLoc(loc.getLocNo());
                if (!tasks.isEmpty()) {
                    continue;
                }
                chargeLoc = loc;
                break;
            }
            if (chargeLoc == null) {
                continue;//未找到充电桩
            }
            if (motionService.count(new LambdaQueryWrapper<Motion>()
                    .eq(Motion::getDeviceCtg, DeviceCtgType.SHUTTLE.val())
                    .eq(Motion::getDevice, device.getDeviceNo())
                    .eq(Motion::getMotionSts, MotionStsType.EXECUTING.val())) > 0) {
                continue;
            }
            //判断当前小车是否满足需要充电要求
            if (!shuttleThread.isRequireCharge()) {
                continue;
            }
            Task taskCharge = taskService.selectChargeWorking(Integer.valueOf(device.getDeviceNo()));
            if (taskCharge != null) {//已有充电任务
                continue;
            }
            String chargeLocNo = chargeLoc.getLocNo();
            Task task = new Task();
            task.setUuid(String.valueOf(snowflakeIdWorker.nextId()));
            task.setTaskNo(String.valueOf(Utils.getTaskNo("CHARGE")));
            task.setTaskSts(TaskStsType.NEW_CHARGE.sts);
            task.setTaskCtg(taskCtg.getId());
            task.setPriority(10);
            task.setOriginSite(null);
            task.setOriginLoc(null);
            task.setDestSite(null);
            task.setDestLoc(chargeLocNo);
            task.setIoTime(new Date());
            task.setStartTime(new Date());
            task.setHostId(device.getHostId());
            task.setStatus(1);
            task.setMemo("charge");
            task.setShuttleNo(Integer.valueOf(device.getDeviceNo()));
            // generate motion list
            List<Motion> motionList = analyzeService.generateChargeMotion(task);
            if (Cools.isEmpty(motionList)) {
                News.error("保存{}号四向穿梭车充电任务失败!!!", device.getDeviceNo());
                continue;
            }
            motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()));
            task.setTaskSts(TaskStsType.ANALYZE_CHARGE.sts);
            if (!taskService.save(task)) {
                News.error("保存{}号四向穿梭车充电任务失败!!!", device.getDeviceNo());
                continue;
            }
            News.info("保存{}号四向穿梭车充电任务成功!!!", device.getDeviceNo());
        }
    }
    /**
     * 四向穿梭车电量检测 ===>> 满电后回到待机位
     */
    public synchronized void loopShuttleToStandbyCauseCharge() {
        Integer enoughPower = 90;
        Dict dict = dictService.getOne(new LambdaQueryWrapper<Dict>()
                .eq(Dict::getFlag, "chargeMaxValue")
                .eq(Dict::getStatus, 1));
        if (dict != null) {
            enoughPower = Integer.parseInt(dict.getValue());
        }
        //获取迁移任务类型
        TaskCtg taskCtg = taskCtgService.getOne(new LambdaQueryWrapper<TaskCtg>()
                .eq(TaskCtg::getFlag, "MOVE")
                .eq(TaskCtg::getStatus, 1));
        if (taskCtg == null) {
            return;
        }
        List<Device> list = deviceService.list(new LambdaQueryWrapper<Device>()
                .eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val())
                .eq(Device::getStatus, 1));
        for (Device device : list) {
            //获取四向穿梭车线程
            ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
            if (shuttleThread == null) {
                continue;
            }
            ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
            if (shuttleProtocol == null) {
                continue;
            }
            if (!shuttleThread.isCharging()) {
                continue;
            }
            if (!shuttleThread.isChargingCompleted()) {
                continue;
            }
            //查找充电任务
            Task chargeTask = taskService.getOne(new LambdaQueryWrapper<Task>()
                    .eq(Task::getTaskSts, TaskStsType.CHARGE_WORKING.sts)
                    .eq(Task::getShuttleNo, device.getDeviceNo()));
            if (chargeTask == null) {
                continue;
            }
            //充电完成
            // 已有迁移任务
            if (taskService.selectMoveWorking(Integer.valueOf(device.getDeviceNo())) != null) {
                continue;
            }
            //获取避让位置
            String standByLocNo = shuttleDispatcher.searchStandByLocNo(Integer.valueOf(device.getDeviceNo()), device.getHostId(), shuttleThread.getStatus().getCurrentLocNo());
            Task task = new Task();
            task.setUuid(String.valueOf(snowflakeIdWorker.nextId()));
            task.setTaskNo(String.valueOf(Utils.getTaskNo("MOVE")));
            task.setTaskSts(TaskStsType.NEW_MOVE.sts);
            task.setTaskCtg(taskCtg.getId());
            task.setPriority(10);
            task.setOriginSite(null);
            task.setOriginLoc(null);
            task.setDestSite(null);
            task.setDestLoc(standByLocNo); // 避让位置
            task.setIoTime(new Date());
            task.setStartTime(new Date());
            task.setHostId(device.getHostId());
            task.setStatus(1);
            task.setMemo("charge");
            task.setShuttleNo(Integer.valueOf(device.getDeviceNo()));
            // generate motion list
            List<Motion> motionList = analyzeService.generateShuttleChargeWrkComplete(task);
            if (Cools.isEmpty(motionList)) {
                News.error("保存{}号四向穿梭车迁移任务失败!!!", device.getDeviceNo());
                continue;
            }
            motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()));
            task.setTaskSts(TaskStsType.ANALYZE_MOVE.sts);
            if (!taskService.save(task)) {
                News.error("保存{}号四向穿梭车迁移任务失败!!!", device.getDeviceNo());
                continue;
            }
            chargeTask.setTaskSts(TaskStsType.COMPLETE_CHARGE.sts);
            chargeTask.setIoTime(new Date());
            taskService.updateById(chargeTask);
        }
    }
}