| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dv.statistics; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.statistics.vo.MesDvOeeRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.statistics.vo.MesDvStatisticsRespVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryTypeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.dv.maintenrecord.MesDvMaintenRecordDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.dv.repair.MesDvRepairDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.dv.machinery.MesDvMachineryMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.dv.machinery.MesDvMachineryTypeMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.dv.maintenrecord.MesDvMaintenRecordMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.dv.repair.MesDvRepairMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.dv.MesDvMachineryStatusEnum; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.Duration; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - MES 设å¤ç»è®¡") |
| | | @RestController |
| | | @RequestMapping("/mes/dv/statistics") |
| | | @Validated |
| | | public class MesDvStatisticsController { |
| | | |
| | | @Resource |
| | | private MesDvMachineryMapper machineryMapper; |
| | | @Resource |
| | | private MesDvMachineryTypeMapper machineryTypeMapper; |
| | | @Resource |
| | | private MesDvRepairMapper repairMapper; |
| | | @Resource |
| | | private MesDvMaintenRecordMapper maintenRecordMapper; |
| | | @Resource |
| | | private MesProTaskMapper taskMapper; |
| | | |
| | | @GetMapping("/summary") |
| | | @Operation(summary = "设å¤ç»è®¡æ±æ»ï¼å°è´¦/ç»´ä¿®/ä¿å
»/ç±»ååå¸ï¼") |
| | | public CommonResult<MesDvStatisticsRespVO> getSummary() { |
| | | MesDvStatisticsRespVO vo = new MesDvStatisticsRespVO(); |
| | | |
| | | // 1. 设å¤å°è´¦ç»è®¡ |
| | | Long total = machineryMapper.selectCount(); |
| | | Long producing = machineryMapper.selectCount(new LambdaQueryWrapperX<MesDvMachineryDO>() |
| | | .eq(MesDvMachineryDO::getStatus, MesDvMachineryStatusEnum.PRODUCING.getStatus())); |
| | | Long stopped = machineryMapper.selectCount(new LambdaQueryWrapperX<MesDvMachineryDO>() |
| | | .eq(MesDvMachineryDO::getStatus, MesDvMachineryStatusEnum.STOP.getStatus())); |
| | | Long maintaining = machineryMapper.selectCount(new LambdaQueryWrapperX<MesDvMachineryDO>() |
| | | .eq(MesDvMachineryDO::getStatus, MesDvMachineryStatusEnum.MAINTENANCE.getStatus())); |
| | | vo.setTotalMachinery(total); |
| | | vo.setProducingCount(producing); |
| | | vo.setStoppedCount(stopped); |
| | | vo.setMaintainingCount(maintaining); |
| | | vo.setFaultRate(total != null && total > 0 |
| | | ? (double) (stopped + maintaining) / total * 100 : 0D); |
| | | |
| | | // 2. ç»´ä¿®ç»è®¡ |
| | | vo.setTotalRepair(repairMapper.selectCount()); |
| | | vo.setFinishedRepair(repairMapper.selectCount(new LambdaQueryWrapperX<MesDvRepairDO>() |
| | | .eq(MesDvRepairDO::getStatus, cn.iocoder.yudao.module.mes.enums.dv.MesDvRepairStatusEnum.FINISHED.getStatus()))); |
| | | vo.setProcessingRepair((vo.getTotalRepair() != null ? vo.getTotalRepair() : 0L) |
| | | - (vo.getFinishedRepair() != null ? vo.getFinishedRepair() : 0L)); |
| | | |
| | | // 3. ä¿å
»ç»è®¡ |
| | | vo.setTotalMainten(maintenRecordMapper.selectCount()); |
| | | vo.setFinishedMainten(maintenRecordMapper.selectCount(new LambdaQueryWrapperX<MesDvMaintenRecordDO>() |
| | | .eq(MesDvMaintenRecordDO::getStatus, cn.iocoder.yudao.module.mes.enums.dv.MesDvMaintenRecordStatusEnum.SUBMITTED.getStatus()))); |
| | | |
| | | // 4. æè®¾å¤ç±»ååå¸ |
| | | List<MesDvMachineryDO> machineryList = machineryMapper.selectList(); |
| | | Map<Long, Long> typeCountMap = new HashMap<>(); |
| | | for (MesDvMachineryDO machinery : machineryList) { |
| | | if (machinery.getMachineryTypeId() == null) { |
| | | continue; |
| | | } |
| | | typeCountMap.merge(machinery.getMachineryTypeId(), 1L, Long::sum); |
| | | } |
| | | List<MesDvMachineryTypeDO> typeList = machineryTypeMapper.selectList(); |
| | | Map<Long, String> typeNameMap = new HashMap<>(); |
| | | for (MesDvMachineryTypeDO type : typeList) { |
| | | typeNameMap.put(type.getId(), type.getName()); |
| | | } |
| | | List<MesDvStatisticsRespVO.TypeItem> typeDistribution = new ArrayList<>(); |
| | | typeCountMap.forEach((typeId, count) -> { |
| | | MesDvStatisticsRespVO.TypeItem item = new MesDvStatisticsRespVO.TypeItem(); |
| | | item.setMachineryTypeId(typeId); |
| | | item.setMachineryTypeName(typeNameMap.get(typeId)); |
| | | item.setCount(count); |
| | | typeDistribution.add(item); |
| | | }); |
| | | vo.setTypeDistribution(typeDistribution); |
| | | return success(vo); |
| | | } |
| | | |
| | | @GetMapping("/oee") |
| | | @Operation(summary = "è®¾å¤ OEE ç»è®¡ï¼ç¨¼å¨çÃæ§è½å¼å¨çÃè¯åçï¼") |
| | | public CommonResult<MesDvOeeRespVO> getOee( |
| | | @RequestParam(value = "startTime", required = false) |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime, |
| | | @RequestParam(value = "endTime", required = false) |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime) { |
| | | // 1. æ¥è¯¢ç»è®¡åºé´å
ä»»å¡ï¼æå®ææ¶é´è¿æ»¤ï¼ä¸ä¼ åºé´åç»è®¡å
¨é¨ï¼ |
| | | List<MesProTaskDO> allTasks = taskMapper.selectList(new LambdaQueryWrapperX<MesProTaskDO>() |
| | | .betweenIfPresent(MesProTaskDO::getFinishDate, new LocalDateTime[]{startTime, endTime})); |
| | | |
| | | // 2. æ´ä½ OEE |
| | | MesDvOeeRespVO vo = new MesDvOeeRespVO(); |
| | | OeeCalc overall = calcOee(allTasks); |
| | | vo.setOverallOee(overall.oee); |
| | | vo.setOverallAvailability(overall.availability); |
| | | vo.setOverallPerformance(overall.performance); |
| | | vo.setOverallQuality(overall.quality); |
| | | vo.setTaskCount((long) allTasks.size()); |
| | | |
| | | // 3. æè®¾å¤èåï¼ä»»å¡æå·¥ä½ç«åç»ï¼è®¾å¤éè¿ workstationId å
³èå·¥ä½ç« |
| | | Map<Long, List<MesProTaskDO>> tasksByWorkstation = new HashMap<>(); |
| | | for (MesProTaskDO task : allTasks) { |
| | | if (task.getWorkstationId() == null) { |
| | | continue; |
| | | } |
| | | tasksByWorkstation.computeIfAbsent(task.getWorkstationId(), k -> new ArrayList<>()).add(task); |
| | | } |
| | | List<MesDvOeeRespVO.MachineryOee> items = new ArrayList<>(); |
| | | for (MesDvMachineryDO machinery : machineryMapper.selectList()) { |
| | | if (machinery.getWorkstationId() == null) { |
| | | continue; // æªç»å®å·¥ä½ç«ï¼æ æ³å
³èä»»å¡ |
| | | } |
| | | List<MesProTaskDO> tasks = tasksByWorkstation.getOrDefault(machinery.getWorkstationId(), Collections.emptyList()); |
| | | OeeCalc calc = calcOee(tasks); |
| | | MesDvOeeRespVO.MachineryOee item = new MesDvOeeRespVO.MachineryOee(); |
| | | item.setMachineryId(machinery.getId()); |
| | | item.setMachineryCode(machinery.getCode()); |
| | | item.setMachineryName(machinery.getName()); |
| | | item.setOee(calc.oee); |
| | | item.setAvailability(calc.availability); |
| | | item.setPerformance(calc.performance); |
| | | item.setQuality(calc.quality); |
| | | item.setTaskCount((long) tasks.size()); |
| | | item.setPlannedQuantity(calc.plannedQuantity); |
| | | item.setProducedQuantity(calc.producedQuantity); |
| | | items.add(item); |
| | | } |
| | | vo.setItems(items); |
| | | return success(vo); |
| | | } |
| | | |
| | | private static OeeCalc calcOee(List<MesProTaskDO> tasks) { |
| | | double totalPlannedHours = 0, totalActualHours = 0; |
| | | BigDecimal totalPlannedQty = BigDecimal.ZERO, totalProducedQty = BigDecimal.ZERO, totalQualifyQty = BigDecimal.ZERO; |
| | | for (MesProTaskDO task : tasks) { |
| | | if (task.getDuration() != null) { |
| | | totalPlannedHours += task.getDuration() * 8D; // 1 工使¥ = 8 å°æ¶ |
| | | } |
| | | if (task.getFirstFeedbackTime() != null && task.getLastFeedbackTime() != null |
| | | && task.getLastFeedbackTime().isAfter(task.getFirstFeedbackTime())) { |
| | | totalActualHours += Duration.between(task.getFirstFeedbackTime(), task.getLastFeedbackTime()).toMinutes() / 60D; |
| | | } |
| | | if (task.getQuantity() != null) { |
| | | totalPlannedQty = totalPlannedQty.add(task.getQuantity()); |
| | | } |
| | | if (task.getProducedQuantity() != null) { |
| | | totalProducedQty = totalProducedQty.add(task.getProducedQuantity()); |
| | | } |
| | | if (task.getQualifyQuantity() != null) { |
| | | totalQualifyQty = totalQualifyQty.add(task.getQualifyQuantity()); |
| | | } |
| | | } |
| | | OeeCalc calc = new OeeCalc(); |
| | | // 稼å¨ç = å®é
æ¥å·¥æ¶é¿ / è®¡åæ¶é¿ï¼æ è®¡åæ¶é¿æ¶æ 1 å¤çï¼ |
| | | calc.availability = percent(totalPlannedHours > 0 ? Math.min(1D, totalActualHours / totalPlannedHours) : 1D); |
| | | // æ§è½å¼å¨ç = å®é
产é / 计å产é |
| | | calc.performance = totalPlannedQty.compareTo(BigDecimal.ZERO) > 0 |
| | | ? percent(Math.min(1D, totalProducedQty.divide(totalPlannedQty, 4, RoundingMode.HALF_UP).doubleValue())) : 0D; |
| | | // è¯åç = åæ ¼å / å®é
产éï¼æ äº§éæ¶æ 1 å¤çï¼ |
| | | calc.quality = totalProducedQty.compareTo(BigDecimal.ZERO) > 0 |
| | | ? percent(Math.min(1D, totalQualifyQty.divide(totalProducedQty, 4, RoundingMode.HALF_UP).doubleValue())) : 100D; |
| | | // OEE(%) = 稼å¨ç(%) à æ§è½(%) à è¯åç(%) / 10000ï¼ä¿ç两ä½å°æ° |
| | | calc.oee = Math.round(calc.availability * calc.performance * calc.quality / 10000D * 100D) / 100D; |
| | | calc.plannedQuantity = totalPlannedQty; |
| | | calc.producedQuantity = totalProducedQty; |
| | | return calc; |
| | | } |
| | | |
| | | /** æ¯ä¾(0-1) 转ç¾åæ¯ï¼ä¿ç两ä½å°æ° */ |
| | | private static double percent(double ratio) { |
| | | return Math.round(ratio * 10000D) / 100D; |
| | | } |
| | | |
| | | private static class OeeCalc { |
| | | private Double availability; |
| | | private Double performance; |
| | | private Double quality; |
| | | private Double oee; |
| | | private BigDecimal plannedQuantity; |
| | | private BigDecimal producedQuantity; |
| | | } |
| | | |
| | | } |