package com.mes.job; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.Assert; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.github.yulichang.query.MPJQueryWrapper; import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.mes.common.S7object; import com.mes.common.config.Const; import com.mes.device.PlcParameterObject; import com.mes.edgstoragecage.entity.EdgStorageCage; import com.mes.edgstoragecage.entity.EdgStorageCageDetails; import com.mes.edgstoragecage.service.EdgStorageCageDetailsService; import com.mes.edgstoragecage.service.EdgStorageCageService; import com.mes.glassinfo.entity.GlassInfo; import com.mes.glassinfo.service.GlassInfoService; import com.mes.taskcache.entity.TaskCache; import com.mes.taskcache.service.TaskCacheService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @Author : zhoush * @Date: 2024/5/8 8:17 * @Description: */ @Component @Slf4j public class CacheGlassTask { @Autowired TaskCacheService taskCacheService; @Autowired GlassInfoService glassInfoService; @Autowired EdgStorageCageService edgStorageCageService; @Autowired EdgStorageCageDetailsService edgStorageCageDetailsService; @Value("${mes.threshold}") private int threshold; @Scheduled(fixedDelay = 1000) public void plcHomeEdgTask() { PlcParameterObject plcParameterObject = S7object.getinstance().PlcMesObject; String taskRequestTypeValue = plcParameterObject.getPlcParameter("A06_request_word").getValue(); String glassIdeValue = plcParameterObject.getPlcParameter("A05_scanning_ID").getValue(); String confirmationWrodValue = plcParameterObject.getPlcParameter("MES_confirmation_word").getValue(); //A08 A09表示线路相同 可做等价 String out08Glassstate = plcParameterObject.getPlcParameter("A08_glass_status").getValue(); String out10Glassstate = plcParameterObject.getPlcParameter("A10_glass_status").getValue(); String confirmationWrodAddress = plcParameterObject.getPlcParameter("MES_confirmation_word").getAddress(); String currentSlot = plcParameterObject.getPlcParameter("Current_slot").getValue(); log.info("1、获取到的请求字为:{},获取到的扫描ID为:{},获取到的确认字为:{},获取到的出片状态分别为:A09{}、A10{},当前格子号为:{}", taskRequestTypeValue, glassIdeValue, confirmationWrodValue, out08Glassstate, out10Glassstate, currentSlot); if ("0".equals(taskRequestTypeValue)) { if ("0".equals(confirmationWrodValue)) { log.info("2、获取到的请求字为0,且确认字为0,不执行任务"); return; } log.info("2、获取到的请求字为0,将确认字改为0"); S7object.getinstance().plccontrol.WriteWord(confirmationWrodAddress, (short) 0); return; } if (!"0".equals(confirmationWrodValue)) { log.info("2、获取到的请求字不为0,将确认字改为0"); S7object.getinstance().plccontrol.WriteWord(confirmationWrodAddress, (short) 0); return; } if ("1".equals(taskRequestTypeValue)) { log.info("3、进片请求,且确认字为0,执行进片任务"); inTo(glassIdeValue, confirmationWrodAddress, currentSlot); } else if ("2".equals(taskRequestTypeValue)) { //09空闲 :1 10空闲 :2 都空闲:3 其他0 log.info("3、出片请求,且确认字为0,执行进片任务"); outTo(Integer.parseInt(out08Glassstate), confirmationWrodAddress); } else if ("3".equals(taskRequestTypeValue)) { log.info("3、进片和出片都空闲,执行出片任务"); //加笼子里面是否有玻璃,有先出,无玻璃先进 int count = edgStorageCageDetailsService.count(new LambdaQueryWrapper().eq(EdgStorageCageDetails::getState, Const.GLASS_STATE_IN)); if ("0".equals(out08Glassstate) || count > 0) { inTo(glassIdeValue, confirmationWrodAddress, currentSlot); } else { outTo(Integer.parseInt(out08Glassstate), confirmationWrodAddress); } } } /** * 进片任务 * * @param glassId * @param confirmationWrodAddress * @param currentSlot */ private void inTo(String glassId, String confirmationWrodAddress, String currentSlot) { log.info("1、按照玻璃id:{}获取玻璃小片信息,当前格子为:{}", glassId, currentSlot); GlassInfo glassInfo = glassInfoService.getOne(new LambdaQueryWrapper().eq(GlassInfo::getGlassId, glassId)); if (glassInfo == null) { log.info("2、此玻璃编号不存在"); return; } log.info("2、获取到的玻璃信息为{}", glassInfo); //添加进片任务 查找空格 EdgStorageCage nearestEmpty = edgStorageCageService.selectNearestEmpty(Integer.parseInt(currentSlot)); Assert.isTrue(nearestEmpty == null, "格子已满"); log.info("3、查询卧式理片笼里面的空格:{}", nearestEmpty); log.info("4、将玻璃信息插入卧式理片笼,当前玻璃信息:{}", glassInfo); EdgStorageCageDetails details = new EdgStorageCageDetails(); BeanUtils.copyProperties(glassInfo, details); details.setState(Const.GLASS_STATE_IN); details.setSlot(nearestEmpty.getSlot()); details.setDeviceId(nearestEmpty.getDeviceId()); edgStorageCageDetailsService.save(details); log.info("5、玻璃信息已存入理片笼详情表,玻璃信息为{}", details); //添加进片任务 TaskCache taskCache = new TaskCache(); taskCache.setGlassId(glassId); taskCache.setTaskStatus(0); taskCache.setStartCell(0); taskCache.setEndCell(nearestEmpty.getSlot()); taskCache.setTaskType(1); taskCache.setCreateTime(new Date()); taskCacheService.insertTaskCache(taskCache); log.info("6、生成进片任务信息存入任务表{}", taskCache); S7object.getinstance().plccontrol.WriteWord(confirmationWrodAddress, (short) 1); log.info("7、发送确认字完成"); } /** * 出片任务 * * @param line * @param confirmationWrodAddress */ private void outTo(int line, String confirmationWrodAddress) { //逻辑步骤: // 0、A09、A10是否空闲,是否可以执行出片任务 // 1、获取钢化版图是否超过阈值 // 1.1、超过阈值:获取当前最小版图需要出片的玻璃信息 // 1.1.1、获取两条线最后一次出片的任务信息 // 1.1.2、按照出片信息去详情表查询格子在笼子里面剩余相同尺寸的玻璃数据 // 1.1.3、判断哪条线玻璃数量在理片笼内的数据最少,决定最小版图走该条线(问题:如果这条线非空闲,直接结束) // 1.2、未超过阈值: // 1.2.1、获取程序那条线空闲 // 1.2.2、获取该条线最后一次出片的任务信息 // 1.2.3、按照出片信息去详情表查询格子在笼子里面剩余相同尺寸的玻璃数据且以版图id、版序升序排序 取第一块玻璃出片 // 2、如果没有历史出片任务 // 2.1、出当前版图id最小版序最小的玻璃(问题:两条线都没有历史任务,出片时两条线的玻璃尺寸相同,是否找尺寸不同的) Assert.isTrue(line != 0, "A09、A10都有玻璃,无法出片"); log.info("0、出片任务出【{}】号线,备注(09空闲:1;10空闲:2;都空闲:3)", line); //定义出片玻璃信息 EdgStorageCageDetails glassInfo = null; int endcell = 0; boolean flag = queryMaxMinDiff(threshold); log.info("1、获取钢化版图是否超过阈值:{}", flag); if (flag) { glassInfo = queryMinGlass(0.0, 0.0); log.info("1.1、超过阈值:获取当前最小版图需要出片的玻璃信息:{}", glassInfo); Integer a09Count = queryCountByTaskLine(Const.A09_OUT_TARGET_POSITION).size(); Integer a10Count = queryCountByTaskLine(Const.A10_OUT_TARGET_POSITION).size(); log.info("1.2、获取笼子剩余数量A09为{},A10为{}", a09Count, a10Count); if (a10Count <= a09Count && line != 2) { log.info("1.2.1、A09线出片"); endcell = Const.A09_OUT_TARGET_POSITION; } else { log.info("1.2.2、A10线出片"); endcell = Const.A09_OUT_TARGET_POSITION; } } else { //获取指定线路将要出的玻璃信息 endcell = line == 2 ? Const.A10_OUT_TARGET_POSITION : Const.A09_OUT_TARGET_POSITION; //当前任务出完无玻璃 更换玻璃 int othercell = endcell == Const.A10_OUT_TARGET_POSITION ? Const.A09_OUT_TARGET_POSITION : Const.A10_OUT_TARGET_POSITION; List details = queryCountByTaskLine(endcell); if (details.size() > 0) { glassInfo = details.get(0); } else { //todo:去理片笼里面查 glassInfo = queryChangeGlassInfo(othercell); } } if (glassInfo != null) { log.info("4、添加出片任务,玻璃id:{},任务类型:{},起始位置:{},结束位置:{}", glassInfo.getGlassId(), 2, glassInfo.getSlot(), endcell); TaskCache taskCache = new TaskCache(); taskCache.setGlassId(glassInfo.getGlassId()); taskCache.setTaskStatus(0); taskCache.setStartCell(glassInfo.getSlot()); taskCache.setEndCell(endcell); taskCache.setTaskType(2); taskCache.setCreateTime(new Date()); taskCacheService.insertTaskCache(taskCache); S7object.getinstance().plccontrol.WriteWord(confirmationWrodAddress, (short) 1); } } /** * 获取详情表内最大最小版图id的差值,判断是否出最小版图玻璃 * * @return */ private boolean queryMaxMinDiff(int threshold) { //todo:获取笼子内最大版图id和最小版图id插值,判断是否大于阈值,大于阈值直接出最小版图玻璃 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.select("max(tempering_layout_id)-min(tempering_layout_id) as diff") .eq("state", Const.GLASS_STATE_IN); Integer diff = (Integer) edgStorageCageDetailsService.listObjs(queryWrapper).get(0); return diff > threshold; } /** * 获取当前最小版图需要出片的玻璃信息 * * @param width * @param height */ private EdgStorageCageDetails queryMinGlass(Double width, Double height) { return edgStorageCageDetailsService.getOne(new LambdaQueryWrapper() .eq(EdgStorageCageDetails::getState, 100) .eq(width != 0, EdgStorageCageDetails::getWidth, width) .eq(height != 0, EdgStorageCageDetails::getHeight, height) .orderByAsc(EdgStorageCageDetails::getTemperingLayoutId, EdgStorageCageDetails::getTemperingFeedSequence) .last("limit 1")); } /** * 获取任务表中指定线路笼子内还剩余的玻璃数量 */ private List queryCountByTaskLine(int line) { //获取任务表中最后一次出片的玻璃id LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().eq(TaskCache::getTaskType, Const.GLASS_CACHE_TYPE_OUT) .eq(TaskCache::getEndCell, line).orderByDesc(TaskCache::getCreateTime); List taskCacheList = taskCacheService.list(queryWrapper); if (CollectionUtil.isEmpty(taskCacheList)) { return new ArrayList<>(); } TaskCache taskCache = taskCacheList.get(0); MPJQueryWrapper mpjLambdaWrapper = new MPJQueryWrapper<>(); mpjLambdaWrapper.select("a.*") .innerJoin("edg_storage_cage_details t1 on t.width = t1.width and t.height = t1.height") .eq("t.glass_id", taskCache.getGlassId()) .ne("t1.glass_id", taskCache.getGlassId()) .orderByAsc("t1.tempering_layout_id", "t1.tempering_feed_sequence"); List details = edgStorageCageDetailsService.selectJoinList(EdgStorageCageDetails.class, mpjLambdaWrapper); if (CollectionUtil.isEmpty(details)) { return new ArrayList<>(); } return details; } /** * 更换出片玻璃 * * @param othercell * @return */ private EdgStorageCageDetails queryChangeGlassInfo(int othercell) { //获取笼子内数量前二的玻璃数量 MPJLambdaWrapper wrapper = new MPJLambdaWrapper<>(); wrapper.select(EdgStorageCageDetails::getWidth, EdgStorageCageDetails::getHeight) .selectCount("*", EdgStorageCageDetails::getCount) .groupBy(EdgStorageCageDetails::getWidth, EdgStorageCageDetails::getHeight) .last("order by count(*) desc limit 2"); List list = edgStorageCageDetailsService.list(wrapper); log.info("获取笼子内数量前二的玻璃数量:{}", list); Assert.notEmpty(list, "笼子里没有玻璃"); //一片玻璃直接出 if (list.size() == 1) { return list.get(0); } //分别获取宽高的玻璃数量 EdgStorageCageDetails firstSize = list.get(0); EdgStorageCageDetails secondSize = list.get(1); //获取任务表中最后一次出片的玻璃id LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().eq(TaskCache::getTaskType, Const.GLASS_CACHE_TYPE_OUT) .eq(TaskCache::getEndCell, othercell).orderByDesc(TaskCache::getCreateTime); List taskCacheList = taskCacheService.list(queryWrapper); log.info("获取任务表中{}线最后一次出片的玻璃任务信息:{}", othercell, taskCacheList); if (CollectionUtil.isEmpty(taskCacheList)) { log.info("{}线没有出片任务信息,直接出片", othercell); return queryMinGlass(firstSize.getWidth(), firstSize.getHeight()); } Integer firstCount = firstSize.getCount(); Double firstWidth = firstSize.getWidth(); Double firstHeight = firstSize.getHeight(); Integer secondCount = secondSize.getCount(); Double secondWidth = secondSize.getWidth(); Double secondHeight = secondSize.getHeight(); //获取数量前2的玻璃数量比例 Integer mix = firstCount / secondCount; log.info("获取玻璃数量前2的玻璃占比为:{}", mix); if (mix >= 2) { log.info("获取玻璃数量前2的玻璃占比为{},大于2,直接出玻璃数据的最多的,宽:{},高:{}", mix, firstWidth, firstHeight); return queryMinGlass(firstWidth, firstHeight); } else { log.info("获取玻璃数量前2的玻璃占比为{},小于2", mix); TaskCache taskCache = taskCacheList.get(0); EdgStorageCageDetails outGlassInfo = edgStorageCageDetailsService.getOne(new LambdaQueryWrapper().eq(EdgStorageCageDetails::getGlassId, taskCache.getGlassId())); log.info("{}线有出片任务信息,任务信息为{},玻璃信息为{}", othercell, taskCache, outGlassInfo); if (outGlassInfo.getWidth().equals(firstWidth) && outGlassInfo.getHeight().equals(firstHeight)) { log.info("数量最多的宽{}高{}和{}线任务的宽{}高{}相同,出数量排第二的玻璃,宽{}高{}", firstWidth, firstHeight, othercell, outGlassInfo.getWidth(), outGlassInfo.getHeight(), secondWidth, secondHeight); return queryMinGlass(secondWidth, secondHeight); } else { log.info("数量第二多的宽{}高{}和{}线任务的宽{}高{}相同,出数量排第二的玻璃,宽{}高{}", secondWidth, secondHeight, othercell, outGlassInfo.getWidth(), outGlassInfo.getHeight(), firstWidth, firstHeight); return queryMinGlass(firstWidth, firstHeight); } } } }