| New file |
| | |
| | | package com.zy.asrs.controller; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.core.annotations.ManagerAuth; |
| | | import com.core.common.R; |
| | | import com.zy.asrs.service.WrkAnalysisService; |
| | | import com.zy.common.web.BaseController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.Map; |
| | | |
| | | @RestController |
| | | public class WrkAnalysisController extends BaseController { |
| | | |
| | | private final WrkAnalysisService wrkAnalysisService; |
| | | |
| | | public WrkAnalysisController(WrkAnalysisService wrkAnalysisService) { |
| | | this.wrkAnalysisService = wrkAnalysisService; |
| | | } |
| | | |
| | | @RequestMapping("/wrkAnalysis/options/auth") |
| | | @ManagerAuth |
| | | public R options() { |
| | | return R.ok(wrkAnalysisService.queryOptions()); |
| | | } |
| | | |
| | | @RequestMapping("/wrkAnalysis/list/auth") |
| | | @ManagerAuth |
| | | public R list(@RequestParam(defaultValue = "1") Integer curr, |
| | | @RequestParam(defaultValue = "20") Integer limit, |
| | | @RequestParam Map<String, Object> param) { |
| | | excludeTrash(param); |
| | | return R.ok(wrkAnalysisService.queryList(curr, limit, param)); |
| | | } |
| | | |
| | | @PostMapping("/wrkAnalysis/analyze/auth") |
| | | @ManagerAuth |
| | | public R analyze(@RequestBody(required = false) JSONObject param) { |
| | | return R.ok(wrkAnalysisService.analyze(param)); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | @Data |
| | | @TableName("asr_wrk_analysis") |
| | | public class WrkAnalysis implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @ApiModelProperty(value = "工作号") |
| | | @TableId(value = "wrk_no", type = IdType.INPUT) |
| | | private Integer wrkNo; |
| | | |
| | | @ApiModelProperty(value = "WMS任务号") |
| | | @TableField("wms_wrk_no") |
| | | private String wmsWrkNo; |
| | | |
| | | @ApiModelProperty(value = "入出库类型") |
| | | @TableField("io_type") |
| | | private Integer ioType; |
| | | |
| | | @ApiModelProperty(value = "最终工作状态") |
| | | @TableField("final_wrk_sts") |
| | | private Long finalWrkSts; |
| | | |
| | | @ApiModelProperty(value = "源站") |
| | | @TableField("source_sta_no") |
| | | private Integer sourceStaNo; |
| | | |
| | | @ApiModelProperty(value = "目标站") |
| | | @TableField("sta_no") |
| | | private Integer staNo; |
| | | |
| | | @ApiModelProperty(value = "源库位") |
| | | @TableField("source_loc_no") |
| | | private String sourceLocNo; |
| | | |
| | | @ApiModelProperty(value = "目标库位") |
| | | @TableField("loc_no") |
| | | private String locNo; |
| | | |
| | | @ApiModelProperty(value = "堆垛机号") |
| | | @TableField("crn_no") |
| | | private Integer crnNo; |
| | | |
| | | @ApiModelProperty(value = "双工位堆垛机号") |
| | | @TableField("dual_crn_no") |
| | | private Integer dualCrnNo; |
| | | |
| | | @ApiModelProperty(value = "RGV号") |
| | | @TableField("rgv_no") |
| | | private Integer rgvNo; |
| | | |
| | | @ApiModelProperty(value = "创建时间") |
| | | @TableField("appe_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date appeTime; |
| | | |
| | | @ApiModelProperty(value = "完成时间") |
| | | @TableField("finish_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date finishTime; |
| | | |
| | | @ApiModelProperty(value = "总耗时毫秒") |
| | | @TableField("total_duration_ms") |
| | | private Long totalDurationMs; |
| | | |
| | | @ApiModelProperty(value = "站点开始时间") |
| | | @TableField("station_start_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date stationStartTime; |
| | | |
| | | @ApiModelProperty(value = "站点结束时间") |
| | | @TableField("station_end_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date stationEndTime; |
| | | |
| | | @ApiModelProperty(value = "站点耗时毫秒") |
| | | @TableField("station_duration_ms") |
| | | private Long stationDurationMs; |
| | | |
| | | @ApiModelProperty(value = "堆垛机开始时间") |
| | | @TableField("crane_start_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date craneStartTime; |
| | | |
| | | @ApiModelProperty(value = "堆垛机结束时间") |
| | | @TableField("crane_end_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date craneEndTime; |
| | | |
| | | @ApiModelProperty(value = "堆垛机耗时毫秒") |
| | | @TableField("crane_duration_ms") |
| | | private Long craneDurationMs; |
| | | |
| | | @ApiModelProperty(value = "是否故障") |
| | | @TableField("has_fault") |
| | | private Integer hasFault; |
| | | |
| | | @ApiModelProperty(value = "故障次数") |
| | | @TableField("fault_count") |
| | | private Integer faultCount; |
| | | |
| | | @ApiModelProperty(value = "故障耗时毫秒") |
| | | @TableField("fault_duration_ms") |
| | | private Long faultDurationMs; |
| | | |
| | | @ApiModelProperty(value = "单堆垛机故障次数") |
| | | @TableField("crn_fault_count") |
| | | private Integer crnFaultCount; |
| | | |
| | | @ApiModelProperty(value = "单堆垛机故障耗时毫秒") |
| | | @TableField("crn_fault_duration_ms") |
| | | private Long crnFaultDurationMs; |
| | | |
| | | @ApiModelProperty(value = "双工位堆垛机故障次数") |
| | | @TableField("dual_crn_fault_count") |
| | | private Integer dualCrnFaultCount; |
| | | |
| | | @ApiModelProperty(value = "双工位堆垛机故障耗时毫秒") |
| | | @TableField("dual_crn_fault_duration_ms") |
| | | private Long dualCrnFaultDurationMs; |
| | | |
| | | @ApiModelProperty(value = "RGV故障次数") |
| | | @TableField("rgv_fault_count") |
| | | private Integer rgvFaultCount; |
| | | |
| | | @ApiModelProperty(value = "RGV故障耗时毫秒") |
| | | @TableField("rgv_fault_duration_ms") |
| | | private Long rgvFaultDurationMs; |
| | | |
| | | @ApiModelProperty(value = "数据完整性") |
| | | @TableField("metric_completeness") |
| | | private String metricCompleteness; |
| | | |
| | | @ApiModelProperty(value = "创建时间") |
| | | @TableField("create_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date createTime; |
| | | |
| | | @ApiModelProperty(value = "更新时间") |
| | | @TableField("update_time") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date updateTime; |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.zy.asrs.entity.WrkAnalysis; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | @Mapper |
| | | @Repository |
| | | public interface WrkAnalysisMapper extends BaseMapper<WrkAnalysis> { |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.service; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.zy.asrs.entity.WrkAnalysis; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Date; |
| | | import java.util.Map; |
| | | |
| | | public interface WrkAnalysisService extends IService<WrkAnalysis> { |
| | | |
| | | String METRIC_COMPLETE = "COMPLETE"; |
| | | String METRIC_PARTIAL = "PARTIAL"; |
| | | |
| | | void initForTask(WrkMast wrkMast); |
| | | |
| | | void markInboundStationStart(WrkMast wrkMast, Date operateTime); |
| | | |
| | | void markOutboundStationStart(WrkMast wrkMast, Date operateTime); |
| | | |
| | | boolean completeInboundStationRun(WrkMast wrkMast, Date operateTime); |
| | | |
| | | void markOutboundStationComplete(WrkMast wrkMast, Date operateTime); |
| | | |
| | | void markCraneStart(WrkMast wrkMast, Date operateTime); |
| | | |
| | | void markCraneComplete(WrkMast wrkMast, Date operateTime, Long finalWrkSts); |
| | | |
| | | void finishTask(WrkMast wrkMast, Date finishTime); |
| | | |
| | | Map<Integer, WrkAnalysis> mapByWrkNos(Collection<Integer> wrkNos); |
| | | |
| | | Map<String, Object> queryOptions(); |
| | | |
| | | Map<String, Object> queryList(Integer curr, Integer limit, Map<String, Object> param); |
| | | |
| | | Map<String, Object> analyze(JSONObject param); |
| | | } |
| | |
| | | taskDataList.add(t); |
| | | } |
| | | |
| | | List<WrkMast> inTasks = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.INBOUND_DEVICE_RUN.sts)); |
| | | List<WrkMast> inTasks = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts)); |
| | | for (WrkMast wrkMast : inTasks) { |
| | | HashMap<String, Object> t = new HashMap<>(); |
| | | t.put("taskId", wrkMast.getWrkNo()); |
| New file |
| | |
| | | package com.zy.asrs.service.impl; |
| | | |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.core.common.Cools; |
| | | import com.core.common.DateUtils; |
| | | import com.zy.asrs.entity.*; |
| | | import com.zy.asrs.mapper.WrkAnalysisMapper; |
| | | import com.zy.asrs.service.*; |
| | | import com.zy.core.enums.WrkIoType; |
| | | import com.zy.core.enums.WrkStsType; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service("wrkAnalysisService") |
| | | public class WrkAnalysisServiceImpl extends ServiceImpl<WrkAnalysisMapper, WrkAnalysis> implements WrkAnalysisService { |
| | | |
| | | private static final List<Long> DEFAULT_FINAL_STATUSES = Arrays.asList( |
| | | WrkStsType.COMPLETE_INBOUND.sts, |
| | | WrkStsType.SETTLE_INBOUND.sts, |
| | | WrkStsType.COMPLETE_OUTBOUND.sts, |
| | | WrkStsType.SETTLE_OUTBOUND.sts, |
| | | WrkStsType.COMPLETE_LOC_MOVE.sts |
| | | ); |
| | | private static final String MODE_TASK = "TASK"; |
| | | private static final String MODE_TIME = "TIME"; |
| | | private static final String TIME_FIELD_FINISH = "finish_time"; |
| | | private static final String TIME_FIELD_APPE = "appe_time"; |
| | | private static final String DEVICE_TYPE_CRN = "CRN"; |
| | | private static final String DEVICE_TYPE_DUAL_CRN = "DUAL_CRN"; |
| | | private static final String DEVICE_TYPE_RGV = "RGV"; |
| | | private static final int DURATION_COMPARE_LIMIT = 20; |
| | | |
| | | private final WrkMastLogService wrkMastLogService; |
| | | private final BasCrnpErrLogService basCrnpErrLogService; |
| | | private final BasDualCrnpErrLogService basDualCrnpErrLogService; |
| | | private final BasRgvErrLogService basRgvErrLogService; |
| | | private final BasStationService basStationService; |
| | | private final BasWrkStatusService basWrkStatusService; |
| | | private final WrkMastService wrkMastService; |
| | | |
| | | public WrkAnalysisServiceImpl(WrkMastLogService wrkMastLogService, |
| | | BasCrnpErrLogService basCrnpErrLogService, |
| | | BasDualCrnpErrLogService basDualCrnpErrLogService, |
| | | BasRgvErrLogService basRgvErrLogService, |
| | | BasStationService basStationService, |
| | | BasWrkStatusService basWrkStatusService, |
| | | WrkMastService wrkMastService) { |
| | | this.wrkMastLogService = wrkMastLogService; |
| | | this.basCrnpErrLogService = basCrnpErrLogService; |
| | | this.basDualCrnpErrLogService = basDualCrnpErrLogService; |
| | | this.basRgvErrLogService = basRgvErrLogService; |
| | | this.basStationService = basStationService; |
| | | this.basWrkStatusService = basWrkStatusService; |
| | | this.wrkMastService = wrkMastService; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void initForTask(WrkMast wrkMast) { |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null) { |
| | | return; |
| | | } |
| | | WrkAnalysis entity = this.getById(wrkMast.getWrkNo()); |
| | | Date now = new Date(); |
| | | if (entity == null) { |
| | | entity = new WrkAnalysis(); |
| | | entity.setWrkNo(wrkMast.getWrkNo()); |
| | | entity.setCreateTime(now); |
| | | } |
| | | syncBaseFields(entity, wrkMast); |
| | | entity.setHasFault(defaultInt(entity.getHasFault())); |
| | | entity.setFaultCount(defaultInt(entity.getFaultCount())); |
| | | entity.setFaultDurationMs(defaultLong(entity.getFaultDurationMs())); |
| | | entity.setCrnFaultCount(defaultInt(entity.getCrnFaultCount())); |
| | | entity.setCrnFaultDurationMs(defaultLong(entity.getCrnFaultDurationMs())); |
| | | entity.setDualCrnFaultCount(defaultInt(entity.getDualCrnFaultCount())); |
| | | entity.setDualCrnFaultDurationMs(defaultLong(entity.getDualCrnFaultDurationMs())); |
| | | entity.setRgvFaultCount(defaultInt(entity.getRgvFaultCount())); |
| | | entity.setRgvFaultDurationMs(defaultLong(entity.getRgvFaultDurationMs())); |
| | | entity.setMetricCompleteness(METRIC_PARTIAL); |
| | | entity.setUpdateTime(now); |
| | | this.saveOrUpdate(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void markInboundStationStart(WrkMast wrkMast, Date operateTime) { |
| | | WrkAnalysis entity = ensureRecord(wrkMast); |
| | | if (entity == null) { |
| | | return; |
| | | } |
| | | Date time = safeDate(operateTime); |
| | | entity.setStationStartTime(time); |
| | | entity.setSourceStaNo(wrkMast.getSourceStaNo()); |
| | | entity.setStaNo(wrkMast.getStaNo()); |
| | | entity.setFinalWrkSts(WrkStsType.INBOUND_STATION_RUN.sts); |
| | | entity.setUpdateTime(time); |
| | | this.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void markOutboundStationStart(WrkMast wrkMast, Date operateTime) { |
| | | WrkAnalysis entity = ensureRecord(wrkMast); |
| | | if (entity == null) { |
| | | return; |
| | | } |
| | | Date time = safeDate(operateTime); |
| | | entity.setStationStartTime(time); |
| | | entity.setFinalWrkSts(WrkStsType.STATION_RUN.sts); |
| | | entity.setUpdateTime(time); |
| | | this.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean completeInboundStationRun(WrkMast wrkMast, Date operateTime) { |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null || !Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_STATION_RUN.sts)) { |
| | | return false; |
| | | } |
| | | WrkMast updateEntity = new WrkMast(); |
| | | updateEntity.setWrkNo(wrkMast.getWrkNo()); |
| | | updateEntity.setWrkSts(WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts); |
| | | Date now = safeDate(operateTime); |
| | | updateEntity.setIoTime(now); |
| | | updateEntity.setModiTime(now); |
| | | boolean updated = wrkMast.getWrkNo() != null && wrkMastService.update(updateEntity, new QueryWrapper<WrkMast>() |
| | | .eq("wrk_no", wrkMast.getWrkNo()) |
| | | .eq("wrk_sts", WrkStsType.INBOUND_STATION_RUN.sts)); |
| | | if (!updated) { |
| | | return false; |
| | | } |
| | | WrkAnalysis entity = ensureRecord(wrkMast); |
| | | if (entity != null) { |
| | | entity.setFinalWrkSts(WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts); |
| | | entity.setStationEndTime(now); |
| | | if (entity.getStationStartTime() != null) { |
| | | entity.setStationDurationMs(durationMs(entity.getStationStartTime(), now)); |
| | | } |
| | | entity.setUpdateTime(now); |
| | | this.updateById(entity); |
| | | } |
| | | wrkMast.setWrkSts(WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void markOutboundStationComplete(WrkMast wrkMast, Date operateTime) { |
| | | WrkAnalysis entity = ensureRecord(wrkMast); |
| | | if (entity == null) { |
| | | return; |
| | | } |
| | | Date time = safeDate(operateTime); |
| | | entity.setFinalWrkSts(WrkStsType.STATION_RUN_COMPLETE.sts); |
| | | entity.setStationEndTime(time); |
| | | if (entity.getStationStartTime() != null) { |
| | | entity.setStationDurationMs(durationMs(entity.getStationStartTime(), time)); |
| | | } |
| | | entity.setUpdateTime(time); |
| | | this.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void markCraneStart(WrkMast wrkMast, Date operateTime) { |
| | | WrkAnalysis entity = ensureRecord(wrkMast); |
| | | if (entity == null) { |
| | | return; |
| | | } |
| | | Date time = safeDate(operateTime); |
| | | entity.setCraneStartTime(time); |
| | | entity.setCrnNo(wrkMast.getCrnNo()); |
| | | entity.setDualCrnNo(wrkMast.getDualCrnNo()); |
| | | entity.setRgvNo(wrkMast.getRgvNo()); |
| | | entity.setFinalWrkSts(wrkMast.getWrkSts()); |
| | | entity.setUpdateTime(time); |
| | | if (Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id) && entity.getStationDurationMs() == null) { |
| | | entity.setStationDurationMs(0L); |
| | | } |
| | | this.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void markCraneComplete(WrkMast wrkMast, Date operateTime, Long finalWrkSts) { |
| | | WrkAnalysis entity = ensureRecord(wrkMast); |
| | | if (entity == null) { |
| | | return; |
| | | } |
| | | Date time = safeDate(operateTime); |
| | | entity.setCraneEndTime(time); |
| | | if (entity.getCraneStartTime() != null) { |
| | | entity.setCraneDurationMs(durationMs(entity.getCraneStartTime(), time)); |
| | | } |
| | | entity.setFinalWrkSts(finalWrkSts); |
| | | entity.setUpdateTime(time); |
| | | this.updateById(entity); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void finishTask(WrkMast wrkMast, Date finishTime) { |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null) { |
| | | return; |
| | | } |
| | | WrkAnalysis entity = ensureRecord(wrkMast); |
| | | if (entity == null) { |
| | | return; |
| | | } |
| | | Date time = safeDate(finishTime); |
| | | syncBaseFields(entity, wrkMast); |
| | | entity.setFinishTime(time); |
| | | if (entity.getAppeTime() != null) { |
| | | entity.setTotalDurationMs(durationMs(entity.getAppeTime(), time)); |
| | | } |
| | | if (Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id) && entity.getStationDurationMs() == null) { |
| | | entity.setStationDurationMs(0L); |
| | | } |
| | | FaultSummary faultSummary = buildFaultSummary(wrkMast.getWrkNo(), time); |
| | | entity.setHasFault(faultSummary.hasFault); |
| | | entity.setFaultCount(faultSummary.totalCount); |
| | | entity.setFaultDurationMs(faultSummary.totalDurationMs); |
| | | entity.setCrnFaultCount(faultSummary.crnCount); |
| | | entity.setCrnFaultDurationMs(faultSummary.crnDurationMs); |
| | | entity.setDualCrnFaultCount(faultSummary.dualCount); |
| | | entity.setDualCrnFaultDurationMs(faultSummary.dualDurationMs); |
| | | entity.setRgvFaultCount(faultSummary.rgvCount); |
| | | entity.setRgvFaultDurationMs(faultSummary.rgvDurationMs); |
| | | entity.setMetricCompleteness(resolveMetricCompleteness(wrkMast, entity)); |
| | | entity.setUpdateTime(time); |
| | | this.saveOrUpdate(entity); |
| | | } |
| | | |
| | | @Override |
| | | public Map<Integer, WrkAnalysis> mapByWrkNos(Collection<Integer> wrkNos) { |
| | | Map<Integer, WrkAnalysis> result = new LinkedHashMap<>(); |
| | | if (wrkNos == null || wrkNos.isEmpty()) { |
| | | return result; |
| | | } |
| | | List<WrkAnalysis> list = this.listByIds(new LinkedHashSet<>(wrkNos)); |
| | | for (WrkAnalysis item : list) { |
| | | if (item != null && item.getWrkNo() != null) { |
| | | result.put(item.getWrkNo(), item); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> queryOptions() { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | List<Map<String, Object>> ioTypes = new ArrayList<>(); |
| | | ioTypes.add(option("1", "IN", "入库", WrkIoType.IN.id)); |
| | | ioTypes.add(option("2", "OUT", "出库", WrkIoType.OUT.id)); |
| | | ioTypes.add(option("3", "LOC_MOVE", "移库", WrkIoType.LOC_MOVE.id)); |
| | | List<Map<String, Object>> timeFields = new ArrayList<>(); |
| | | timeFields.add(option(TIME_FIELD_FINISH, TIME_FIELD_FINISH, "完成时间", TIME_FIELD_FINISH)); |
| | | timeFields.add(option(TIME_FIELD_APPE, TIME_FIELD_APPE, "创建时间", TIME_FIELD_APPE)); |
| | | List<Map<String, Object>> deviceTypes = new ArrayList<>(); |
| | | deviceTypes.add(option(DEVICE_TYPE_CRN, DEVICE_TYPE_CRN, "单堆垛机", DEVICE_TYPE_CRN)); |
| | | deviceTypes.add(option(DEVICE_TYPE_DUAL_CRN, DEVICE_TYPE_DUAL_CRN, "双工位堆垛机", DEVICE_TYPE_DUAL_CRN)); |
| | | deviceTypes.add(option(DEVICE_TYPE_RGV, DEVICE_TYPE_RGV, "RGV", DEVICE_TYPE_RGV)); |
| | | List<Map<String, Object>> statusList = basWrkStatusService.list(new QueryWrapper<BasWrkStatus>().orderByAsc("wrk_sts")) |
| | | .stream() |
| | | .map(item -> option(String.valueOf(item.getWrkSts()), String.valueOf(item.getWrkSts()), item.getWrkDesc(), item.getWrkSts())) |
| | | .collect(Collectors.toList()); |
| | | List<Map<String, Object>> stations = basStationService.list(new QueryWrapper<BasStation>().orderByAsc("station_id")) |
| | | .stream() |
| | | .map(item -> { |
| | | String label = item.getStationAlias(); |
| | | if (Cools.isEmpty(label)) { |
| | | label = "站点" + item.getStationId(); |
| | | } else { |
| | | label = item.getStationId() + " - " + label; |
| | | } |
| | | return option(String.valueOf(item.getStationId()), String.valueOf(item.getStationId()), label, item.getStationId()); |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | result.put("ioTypes", ioTypes); |
| | | result.put("timeFields", timeFields); |
| | | result.put("deviceTypes", deviceTypes); |
| | | result.put("statuses", statusList); |
| | | result.put("stations", stations); |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> queryList(Integer curr, Integer limit, Map<String, Object> param) { |
| | | int pageNo = curr == null || curr <= 0 ? 1 : curr; |
| | | int pageSize = limit == null || limit <= 0 ? 20 : limit; |
| | | QueryWrapper<WrkMastLog> wrapper = buildHistoryLogWrapper(param); |
| | | Page<WrkMastLog> page = wrkMastLogService.page(new Page<>(pageNo, pageSize), wrapper); |
| | | List<WrkMastLog> logList = page.getRecords(); |
| | | List<Integer> wrkNos = logList.stream().map(WrkMastLog::getWrkNo).filter(Objects::nonNull).collect(Collectors.toList()); |
| | | Map<Integer, WrkAnalysis> analysisMap = mapByWrkNos(wrkNos); |
| | | List<Map<String, Object>> records = new ArrayList<>(); |
| | | for (WrkMastLog log : logList) { |
| | | records.add(toListItem(log, analysisMap.get(log.getWrkNo()))); |
| | | } |
| | | Map<String, Object> data = new LinkedHashMap<>(); |
| | | data.put("records", records); |
| | | data.put("total", page.getTotal()); |
| | | data.put("size", page.getSize()); |
| | | data.put("current", page.getCurrent()); |
| | | return data; |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> analyze(JSONObject param) { |
| | | JSONObject request = param == null ? new JSONObject() : param; |
| | | String mode = upperTrim(request.getString("mode")); |
| | | if (Cools.isEmpty(mode)) { |
| | | mode = MODE_TIME; |
| | | } |
| | | QueryWrapper<WrkAnalysis> wrapper = buildAnalysisWrapper(request, mode); |
| | | List<WrkAnalysis> list = this.list(wrapper); |
| | | list.sort(Comparator.comparing(WrkAnalysis::getFinishTime, Comparator.nullsLast(Date::compareTo)) |
| | | .thenComparing(WrkAnalysis::getWrkNo, Comparator.nullsLast(Integer::compareTo))); |
| | | Collections.reverse(list); |
| | | String timeField = TIME_FIELD_FINISH; |
| | | if (TIME_FIELD_APPE.equalsIgnoreCase(request.getString("timeField"))) { |
| | | timeField = TIME_FIELD_APPE; |
| | | } |
| | | return buildAnalysisResult(list, mode, timeField, request); |
| | | } |
| | | |
| | | private QueryWrapper<WrkMastLog> buildHistoryLogWrapper(Map<String, Object> param) { |
| | | QueryWrapper<WrkMastLog> wrapper = new QueryWrapper<>(); |
| | | String keyword = stringValue(param.get("keyword")); |
| | | if (!Cools.isEmpty(keyword)) { |
| | | wrapper.and(w -> w.like("wrk_no", keyword) |
| | | .or().like("wms_wrk_no", keyword) |
| | | .or().like("loc_no", keyword) |
| | | .or().like("source_loc_no", keyword) |
| | | .or().like("barcode", keyword)); |
| | | } |
| | | Integer ioType = parseInteger(param.get("ioType")); |
| | | if (ioType != null) { |
| | | wrapper.eq("io_type", ioType); |
| | | } |
| | | Long finalWrkSts = parseLong(param.get("finalWrkSts")); |
| | | if (finalWrkSts != null) { |
| | | wrapper.eq("wrk_sts", finalWrkSts); |
| | | } else { |
| | | wrapper.in("wrk_sts", DEFAULT_FINAL_STATUSES); |
| | | } |
| | | Integer sourceStaNo = parseInteger(param.get("sourceStaNo")); |
| | | if (sourceStaNo != null) { |
| | | wrapper.eq("source_sta_no", sourceStaNo); |
| | | } |
| | | Integer staNo = parseInteger(param.get("staNo")); |
| | | if (staNo != null) { |
| | | wrapper.eq("sta_no", staNo); |
| | | } |
| | | applyDeviceTypeFilter(wrapper, upperTrim(stringValue(param.get("deviceType")))); |
| | | applyRange(wrapper, "appe_time", stringValue(param.get("appeTimeRange"))); |
| | | applyRange(wrapper, "modi_time", stringValue(param.get("finishTimeRange"))); |
| | | wrapper.orderByDesc("modi_time", "wrk_no"); |
| | | return wrapper; |
| | | } |
| | | |
| | | private QueryWrapper<WrkAnalysis> buildAnalysisWrapper(JSONObject request, String mode) { |
| | | QueryWrapper<WrkAnalysis> wrapper = new QueryWrapper<>(); |
| | | Integer ioType = parseInteger(request.get("ioType")); |
| | | if (ioType != null) { |
| | | wrapper.eq("io_type", ioType); |
| | | } |
| | | Long finalWrkSts = parseLong(request.get("finalWrkSts")); |
| | | if (finalWrkSts != null) { |
| | | wrapper.eq("final_wrk_sts", finalWrkSts); |
| | | } else { |
| | | wrapper.in("final_wrk_sts", DEFAULT_FINAL_STATUSES); |
| | | } |
| | | Integer sourceStaNo = parseInteger(request.get("sourceStaNo")); |
| | | if (sourceStaNo != null) { |
| | | wrapper.eq("source_sta_no", sourceStaNo); |
| | | } |
| | | Integer staNo = parseInteger(request.get("staNo")); |
| | | if (staNo != null) { |
| | | wrapper.eq("sta_no", staNo); |
| | | } |
| | | String deviceType = upperTrim(request.getString("deviceType")); |
| | | if (DEVICE_TYPE_CRN.equals(deviceType)) { |
| | | wrapper.isNotNull("crn_no"); |
| | | } else if (DEVICE_TYPE_DUAL_CRN.equals(deviceType)) { |
| | | wrapper.isNotNull("dual_crn_no"); |
| | | } else if (DEVICE_TYPE_RGV.equals(deviceType)) { |
| | | wrapper.isNotNull("rgv_no"); |
| | | } |
| | | if (MODE_TASK.equals(mode)) { |
| | | List<Integer> wrkNos = parseWrkNos(request.get("wrkNos")); |
| | | if (wrkNos.isEmpty()) { |
| | | wrapper.eq("wrk_no", -1); |
| | | return wrapper; |
| | | } |
| | | wrapper.in("wrk_no", wrkNos); |
| | | } else { |
| | | String timeField = TIME_FIELD_APPE.equalsIgnoreCase(request.getString("timeField")) ? TIME_FIELD_APPE : TIME_FIELD_FINISH; |
| | | Long startTime = parseLong(request.get("startTime")); |
| | | Long endTime = parseLong(request.get("endTime")); |
| | | if (startTime != null && endTime != null) { |
| | | wrapper.ge(timeField, new Date(startTime)); |
| | | wrapper.le(timeField, new Date(endTime)); |
| | | } else { |
| | | wrapper.eq("wrk_no", -1); |
| | | return wrapper; |
| | | } |
| | | } |
| | | return wrapper; |
| | | } |
| | | |
| | | private void applyDeviceTypeFilter(QueryWrapper<WrkMastLog> wrapper, String deviceType) { |
| | | if (Cools.isEmpty(deviceType)) { |
| | | return; |
| | | } |
| | | QueryWrapper<WrkAnalysis> analysisWrapper = new QueryWrapper<>(); |
| | | analysisWrapper.select("wrk_no"); |
| | | if (DEVICE_TYPE_CRN.equals(deviceType)) { |
| | | analysisWrapper.isNotNull("crn_no"); |
| | | } else if (DEVICE_TYPE_DUAL_CRN.equals(deviceType)) { |
| | | analysisWrapper.isNotNull("dual_crn_no"); |
| | | } else if (DEVICE_TYPE_RGV.equals(deviceType)) { |
| | | analysisWrapper.isNotNull("rgv_no"); |
| | | } else { |
| | | return; |
| | | } |
| | | List<Integer> wrkNos = this.list(analysisWrapper).stream() |
| | | .map(WrkAnalysis::getWrkNo) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if (wrkNos.isEmpty()) { |
| | | wrapper.eq("wrk_no", -1); |
| | | return; |
| | | } |
| | | wrapper.in("wrk_no", wrkNos); |
| | | } |
| | | |
| | | private Map<String, Object> buildAnalysisResult(List<WrkAnalysis> list, String mode, String timeField, JSONObject request) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("taskCount", list.size()); |
| | | summary.put("avgTotalDurationMs", average(list, item -> item.getTotalDurationMs() != null, WrkAnalysis::getTotalDurationMs)); |
| | | summary.put("avgStationDurationMs", average(list, item -> !METRIC_PARTIAL.equals(item.getMetricCompleteness()) && item.getStationDurationMs() != null, WrkAnalysis::getStationDurationMs)); |
| | | summary.put("avgCraneDurationMs", average(list, item -> !METRIC_PARTIAL.equals(item.getMetricCompleteness()) && item.getCraneDurationMs() != null, WrkAnalysis::getCraneDurationMs)); |
| | | summary.put("faultTaskCount", list.stream().filter(this::hasFault).count()); |
| | | summary.put("faultDurationMs", list.stream().map(WrkAnalysis::getFaultDurationMs).filter(Objects::nonNull).reduce(0L, Long::sum)); |
| | | summary.put("partialTaskCount", list.stream().filter(item -> METRIC_PARTIAL.equals(item.getMetricCompleteness())).count()); |
| | | result.put("summary", summary); |
| | | result.put("durationCompare", buildDurationCompare(list)); |
| | | result.put("trend", buildTrend(list, mode, timeField, request)); |
| | | result.put("faultPie", buildFaultPie(list)); |
| | | result.put("faultDuration", buildFaultDuration(list)); |
| | | result.put("detail", buildDetailRows(list)); |
| | | result.put("timeField", timeField); |
| | | return result; |
| | | } |
| | | |
| | | private List<Map<String, Object>> buildDurationCompare(List<WrkAnalysis> list) { |
| | | return list.stream() |
| | | .limit(DURATION_COMPARE_LIMIT) |
| | | .map(this::toDetailItem) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | private List<Map<String, Object>> buildTrend(List<WrkAnalysis> list, String mode, String timeField, JSONObject request) { |
| | | List<Map<String, Object>> trend = new ArrayList<>(); |
| | | if (list.isEmpty()) { |
| | | return trend; |
| | | } |
| | | long startMs = Long.MAX_VALUE; |
| | | long endMs = Long.MIN_VALUE; |
| | | if (MODE_TIME.equals(mode)) { |
| | | Long reqStart = parseLong(request.get("startTime")); |
| | | Long reqEnd = parseLong(request.get("endTime")); |
| | | if (reqStart != null && reqEnd != null) { |
| | | startMs = reqStart; |
| | | endMs = reqEnd; |
| | | } |
| | | } |
| | | if (startMs == Long.MAX_VALUE || endMs == Long.MIN_VALUE) { |
| | | for (WrkAnalysis item : list) { |
| | | Date date = resolveBucketTime(item, timeField); |
| | | if (date == null) { |
| | | continue; |
| | | } |
| | | long time = date.getTime(); |
| | | startMs = Math.min(startMs, time); |
| | | endMs = Math.max(endMs, time); |
| | | } |
| | | } |
| | | if (startMs == Long.MAX_VALUE || endMs == Long.MIN_VALUE) { |
| | | return trend; |
| | | } |
| | | boolean hourly = endMs - startMs <= 72L * 60L * 60L * 1000L; |
| | | SimpleDateFormat keyFormat = new SimpleDateFormat(hourly ? "yyyy-MM-dd HH:00" : "yyyy-MM-dd"); |
| | | keyFormat.setTimeZone(TimeZone.getDefault()); |
| | | Map<String, BucketAccumulator> bucketMap = new LinkedHashMap<>(); |
| | | List<WrkAnalysis> sorted = new ArrayList<>(list); |
| | | sorted.sort(Comparator.comparing(item -> resolveBucketTime(item, timeField), Comparator.nullsLast(Date::compareTo))); |
| | | for (WrkAnalysis item : sorted) { |
| | | Date bucketTime = resolveBucketTime(item, timeField); |
| | | if (bucketTime == null) { |
| | | continue; |
| | | } |
| | | String key = keyFormat.format(bucketTime); |
| | | BucketAccumulator accumulator = bucketMap.computeIfAbsent(key, k -> new BucketAccumulator()); |
| | | accumulator.taskCount++; |
| | | if (item.getTotalDurationMs() != null) { |
| | | accumulator.totalDurationMs += item.getTotalDurationMs(); |
| | | accumulator.totalDurationCount++; |
| | | } |
| | | if (!METRIC_PARTIAL.equals(item.getMetricCompleteness()) && item.getStationDurationMs() != null) { |
| | | accumulator.stationDurationMs += item.getStationDurationMs(); |
| | | accumulator.stationDurationCount++; |
| | | } |
| | | if (!METRIC_PARTIAL.equals(item.getMetricCompleteness()) && item.getCraneDurationMs() != null) { |
| | | accumulator.craneDurationMs += item.getCraneDurationMs(); |
| | | accumulator.craneDurationCount++; |
| | | } |
| | | } |
| | | for (Map.Entry<String, BucketAccumulator> entry : bucketMap.entrySet()) { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("bucketLabel", entry.getKey()); |
| | | item.put("taskCount", entry.getValue().taskCount); |
| | | item.put("avgTotalDurationMs", entry.getValue().totalDurationCount == 0 ? null : entry.getValue().totalDurationMs / entry.getValue().totalDurationCount); |
| | | item.put("avgStationDurationMs", entry.getValue().stationDurationCount == 0 ? null : entry.getValue().stationDurationMs / entry.getValue().stationDurationCount); |
| | | item.put("avgCraneDurationMs", entry.getValue().craneDurationCount == 0 ? null : entry.getValue().craneDurationMs / entry.getValue().craneDurationCount); |
| | | trend.add(item); |
| | | } |
| | | return trend; |
| | | } |
| | | |
| | | private List<Map<String, Object>> buildFaultPie(List<WrkAnalysis> list) { |
| | | long fault = list.stream().filter(this::hasFault).count(); |
| | | long normal = Math.max(0, list.size() - fault); |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | result.add(slice("故障任务", fault)); |
| | | result.add(slice("无故障任务", normal)); |
| | | return result; |
| | | } |
| | | |
| | | private List<Map<String, Object>> buildFaultDuration(List<WrkAnalysis> list) { |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | result.add(slice("单堆垛机", list.stream().map(WrkAnalysis::getCrnFaultDurationMs).filter(Objects::nonNull).reduce(0L, Long::sum))); |
| | | result.add(slice("双工位堆垛机", list.stream().map(WrkAnalysis::getDualCrnFaultDurationMs).filter(Objects::nonNull).reduce(0L, Long::sum))); |
| | | result.add(slice("RGV", list.stream().map(WrkAnalysis::getRgvFaultDurationMs).filter(Objects::nonNull).reduce(0L, Long::sum))); |
| | | return result; |
| | | } |
| | | |
| | | private List<Map<String, Object>> buildDetailRows(List<WrkAnalysis> list) { |
| | | return list.stream().map(this::toDetailItem).collect(Collectors.toList()); |
| | | } |
| | | |
| | | private Map<String, Object> toListItem(WrkMastLog log, WrkAnalysis analysis) { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("wrkNo", log.getWrkNo()); |
| | | item.put("wmsWrkNo", log.getWmsWrkNo()); |
| | | item.put("wrkSts", log.getWrkSts()); |
| | | item.put("wrkSts$", log.getWrkSts$()); |
| | | item.put("ioType", log.getIoType()); |
| | | item.put("ioType$", log.getIoType$()); |
| | | item.put("sourceStaNo", log.getSourceStaNo()); |
| | | item.put("staNo", log.getStaNo()); |
| | | item.put("sourceLocNo", log.getSourceLocNo()); |
| | | item.put("locNo", log.getLocNo()); |
| | | item.put("barcode", log.getBarcode()); |
| | | item.put("appeTime", log.getAppeTime()); |
| | | item.put("appeTime$", formatDate(log.getAppeTime())); |
| | | item.put("finishTime", analysis == null ? null : analysis.getFinishTime()); |
| | | item.put("finishTime$", analysis == null ? "" : formatDate(analysis.getFinishTime())); |
| | | fillMetrics(item, analysis); |
| | | return item; |
| | | } |
| | | |
| | | private Map<String, Object> toDetailItem(WrkAnalysis item) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("wrkNo", item.getWrkNo()); |
| | | result.put("wmsWrkNo", item.getWmsWrkNo()); |
| | | result.put("ioType", item.getIoType()); |
| | | result.put("ioType$", resolveIoTypeDesc(item.getIoType())); |
| | | result.put("finalWrkSts", item.getFinalWrkSts()); |
| | | result.put("finalWrkSts$", resolveWrkStsDesc(item.getFinalWrkSts())); |
| | | result.put("sourceStaNo", item.getSourceStaNo()); |
| | | result.put("staNo", item.getStaNo()); |
| | | result.put("sourceLocNo", item.getSourceLocNo()); |
| | | result.put("locNo", item.getLocNo()); |
| | | result.put("appeTime", item.getAppeTime()); |
| | | result.put("appeTime$", formatDate(item.getAppeTime())); |
| | | result.put("finishTime", item.getFinishTime()); |
| | | result.put("finishTime$", formatDate(item.getFinishTime())); |
| | | fillMetrics(result, item); |
| | | return result; |
| | | } |
| | | |
| | | private void fillMetrics(Map<String, Object> target, WrkAnalysis analysis) { |
| | | target.put("totalDurationMs", analysis == null ? null : analysis.getTotalDurationMs()); |
| | | target.put("stationDurationMs", analysis == null ? null : analysis.getStationDurationMs()); |
| | | target.put("craneDurationMs", analysis == null ? null : analysis.getCraneDurationMs()); |
| | | target.put("faultCount", analysis == null ? 0 : defaultInt(analysis.getFaultCount())); |
| | | target.put("faultDurationMs", analysis == null ? 0L : defaultLong(analysis.getFaultDurationMs())); |
| | | target.put("hasFault", analysis == null ? 0 : defaultInt(analysis.getHasFault())); |
| | | target.put("crnFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getCrnFaultDurationMs())); |
| | | target.put("dualCrnFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getDualCrnFaultDurationMs())); |
| | | target.put("rgvFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getRgvFaultDurationMs())); |
| | | target.put("metricCompleteness", analysis == null ? METRIC_PARTIAL : analysis.getMetricCompleteness()); |
| | | } |
| | | |
| | | private FaultSummary buildFaultSummary(Integer wrkNo, Date finishTime) { |
| | | FaultSummary summary = new FaultSummary(); |
| | | if (wrkNo == null) { |
| | | return summary; |
| | | } |
| | | List<BasCrnpErrLog> crnList = basCrnpErrLogService.list(new QueryWrapper<BasCrnpErrLog>().eq("wrk_no", wrkNo)); |
| | | List<BasDualCrnpErrLog> dualList = basDualCrnpErrLogService.list(new QueryWrapper<BasDualCrnpErrLog>().eq("wrk_no", wrkNo)); |
| | | List<BasRgvErrLog> rgvList = basRgvErrLogService.list(new QueryWrapper<BasRgvErrLog>().eq("task_no", wrkNo)); |
| | | summary.crnCount = crnList.size(); |
| | | summary.crnDurationMs = durationMs(crnList, finishTime); |
| | | summary.dualCount = dualList.size(); |
| | | summary.dualDurationMs = durationMs(dualList, finishTime); |
| | | summary.rgvCount = rgvList.size(); |
| | | summary.rgvDurationMs = durationMs(rgvList, finishTime); |
| | | summary.totalCount = summary.crnCount + summary.dualCount + summary.rgvCount; |
| | | summary.totalDurationMs = summary.crnDurationMs + summary.dualDurationMs + summary.rgvDurationMs; |
| | | summary.hasFault = summary.totalCount > 0 ? 1 : 0; |
| | | return summary; |
| | | } |
| | | |
| | | private Long durationMs(List<? extends Object> list, Date finishTime) { |
| | | long total = 0L; |
| | | for (Object item : list) { |
| | | Date startTime = null; |
| | | Date endTime = null; |
| | | if (item instanceof BasCrnpErrLog) { |
| | | startTime = ((BasCrnpErrLog) item).getStartTime(); |
| | | endTime = ((BasCrnpErrLog) item).getEndTime(); |
| | | } else if (item instanceof BasDualCrnpErrLog) { |
| | | startTime = ((BasDualCrnpErrLog) item).getStartTime(); |
| | | endTime = ((BasDualCrnpErrLog) item).getEndTime(); |
| | | } else if (item instanceof BasRgvErrLog) { |
| | | startTime = ((BasRgvErrLog) item).getStartTime(); |
| | | endTime = ((BasRgvErrLog) item).getEndTime(); |
| | | } |
| | | if (startTime == null) { |
| | | continue; |
| | | } |
| | | total += durationMs(startTime, endTime == null ? finishTime : endTime); |
| | | } |
| | | return total; |
| | | } |
| | | |
| | | private String resolveMetricCompleteness(WrkMast wrkMast, WrkAnalysis entity) { |
| | | if (wrkMast == null) { |
| | | return METRIC_PARTIAL; |
| | | } |
| | | if (Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id)) { |
| | | return entity.getCraneStartTime() != null && entity.getCraneEndTime() != null ? METRIC_COMPLETE : METRIC_PARTIAL; |
| | | } |
| | | return entity.getStationStartTime() != null |
| | | && entity.getStationEndTime() != null |
| | | && entity.getCraneStartTime() != null |
| | | && entity.getCraneEndTime() != null ? METRIC_COMPLETE : METRIC_PARTIAL; |
| | | } |
| | | |
| | | private WrkAnalysis ensureRecord(WrkMast wrkMast) { |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null) { |
| | | return null; |
| | | } |
| | | WrkAnalysis entity = this.getById(wrkMast.getWrkNo()); |
| | | if (entity != null) { |
| | | syncBaseFields(entity, wrkMast); |
| | | return entity; |
| | | } |
| | | initForTask(wrkMast); |
| | | return this.getById(wrkMast.getWrkNo()); |
| | | } |
| | | |
| | | private void syncBaseFields(WrkAnalysis entity, WrkMast wrkMast) { |
| | | entity.setWrkNo(wrkMast.getWrkNo()); |
| | | entity.setWmsWrkNo(wrkMast.getWmsWrkNo()); |
| | | entity.setIoType(wrkMast.getIoType()); |
| | | entity.setFinalWrkSts(wrkMast.getWrkSts()); |
| | | entity.setSourceStaNo(wrkMast.getSourceStaNo()); |
| | | entity.setStaNo(wrkMast.getStaNo()); |
| | | entity.setSourceLocNo(wrkMast.getSourceLocNo()); |
| | | entity.setLocNo(wrkMast.getLocNo()); |
| | | entity.setCrnNo(wrkMast.getCrnNo()); |
| | | entity.setDualCrnNo(wrkMast.getDualCrnNo()); |
| | | entity.setRgvNo(wrkMast.getRgvNo()); |
| | | entity.setAppeTime(wrkMast.getAppeTime()); |
| | | } |
| | | |
| | | private Date resolveBucketTime(WrkAnalysis item, String timeField) { |
| | | if (TIME_FIELD_APPE.equals(timeField)) { |
| | | return item.getAppeTime(); |
| | | } |
| | | return item.getFinishTime(); |
| | | } |
| | | |
| | | private Map<String, Object> option(String key, String code, String label, Object value) { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("key", key); |
| | | item.put("code", code); |
| | | item.put("label", label); |
| | | item.put("value", value); |
| | | return item; |
| | | } |
| | | |
| | | private Map<String, Object> slice(String name, Object value) { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("name", name); |
| | | item.put("value", value); |
| | | return item; |
| | | } |
| | | |
| | | private String formatDate(Date date) { |
| | | if (date == null) { |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date); |
| | | } |
| | | |
| | | private String resolveWrkStsDesc(Long wrkSts) { |
| | | if (wrkSts == null) { |
| | | return ""; |
| | | } |
| | | BasWrkStatus basWrkStatus = basWrkStatusService.getById(wrkSts); |
| | | if (basWrkStatus != null && !Cools.isEmpty(basWrkStatus.getWrkDesc())) { |
| | | return basWrkStatus.getWrkDesc(); |
| | | } |
| | | try { |
| | | return WrkStsType.query(wrkSts).desc; |
| | | } catch (Exception ignore) { |
| | | return String.valueOf(wrkSts); |
| | | } |
| | | } |
| | | |
| | | private String resolveIoTypeDesc(Integer ioType) { |
| | | if (ioType == null) { |
| | | return ""; |
| | | } |
| | | if (Objects.equals(ioType, WrkIoType.IN.id)) { |
| | | return "入库"; |
| | | } |
| | | if (Objects.equals(ioType, WrkIoType.OUT.id)) { |
| | | return "出库"; |
| | | } |
| | | if (Objects.equals(ioType, WrkIoType.LOC_MOVE.id)) { |
| | | return "移库"; |
| | | } |
| | | return String.valueOf(ioType); |
| | | } |
| | | |
| | | private boolean hasFault(WrkAnalysis item) { |
| | | return item != null && (defaultInt(item.getHasFault()) > 0 || defaultInt(item.getFaultCount()) > 0); |
| | | } |
| | | |
| | | private Long average(List<WrkAnalysis> list, |
| | | java.util.function.Predicate<WrkAnalysis> predicate, |
| | | java.util.function.Function<WrkAnalysis, Long> valueFn) { |
| | | long total = 0L; |
| | | long count = 0L; |
| | | for (WrkAnalysis item : list) { |
| | | if (!predicate.test(item)) { |
| | | continue; |
| | | } |
| | | Long value = valueFn.apply(item); |
| | | if (value == null) { |
| | | continue; |
| | | } |
| | | total += value; |
| | | count++; |
| | | } |
| | | return count == 0 ? null : total / count; |
| | | } |
| | | |
| | | private void applyRange(QueryWrapper<WrkMastLog> wrapper, String column, String rawValue) { |
| | | if (Cools.isEmpty(rawValue) || !rawValue.contains("~")) { |
| | | return; |
| | | } |
| | | String[] parts = rawValue.split("~"); |
| | | if (parts.length != 2) { |
| | | return; |
| | | } |
| | | wrapper.ge(column, DateUtils.convert(parts[0].trim())); |
| | | wrapper.le(column, DateUtils.convert(parts[1].trim())); |
| | | } |
| | | |
| | | private List<Integer> parseWrkNos(Object value) { |
| | | List<Integer> result = new ArrayList<>(); |
| | | if (value == null) { |
| | | return result; |
| | | } |
| | | if (value instanceof JSONArray) { |
| | | JSONArray array = (JSONArray) value; |
| | | for (int i = 0; i < array.size(); i++) { |
| | | Integer item = parseInteger(array.get(i)); |
| | | if (item != null) { |
| | | result.add(item); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | if (value instanceof Collection) { |
| | | for (Object item : (Collection<?>) value) { |
| | | Integer parsed = parseInteger(item); |
| | | if (parsed != null) { |
| | | result.add(parsed); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | String text = String.valueOf(value).trim(); |
| | | if (text.startsWith("[") && text.endsWith("]")) { |
| | | return parseWrkNos(JSONArray.parseArray(text)); |
| | | } |
| | | if (!Cools.isEmpty(text)) { |
| | | for (String part : text.split(",")) { |
| | | Integer parsed = parseInteger(part); |
| | | if (parsed != null) { |
| | | result.add(parsed); |
| | | } |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private Integer parseInteger(Object value) { |
| | | if (value == null || Cools.isEmpty(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Integer.valueOf(String.valueOf(value).trim()); |
| | | } catch (Exception ignore) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private Long parseLong(Object value) { |
| | | if (value == null || Cools.isEmpty(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Long.valueOf(String.valueOf(value).trim()); |
| | | } catch (Exception ignore) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private String stringValue(Object value) { |
| | | return value == null ? null : String.valueOf(value).trim(); |
| | | } |
| | | |
| | | private String upperTrim(String value) { |
| | | return value == null ? null : value.trim().toUpperCase(Locale.ROOT); |
| | | } |
| | | |
| | | private Date safeDate(Date date) { |
| | | return date == null ? new Date() : date; |
| | | } |
| | | |
| | | private long durationMs(Date startTime, Date endTime) { |
| | | if (startTime == null || endTime == null) { |
| | | return 0L; |
| | | } |
| | | return Math.max(0L, endTime.getTime() - startTime.getTime()); |
| | | } |
| | | |
| | | private Integer defaultInt(Integer value) { |
| | | return value == null ? 0 : value; |
| | | } |
| | | |
| | | private Long defaultLong(Long value) { |
| | | return value == null ? 0L : value; |
| | | } |
| | | |
| | | private static class FaultSummary { |
| | | private int hasFault; |
| | | private int totalCount; |
| | | private long totalDurationMs; |
| | | private int crnCount; |
| | | private long crnDurationMs; |
| | | private int dualCount; |
| | | private long dualDurationMs; |
| | | private int rgvCount; |
| | | private long rgvDurationMs; |
| | | } |
| | | |
| | | private static class BucketAccumulator { |
| | | private long taskCount; |
| | | private long totalDurationMs; |
| | | private long totalDurationCount; |
| | | private long stationDurationMs; |
| | | private long stationDurationCount; |
| | | private long craneDurationMs; |
| | | private long craneDurationCount; |
| | | } |
| | | |
| | | } |
| | |
| | | import com.zy.asrs.mapper.WrkMastLogMapper; |
| | | import com.zy.asrs.service.WrkMastLogService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | |
| | | @Service("wrkMastLogService") |
| | | public class WrkMastLogServiceImpl extends ServiceImpl<WrkMastLogMapper, WrkMastLog> implements WrkMastLogService { |
| | | |
| | | @Autowired |
| | | private WrkMastService wrkMastService; |
| | | private final WrkMastService wrkMastService; |
| | | |
| | | public WrkMastLogServiceImpl(WrkMastService wrkMastService) { |
| | | this.wrkMastService = wrkMastService; |
| | | } |
| | | |
| | | @Override |
| | | public boolean save(Integer wrkNo) { |
| New file |
| | |
| | | package com.zy.asrs.task; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.zy.asrs.entity.BasStation; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.service.BasStationService; |
| | | import com.zy.asrs.service.WrkAnalysisService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.core.News; |
| | | import com.zy.core.cache.SlaveConnection; |
| | | import com.zy.core.enums.SlaveType; |
| | | import com.zy.core.enums.WrkStsType; |
| | | import com.zy.core.model.protocol.StationProtocol; |
| | | import com.zy.core.thread.StationThread; |
| | | import com.zy.core.utils.StationOperateProcessUtils; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @Component |
| | | public class WrkAnalysisStationArrivalScanner { |
| | | |
| | | private final WrkMastService wrkMastService; |
| | | private final BasStationService basStationService; |
| | | private final WrkAnalysisService wrkAnalysisService; |
| | | private final StationOperateProcessUtils stationOperateProcessUtils; |
| | | |
| | | public WrkAnalysisStationArrivalScanner(WrkMastService wrkMastService, |
| | | BasStationService basStationService, |
| | | WrkAnalysisService wrkAnalysisService, |
| | | StationOperateProcessUtils stationOperateProcessUtils) { |
| | | this.wrkMastService = wrkMastService; |
| | | this.basStationService = basStationService; |
| | | this.wrkAnalysisService = wrkAnalysisService; |
| | | this.stationOperateProcessUtils = stationOperateProcessUtils; |
| | | } |
| | | |
| | | @Scheduled(fixedDelay = 1000L) |
| | | public void scanOutboundStationFlow() { |
| | | stationOperateProcessUtils.stationOutExecuteFinish(); |
| | | stationOperateProcessUtils.checkTaskToComplete(); |
| | | } |
| | | |
| | | @Scheduled(fixedDelay = 1000L) |
| | | public void scanInboundStationArrival() { |
| | | List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>() |
| | | .eq("io_type", 1) |
| | | .eq("wrk_sts", WrkStsType.INBOUND_STATION_RUN.sts) |
| | | .isNotNull("sta_no")); |
| | | for (WrkMast wrkMast : wrkMasts) { |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null || wrkMast.getStaNo() == null) { |
| | | continue; |
| | | } |
| | | BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>() |
| | | .eq("station_id", wrkMast.getStaNo()) |
| | | .last("limit 1")); |
| | | if (basStation == null || basStation.getDeviceNo() == null) { |
| | | continue; |
| | | } |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap(); |
| | | StationProtocol stationProtocol = statusMap == null ? null : statusMap.get(basStation.getStationId()); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | if (!wrkMast.getWrkNo().equals(stationProtocol.getTaskNo()) || !stationProtocol.isLoading()) { |
| | | continue; |
| | | } |
| | | boolean updated = wrkAnalysisService.completeInboundStationRun(wrkMast, new Date()); |
| | | if (updated) { |
| | | News.info("入库站点到达扫描命中,工作号={},目标站={}", wrkMast.getWrkNo(), wrkMast.getStaNo()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.zy.asrs.entity.LocMast; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.service.LocMastService; |
| | | import com.zy.asrs.service.WrkAnalysisService; |
| | | import com.zy.asrs.service.WrkMastLogService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.asrs.utils.NotifyUtils; |
| | |
| | | import com.zy.core.enums.WrkIoType; |
| | | import com.zy.core.enums.WrkStsType; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | @Slf4j |
| | | public class WrkMastScheduler { |
| | | |
| | | @Autowired |
| | | private WrkMastService wrkMastService; |
| | | @Autowired |
| | | private WrkMastLogService wrkMastLogService; |
| | | @Autowired |
| | | private LocMastService locMastService; |
| | | @Autowired |
| | | private NotifyUtils notifyUtils; |
| | | private final WrkMastService wrkMastService; |
| | | private final WrkMastLogService wrkMastLogService; |
| | | private final WrkAnalysisService wrkAnalysisService; |
| | | private final LocMastService locMastService; |
| | | private final NotifyUtils notifyUtils; |
| | | |
| | | public WrkMastScheduler(WrkMastService wrkMastService, |
| | | WrkMastLogService wrkMastLogService, |
| | | WrkAnalysisService wrkAnalysisService, |
| | | LocMastService locMastService, |
| | | NotifyUtils notifyUtils) { |
| | | this.wrkMastService = wrkMastService; |
| | | this.wrkMastLogService = wrkMastLogService; |
| | | this.wrkAnalysisService = wrkAnalysisService; |
| | | this.locMastService = locMastService; |
| | | this.notifyUtils = notifyUtils; |
| | | } |
| | | |
| | | @Scheduled(cron = "0/1 * * * * ? ") |
| | | @Transactional |
| | |
| | | // 保存工作主档历史档 |
| | | if (!wrkMastLogService.save(wrkMast.getWrkNo())) { |
| | | log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo()); |
| | | } else { |
| | | wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast)); |
| | | } |
| | | // 删除工作主档 |
| | | if (!wrkMastService.removeById(wrkMast.getWrkNo())) { |
| | |
| | | // 保存工作主档历史档 |
| | | if (!wrkMastLogService.save(wrkMast.getWrkNo())) { |
| | | log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo()); |
| | | } else { |
| | | wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast)); |
| | | } |
| | | // 删除工作主档 |
| | | if (!wrkMastService.removeById(wrkMast.getWrkNo())) { |
| | |
| | | // 保存工作主档历史档 |
| | | if (!wrkMastLogService.save(wrkMast.getWrkNo())) { |
| | | log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo()); |
| | | } else { |
| | | wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast)); |
| | | } |
| | | // 删除工作主档 |
| | | if (!wrkMastService.removeById(wrkMast.getWrkNo())) { |
| | |
| | | // 保存工作主档历史档 |
| | | if (!wrkMastLogService.save(wrkMast.getWrkNo())) { |
| | | log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo()); |
| | | } else { |
| | | wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast)); |
| | | } |
| | | // 删除工作主档 |
| | | if (!wrkMastService.removeById(wrkMast.getWrkNo())) { |
| | |
| | | } |
| | | } |
| | | |
| | | private Date resolveFinishTime(WrkMast wrkMast) { |
| | | if (wrkMast == null) { |
| | | return new Date(); |
| | | } |
| | | if (wrkMast.getModiTime() != null) { |
| | | return wrkMast.getModiTime(); |
| | | } |
| | | if (wrkMast.getIoTime() != null) { |
| | | return wrkMast.getIoTime(); |
| | | } |
| | | return new Date(); |
| | | } |
| | | |
| | | } |
| | |
| | | private BasOutStationAreaService basOutStationAreaService; |
| | | @Autowired |
| | | private WrkCommandRollbackService wrkCommandRollbackService; |
| | | @Autowired |
| | | private WrkAnalysisService wrkAnalysisService; |
| | | |
| | | /** |
| | | * 生成工作号 |
| | |
| | | News.error("移库任务 --- 保存工作档失败!"); |
| | | throw new CoolException("保存工作档失败"); |
| | | } |
| | | wrkAnalysisService.initForTask(wrkMast); |
| | | |
| | | sourceLocMast.setLocSts("R"); |
| | | sourceLocMast.setModiTime(new Date()); |
| | |
| | | News.error("入库任务 --- 保存工作档失败!"); |
| | | throw new CoolException("保存工作档失败"); |
| | | } |
| | | wrkAnalysisService.initForTask(wrkMast); |
| | | |
| | | locMast.setLocSts("S"); |
| | | locMast.setModiTime(new Date()); |
| | |
| | | News.error("出库任务 --- 保存工作档失败!"); |
| | | throw new CoolException("保存工作档失败"); |
| | | } |
| | | wrkAnalysisService.initForTask(wrkMast); |
| | | |
| | | locMast.setLocSts("R"); |
| | | locMast.setModiTime(new Date()); |
| | |
| | | public enum WrkStsType { |
| | | |
| | | NEW_INBOUND(1, "生成入库任务"), |
| | | INBOUND_DEVICE_RUN(2, "设备上走"), |
| | | INBOUND_RUN(3, "设备搬运中"), |
| | | INBOUND_RUN_COMPLETE(4, "设备搬运完成"), |
| | | INBOUND_STATION_RUN(2, "站点运行中"), |
| | | INBOUND_STATION_RUN_COMPLETE(3, "站点运行完成"), |
| | | INBOUND_RUN(4, "设备搬运中"), |
| | | INBOUND_RUN_COMPLETE(5, "设备搬运完成"), |
| | | INBOUND_MANUAL(6, "入库待人工回滚"), |
| | | COMPLETE_INBOUND(9, "入库完成"), |
| | | SETTLE_INBOUND(10, "入库库存更新"), |
| | |
| | | @Autowired |
| | | private WmsOperateUtils wmsOperateUtils; |
| | | @Autowired |
| | | private WrkAnalysisService wrkAnalysisService; |
| | | @Autowired |
| | | private DualCrnOperateProcessUtils dualCrnOperateProcessUtils; |
| | | @Autowired |
| | | private StoreInTaskGenerationService storeInTaskGenerationService; |
| | |
| | | stationOperateProcessUtils.stationInExecute(); |
| | | // 执行输送站点出库任务 |
| | | stationOperateProcessUtils.crnStationOutExecute(); |
| | | // 检测输送站点出库任务执行完成 |
| | | stationOperateProcessUtils.stationOutExecuteFinish(); |
| | | // 检测任务转完成 |
| | | stationOperateProcessUtils.checkTaskToComplete(); |
| | | // 检测出库排序 |
| | | stationOperateProcessUtils.checkStationOutOrder(); |
| | | // 监控绕圈站点 |
| | |
| | | JSON.toJSONString(command)); |
| | | } else { |
| | | if (wrkMast.getWrkSts() != WrkStsType.NEW_INBOUND.sts |
| | | && wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts) { |
| | | && wrkMast.getWrkSts() != WrkStsType.INBOUND_STATION_RUN.sts) { |
| | | Integer crnNo = wrkMast.getCrnNo(); |
| | | if (crnNo != null) { |
| | | CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo); |
| | |
| | | continue; |
| | | } |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(updateWrkSts); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneComplete(wrkMast, now, updateWrkSts); |
| | | CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo()); |
| | | MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand)); |
| | | News.info("堆垛机任务状态更新成功,堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo()); |
| | |
| | | stationOperateProcessUtils.stationInExecute(); |
| | | //执行输送站点出库任务 |
| | | stationOperateProcessUtils.crnStationOutExecute(); |
| | | //检测输送站点出库任务执行完成 |
| | | stationOperateProcessUtils.stationOutExecuteFinish(); |
| | | // 检测任务转完成 |
| | | stationOperateProcessUtils.checkTaskToComplete(); |
| | | // 检测出库排序 |
| | | stationOperateProcessUtils.checkStationOutOrder(); |
| | | // 监控绕圈站点 |
| | |
| | | stationOperateProcessUtils.stationInExecute(); |
| | | //执行输送站点出库任务 |
| | | stationOperateProcessUtils.crnStationOutExecute(); |
| | | //检测输送站点出库任务执行完成 |
| | | stationOperateProcessUtils.stationOutExecuteFinish(); |
| | | |
| | | //检测输送站点是否运行堵塞 |
| | | stationOperateProcessUtils.checkStationRunBlock(); |
| | |
| | | stationOperateProcessUtils.crnStationOutExecute(); |
| | | //执行双工位堆垛机输送站点出库任务 |
| | | stationOperateProcessUtils.dualCrnStationOutExecute(); |
| | | //检测输送站点出库任务执行完成 |
| | | stationOperateProcessUtils.stationOutExecuteFinish(); |
| | | // 检测任务转完成 |
| | | stationOperateProcessUtils.checkTaskToComplete(); |
| | | //检测输送站点是否运行堵塞 |
| | | stationOperateProcessUtils.checkStationRunBlock(); |
| | | //检测输送站点任务停留超时后重新计算路径 |
| | |
| | | |
| | | private Long getRollbackStatus(Long wrkSts) { |
| | | if (Long.valueOf(WrkStsType.INBOUND_RUN.sts).equals(wrkSts)) { |
| | | return WrkStsType.INBOUND_DEVICE_RUN.sts; |
| | | return WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts; |
| | | } |
| | | if (Long.valueOf(WrkStsType.OUTBOUND_RUN.sts).equals(wrkSts)) { |
| | | return WrkStsType.NEW_OUTBOUND.sts; |
| | |
| | | |
| | | private Long getRollbackStatusFromManual(Long wrkSts) { |
| | | if (Long.valueOf(WrkStsType.INBOUND_MANUAL.sts).equals(wrkSts)) { |
| | | return WrkStsType.INBOUND_DEVICE_RUN.sts; |
| | | return WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts; |
| | | } |
| | | if (Long.valueOf(WrkStsType.OUTBOUND_MANUAL.sts).equals(wrkSts)) { |
| | | return WrkStsType.NEW_OUTBOUND.sts; |
| | |
| | | } |
| | | |
| | | private boolean isStationTraceActiveWrkStatus(Long wrkSts) { |
| | | return Objects.equals(wrkSts, WrkStsType.INBOUND_DEVICE_RUN.sts) |
| | | return Objects.equals(wrkSts, WrkStsType.INBOUND_STATION_RUN.sts) |
| | | || Objects.equals(wrkSts, WrkStsType.STATION_RUN.sts); |
| | | } |
| | | |
| | |
| | | import com.zy.asrs.service.BasCrnpService; |
| | | import com.zy.asrs.service.BasStationService; |
| | | import com.zy.asrs.service.LocMastService; |
| | | import com.zy.asrs.service.WrkAnalysisService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.asrs.utils.NotifyUtils; |
| | | import com.zy.asrs.utils.Utils; |
| | |
| | | private NotifyUtils notifyUtils; |
| | | @Autowired |
| | | private StationOperateProcessUtils stationOperateProcessUtils; |
| | | @Autowired |
| | | private WrkAnalysisService wrkAnalysisService; |
| | | |
| | | private static final String CRN_OUT_REQUIRE_STATION_OUT_ENABLE_CONFIG = "crnOutRequireStationOutEnable"; |
| | | |
| | |
| | | List<WrkMast> taskQueue = wrkMastService.list(new QueryWrapper<WrkMast>() |
| | | .in("crn_no", new ArrayList<>(dispatchCrnMap.keySet())) |
| | | .in("wrk_sts", |
| | | WrkStsType.INBOUND_DEVICE_RUN.sts, |
| | | WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts, |
| | | WrkStsType.NEW_OUTBOUND.sts, |
| | | WrkStsType.NEW_LOC_MOVE.sts)); |
| | | taskQueue.sort(Comparator |
| | |
| | | continue; |
| | | } |
| | | |
| | | if (wrkMast.getWrkSts() != null && wrkMast.getWrkSts() == WrkStsType.INBOUND_DEVICE_RUN.sts) { |
| | | if (wrkMast.getWrkSts() != null && wrkMast.getWrkSts() == WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts) { |
| | | boolean result = this.crnExecuteInPlanner(basCrnp, crnThread, wrkMast); |
| | | if (result) { |
| | | crnProtocol.setLastIo("O"); |
| | |
| | | continue; |
| | | } |
| | | |
| | | if(wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts){ |
| | | if(wrkMast.getWrkSts() != WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts){ |
| | | continue; |
| | | } |
| | | |
| | |
| | | |
| | | CrnCommand command = crnThread.getPickAndPutCommand(sourceLocNo, wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.INBOUND_RUN.sts); |
| | | wrkMast.setCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command)); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_RUN, null); |
| | | News.info("堆垛机命令下发成功,堆垛机号={},任务数据={}", crnNo, JSON.toJSON(command)); |
| | |
| | | |
| | | CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), targetLocNo, wrkMast.getWrkNo(), crnNo); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN.sts); |
| | | wrkMast.setCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command)); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_OUT_TASK_RUN, null); |
| | | News.info("堆垛机命令下发成功,堆垛机号={},任务数据={}", crnNo, JSON.toJSON(command)); |
| | |
| | | continue; |
| | | } |
| | | |
| | | if (wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts) { |
| | | if (wrkMast.getWrkSts() != WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts) { |
| | | continue; |
| | | } |
| | | |
| | |
| | | |
| | | CrnCommand command = crnThread.getPickAndPutCommand(sourceLocNo, wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.INBOUND_RUN.sts); |
| | | wrkMast.setCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command)); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_RUN, null); |
| | | News.info("堆垛机命令下发成功,堆垛机号={},任务数据={}", crnNo, JSON.toJSON(command)); |
| | |
| | | |
| | | CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), targetLocNo, wrkMast.getWrkNo(), crnNo); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN.sts); |
| | | wrkMast.setCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command)); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_OUT_TASK_RUN, null); |
| | | News.info("堆垛机命令下发成功,堆垛机号={},任务数据={}", crnNo, JSON.toJSON(command)); |
| | |
| | | |
| | | CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.LOC_MOVE_RUN.sts); |
| | | wrkMast.setCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command)); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_RUN, null); |
| | | News.info("堆垛机命令下发成功,堆垛机号={},任务数据={}", crnNo, JSON.toJSON(command)); |
| | |
| | | } |
| | | |
| | | Long updateWrkSts = null; |
| | | Date now = new Date(); |
| | | if(wrkMast.getWrkSts() == WrkStsType.INBOUND_RUN.sts){ |
| | | updateWrkSts = WrkStsType.COMPLETE_INBOUND.sts; |
| | | notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_COMPLETE, null); |
| | |
| | | |
| | | wrkMast.setWrkSts(updateWrkSts); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneComplete(wrkMast, now, updateWrkSts); |
| | | CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo()); |
| | | MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand)); |
| | | News.info("堆垛机任务状态更新成功,堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo()); |
| | |
| | | |
| | | CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.LOC_MOVE_RUN.sts); |
| | | wrkMast.setCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command)); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_RUN, null); |
| | | News.info("堆垛机命令下发成功,堆垛机号={},任务数据={}", crnNo, JSON.toJSON(command)); |
| | |
| | | import com.zy.asrs.service.BasDualCrnpService; |
| | | import com.zy.asrs.service.BasStationService; |
| | | import com.zy.asrs.service.LocMastService; |
| | | import com.zy.asrs.service.WrkAnalysisService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.asrs.utils.NotifyUtils; |
| | | import com.zy.asrs.utils.Utils; |
| | |
| | | private NotifyUtils notifyUtils; |
| | | @Autowired |
| | | private StationOperateProcessUtils stationOperateProcessUtils; |
| | | @Autowired |
| | | private WrkAnalysisService wrkAnalysisService; |
| | | |
| | | private static final String CRN_OUT_REQUIRE_STATION_OUT_ENABLE_CONFIG = "crnOutRequireStationOutEnable"; |
| | | |
| | |
| | | .in("wrk_no", taskList) |
| | | ); |
| | | for (WrkMast wrkMast : wrkMasts) { |
| | | if(wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts){ |
| | | if(wrkMast.getWrkSts() != WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts){ |
| | | continue; |
| | | } |
| | | list.add(wrkMast); |
| | |
| | | |
| | | Integer crnNo = basDualCrnp.getCrnNo(); |
| | | |
| | | if (wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts) { |
| | | if (wrkMast.getWrkSts() != WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts) { |
| | | return null; |
| | | } |
| | | |
| | |
| | | commandList.add(pickCommand); |
| | | commandList.add(putCommand); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.INBOUND_RUN.sts); |
| | | wrkMast.setDualCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | SendDualCrnCommandParam sendDualCrnCommandParam = new SendDualCrnCommandParam(); |
| | | sendDualCrnCommandParam.setCrnNo(crnNo); |
| | | sendDualCrnCommandParam.setStation(station); |
| | |
| | | commandList.add(pickCommand); |
| | | commandList.add(putCommand); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN.sts); |
| | | wrkMast.setDualCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + wrkMast.getWrkNo(), JSON.toJSONString(stationObjModel, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24); |
| | | |
| | | SendDualCrnCommandParam sendDualCrnCommandParam = new SendDualCrnCommandParam(); |
| | |
| | | commandList.add(pickCommand); |
| | | commandList.add(putCommand); |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.LOC_MOVE_RUN.sts); |
| | | wrkMast.setDualCrnNo(crnNo); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneStart(wrkMast, now); |
| | | SendDualCrnCommandParam sendDualCrnCommandParam = new SendDualCrnCommandParam(); |
| | | sendDualCrnCommandParam.setCrnNo(crnNo); |
| | | sendDualCrnCommandParam.setStation(station); |
| | |
| | | |
| | | if (idx >= 2) { |
| | | Long updateWrkSts = null; |
| | | Date now = new Date(); |
| | | if (wrkMast.getWrkSts() == WrkStsType.INBOUND_RUN.sts) { |
| | | updateWrkSts = WrkStsType.COMPLETE_INBOUND.sts; |
| | | notifyUtils.notify(String.valueOf(SlaveType.DualCrn), basDualCrnp.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.DUAL_CRN_IN_TASK_COMPLETE, null); |
| | |
| | | |
| | | wrkMast.setWrkSts(updateWrkSts); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markCraneComplete(wrkMast, now, updateWrkSts); |
| | | News.info("双工位堆垛机任务状态更新成功,堆垛机号={},工作号={}", basDualCrnp.getCrnNo(), taskNo); |
| | | } |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_IO_EXECUTE_FINISH_LIMIT.key + basDualCrnp.getCrnNo() + "_" + taskNo, "lock", 10); |
| | |
| | | private BasStationOptService basStationOptService; |
| | | @Autowired |
| | | private StationTaskLoopService stationTaskLoopService; |
| | | @Autowired |
| | | private WrkAnalysisService wrkAnalysisService; |
| | | |
| | | //执行输送站点入库任务 |
| | | public synchronized void stationInExecute() { |
| | |
| | | continue; |
| | | } |
| | | |
| | | if (wrkMast.getWrkSts() == WrkStsType.INBOUND_DEVICE_RUN.sts) { |
| | | if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.NEW_INBOUND.sts)) { |
| | | continue; |
| | | } |
| | | |
| | |
| | | continue; |
| | | } |
| | | |
| | | wrkMast.setWrkSts(WrkStsType.INBOUND_DEVICE_RUN.sts); |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.INBOUND_STATION_RUN.sts); |
| | | wrkMast.setSourceStaNo(stationProtocol.getStationId()); |
| | | wrkMast.setStaNo(targetStationId); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markInboundStationStart(wrkMast, now); |
| | | MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command)); |
| | | News.info("输送站点入库命令下发成功,站点号={},工作号={},命令数据={}", stationId, wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | redisUtil.set(RedisKeyType.STATION_IN_EXECUTE_LIMIT.key + stationId, "lock", 5); |
| | |
| | | continue; |
| | | } |
| | | |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.STATION_RUN.sts); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markOutboundStationStart(wrkMast, now); |
| | | MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command)); |
| | | News.info("输送站点出库命令下发成功,站点号={},工作号={},命令数据={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5); |
| | |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null) { |
| | | return; |
| | | } |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.STATION_RUN_COMPLETE.sts); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | wrkMastService.updateById(wrkMast); |
| | | wrkAnalysisService.markOutboundStationComplete(wrkMast, now); |
| | | if (deviceNo != null) { |
| | | notifyUtils.notify(String.valueOf(SlaveType.Devp), deviceNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.STATION_OUT_TASK_RUN_COMPLETE, null); |
| | | } |
| | |
| | | if (Objects.equals(currentStationId, wrkMast.getStaNo())) { |
| | | return false; |
| | | } |
| | | return Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_DEVICE_RUN.sts) |
| | | return Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_STATION_RUN.sts) |
| | | || Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts); |
| | | } |
| | | |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.zy.asrs.mapper.WrkAnalysisMapper"> |
| | | |
| | | <resultMap id="BaseResultMap" type="com.zy.asrs.entity.WrkAnalysis"> |
| | | <id column="wrk_no" property="wrkNo" /> |
| | | <result column="wms_wrk_no" property="wmsWrkNo" /> |
| | | <result column="io_type" property="ioType" /> |
| | | <result column="final_wrk_sts" property="finalWrkSts" /> |
| | | <result column="source_sta_no" property="sourceStaNo" /> |
| | | <result column="sta_no" property="staNo" /> |
| | | <result column="source_loc_no" property="sourceLocNo" /> |
| | | <result column="loc_no" property="locNo" /> |
| | | <result column="crn_no" property="crnNo" /> |
| | | <result column="dual_crn_no" property="dualCrnNo" /> |
| | | <result column="rgv_no" property="rgvNo" /> |
| | | <result column="appe_time" property="appeTime" /> |
| | | <result column="finish_time" property="finishTime" /> |
| | | <result column="total_duration_ms" property="totalDurationMs" /> |
| | | <result column="station_start_time" property="stationStartTime" /> |
| | | <result column="station_end_time" property="stationEndTime" /> |
| | | <result column="station_duration_ms" property="stationDurationMs" /> |
| | | <result column="crane_start_time" property="craneStartTime" /> |
| | | <result column="crane_end_time" property="craneEndTime" /> |
| | | <result column="crane_duration_ms" property="craneDurationMs" /> |
| | | <result column="has_fault" property="hasFault" /> |
| | | <result column="fault_count" property="faultCount" /> |
| | | <result column="fault_duration_ms" property="faultDurationMs" /> |
| | | <result column="crn_fault_count" property="crnFaultCount" /> |
| | | <result column="crn_fault_duration_ms" property="crnFaultDurationMs" /> |
| | | <result column="dual_crn_fault_count" property="dualCrnFaultCount" /> |
| | | <result column="dual_crn_fault_duration_ms" property="dualCrnFaultDurationMs" /> |
| | | <result column="rgv_fault_count" property="rgvFaultCount" /> |
| | | <result column="rgv_fault_duration_ms" property="rgvFaultDurationMs" /> |
| | | <result column="metric_completeness" property="metricCompleteness" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| New file |
| | |
| | | CREATE TABLE IF NOT EXISTS `asr_wrk_analysis` ( |
| | | `wrk_no` int NOT NULL COMMENT '工作号', |
| | | `wms_wrk_no` varchar(64) DEFAULT NULL COMMENT 'WMS任务号', |
| | | `io_type` int DEFAULT NULL COMMENT '入出库类型', |
| | | `final_wrk_sts` bigint DEFAULT NULL COMMENT '最终工作状态', |
| | | `source_sta_no` int DEFAULT NULL COMMENT '源站', |
| | | `sta_no` int DEFAULT NULL COMMENT '目标站', |
| | | `source_loc_no` varchar(64) DEFAULT NULL COMMENT '源库位', |
| | | `loc_no` varchar(64) DEFAULT NULL COMMENT '目标库位', |
| | | `crn_no` int DEFAULT NULL COMMENT '堆垛机号', |
| | | `dual_crn_no` int DEFAULT NULL COMMENT '双工位堆垛机号', |
| | | `rgv_no` int DEFAULT NULL COMMENT 'RGV号', |
| | | `appe_time` datetime DEFAULT NULL COMMENT '创建时间', |
| | | `finish_time` datetime DEFAULT NULL COMMENT '完成时间', |
| | | `total_duration_ms` bigint DEFAULT NULL COMMENT '总耗时毫秒', |
| | | `station_start_time` datetime DEFAULT NULL COMMENT '站点开始时间', |
| | | `station_end_time` datetime DEFAULT NULL COMMENT '站点结束时间', |
| | | `station_duration_ms` bigint DEFAULT NULL COMMENT '站点耗时毫秒', |
| | | `crane_start_time` datetime DEFAULT NULL COMMENT '堆垛机开始时间', |
| | | `crane_end_time` datetime DEFAULT NULL COMMENT '堆垛机结束时间', |
| | | `crane_duration_ms` bigint DEFAULT NULL COMMENT '堆垛机耗时毫秒', |
| | | `has_fault` int DEFAULT '0' COMMENT '是否故障', |
| | | `fault_count` int DEFAULT '0' COMMENT '故障次数', |
| | | `fault_duration_ms` bigint DEFAULT '0' COMMENT '故障耗时毫秒', |
| | | `crn_fault_count` int DEFAULT '0' COMMENT '单堆垛机故障次数', |
| | | `crn_fault_duration_ms` bigint DEFAULT '0' COMMENT '单堆垛机故障耗时毫秒', |
| | | `dual_crn_fault_count` int DEFAULT '0' COMMENT '双工位堆垛机故障次数', |
| | | `dual_crn_fault_duration_ms` bigint DEFAULT '0' COMMENT '双工位堆垛机故障耗时毫秒', |
| | | `rgv_fault_count` int DEFAULT '0' COMMENT 'RGV故障次数', |
| | | `rgv_fault_duration_ms` bigint DEFAULT '0' COMMENT 'RGV故障耗时毫秒', |
| | | `metric_completeness` varchar(16) DEFAULT 'PARTIAL' COMMENT '数据完整性', |
| | | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
| | | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| | | PRIMARY KEY (`wrk_no`), |
| | | KEY `idx_wrk_analysis_finish_time` (`finish_time`), |
| | | KEY `idx_wrk_analysis_appe_time` (`appe_time`), |
| | | KEY `idx_wrk_analysis_io_type` (`io_type`), |
| | | KEY `idx_wrk_analysis_final_wrk_sts` (`final_wrk_sts`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务执行分析汇总表'; |
| | | |
| | | SET @wrk_analysis_inbound_status_migrated := CASE |
| | | WHEN EXISTS ( |
| | | SELECT 1 |
| | | FROM `asr_bas_wrk_status` |
| | | WHERE `wrk_sts` = 3 |
| | | AND `wrk_desc` = '站点运行完成' |
| | | ) THEN 1 |
| | | ELSE 0 |
| | | END; |
| | | |
| | | UPDATE `asr_wrk_mast` |
| | | SET `wrk_sts` = CASE |
| | | WHEN `wrk_sts` = 3 THEN 4 |
| | | WHEN `wrk_sts` = 4 THEN 5 |
| | | ELSE `wrk_sts` |
| | | END |
| | | WHERE @wrk_analysis_inbound_status_migrated = 0 |
| | | AND `wrk_sts` IN (3, 4); |
| | | |
| | | UPDATE `asr_wrk_mast_log` |
| | | SET `wrk_sts` = CASE |
| | | WHEN `wrk_sts` = 3 THEN 4 |
| | | WHEN `wrk_sts` = 4 THEN 5 |
| | | ELSE `wrk_sts` |
| | | END |
| | | WHERE @wrk_analysis_inbound_status_migrated = 0 |
| | | AND `wrk_sts` IN (3, 4); |
| | | |
| | | UPDATE `asr_bas_crnp_err_log` |
| | | SET `wrk_sts` = CASE |
| | | WHEN `wrk_sts` = 3 THEN 4 |
| | | WHEN `wrk_sts` = 4 THEN 5 |
| | | ELSE `wrk_sts` |
| | | END |
| | | WHERE @wrk_analysis_inbound_status_migrated = 0 |
| | | AND `wrk_sts` IN (3, 4); |
| | | |
| | | UPDATE `asr_bas_dual_crnp_err_log` |
| | | SET `wrk_sts` = CASE |
| | | WHEN `wrk_sts` = 3 THEN 4 |
| | | WHEN `wrk_sts` = 4 THEN 5 |
| | | ELSE `wrk_sts` |
| | | END |
| | | WHERE @wrk_analysis_inbound_status_migrated = 0 |
| | | AND `wrk_sts` IN (3, 4); |
| | | |
| | | UPDATE `asr_bas_rgv_err_log` |
| | | SET `wrk_sts` = CASE |
| | | WHEN `wrk_sts` = 3 THEN 4 |
| | | WHEN `wrk_sts` = 4 THEN 5 |
| | | ELSE `wrk_sts` |
| | | END |
| | | WHERE @wrk_analysis_inbound_status_migrated = 0 |
| | | AND `wrk_sts` IN (3, 4); |
| | | |
| | | INSERT INTO `asr_bas_wrk_status` (`wrk_sts`, `wrk_desc`, `appe_time`, `modi_time`) |
| | | VALUES (3, '站点运行完成', NOW(), NOW()) |
| | | ON DUPLICATE KEY UPDATE |
| | | `wrk_desc` = VALUES(`wrk_desc`), |
| | | `modi_time` = NOW(); |
| | | |
| | | INSERT INTO `asr_bas_wrk_status` (`wrk_sts`, `wrk_desc`, `appe_time`, `modi_time`) |
| | | VALUES (4, '设备搬运中', NOW(), NOW()) |
| | | ON DUPLICATE KEY UPDATE |
| | | `wrk_desc` = VALUES(`wrk_desc`), |
| | | `modi_time` = NOW(); |
| | | |
| | | INSERT INTO `asr_bas_wrk_status` (`wrk_sts`, `wrk_desc`, `appe_time`, `modi_time`) |
| | | VALUES (5, '设备搬运完成', NOW(), NOW()) |
| | | ON DUPLICATE KEY UPDATE |
| | | `wrk_desc` = VALUES(`wrk_desc`), |
| | | `modi_time` = NOW(); |
| | | |
| | | UPDATE `asr_bas_wrk_status` |
| | | SET `wrk_desc` = '站点运行中', |
| | | `modi_time` = NOW() |
| | | WHERE `wrk_sts` = 2; |
| | | |
| | | INSERT INTO `asr_wrk_analysis` ( |
| | | `wrk_no`, |
| | | `wms_wrk_no`, |
| | | `io_type`, |
| | | `final_wrk_sts`, |
| | | `source_sta_no`, |
| | | `sta_no`, |
| | | `source_loc_no`, |
| | | `loc_no`, |
| | | `crn_no`, |
| | | `dual_crn_no`, |
| | | `rgv_no`, |
| | | `appe_time`, |
| | | `finish_time`, |
| | | `total_duration_ms`, |
| | | `station_duration_ms`, |
| | | `crane_duration_ms`, |
| | | `has_fault`, |
| | | `fault_count`, |
| | | `fault_duration_ms`, |
| | | `crn_fault_count`, |
| | | `crn_fault_duration_ms`, |
| | | `dual_crn_fault_count`, |
| | | `dual_crn_fault_duration_ms`, |
| | | `rgv_fault_count`, |
| | | `rgv_fault_duration_ms`, |
| | | `metric_completeness`, |
| | | `create_time`, |
| | | `update_time` |
| | | ) |
| | | SELECT |
| | | l.`wrk_no`, |
| | | l.`wms_wrk_no`, |
| | | l.`io_type`, |
| | | l.`wrk_sts`, |
| | | l.`source_sta_no`, |
| | | l.`sta_no`, |
| | | l.`source_loc_no`, |
| | | l.`loc_no`, |
| | | l.`crn_no`, |
| | | l.`dual_crn_no`, |
| | | l.`rgv_no`, |
| | | l.`appe_time`, |
| | | COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`) AS `finish_time`, |
| | | CASE |
| | | WHEN l.`appe_time` IS NULL OR COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`) IS NULL THEN NULL |
| | | ELSE GREATEST(TIMESTAMPDIFF(MICROSECOND, l.`appe_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)), 0) DIV 1000 |
| | | END AS `total_duration_ms`, |
| | | CASE WHEN l.`io_type` = 201 THEN 0 ELSE NULL END AS `station_duration_ms`, |
| | | NULL AS `crane_duration_ms`, |
| | | CASE WHEN COALESCE(c.`fault_count`, 0) + COALESCE(d.`fault_count`, 0) + COALESCE(r.`fault_count`, 0) > 0 THEN 1 ELSE 0 END AS `has_fault`, |
| | | COALESCE(c.`fault_count`, 0) + COALESCE(d.`fault_count`, 0) + COALESCE(r.`fault_count`, 0) AS `fault_count`, |
| | | COALESCE(c.`fault_duration_ms`, 0) + COALESCE(d.`fault_duration_ms`, 0) + COALESCE(r.`fault_duration_ms`, 0) AS `fault_duration_ms`, |
| | | COALESCE(c.`fault_count`, 0) AS `crn_fault_count`, |
| | | COALESCE(c.`fault_duration_ms`, 0) AS `crn_fault_duration_ms`, |
| | | COALESCE(d.`fault_count`, 0) AS `dual_crn_fault_count`, |
| | | COALESCE(d.`fault_duration_ms`, 0) AS `dual_crn_fault_duration_ms`, |
| | | COALESCE(r.`fault_count`, 0) AS `rgv_fault_count`, |
| | | COALESCE(r.`fault_duration_ms`, 0) AS `rgv_fault_duration_ms`, |
| | | 'PARTIAL' AS `metric_completeness`, |
| | | NOW(), |
| | | NOW() |
| | | FROM `asr_wrk_mast_log` l |
| | | LEFT JOIN ( |
| | | SELECT |
| | | e.`wrk_no`, |
| | | COUNT(*) AS `fault_count`, |
| | | SUM( |
| | | CASE |
| | | WHEN e.`start_time` IS NULL THEN 0 |
| | | ELSE GREATEST( |
| | | TIMESTAMPDIFF( |
| | | MICROSECOND, |
| | | e.`start_time`, |
| | | COALESCE(e.`end_time`, wl.`modi_time`, wl.`io_time`, wl.`appe_time`, e.`start_time`) |
| | | ), |
| | | 0 |
| | | ) DIV 1000 |
| | | END |
| | | ) AS `fault_duration_ms` |
| | | FROM `asr_bas_crnp_err_log` e |
| | | LEFT JOIN `asr_wrk_mast_log` wl ON wl.`wrk_no` = e.`wrk_no` |
| | | GROUP BY e.`wrk_no` |
| | | ) c ON c.`wrk_no` = l.`wrk_no` |
| | | LEFT JOIN ( |
| | | SELECT |
| | | e.`wrk_no`, |
| | | COUNT(*) AS `fault_count`, |
| | | SUM( |
| | | CASE |
| | | WHEN e.`start_time` IS NULL THEN 0 |
| | | ELSE GREATEST( |
| | | TIMESTAMPDIFF( |
| | | MICROSECOND, |
| | | e.`start_time`, |
| | | COALESCE(e.`end_time`, wl.`modi_time`, wl.`io_time`, wl.`appe_time`, e.`start_time`) |
| | | ), |
| | | 0 |
| | | ) DIV 1000 |
| | | END |
| | | ) AS `fault_duration_ms` |
| | | FROM `asr_bas_dual_crnp_err_log` e |
| | | LEFT JOIN `asr_wrk_mast_log` wl ON wl.`wrk_no` = e.`wrk_no` |
| | | GROUP BY e.`wrk_no` |
| | | ) d ON d.`wrk_no` = l.`wrk_no` |
| | | LEFT JOIN ( |
| | | SELECT |
| | | e.`task_no` AS `wrk_no`, |
| | | COUNT(*) AS `fault_count`, |
| | | SUM( |
| | | CASE |
| | | WHEN e.`start_time` IS NULL THEN 0 |
| | | ELSE GREATEST( |
| | | TIMESTAMPDIFF( |
| | | MICROSECOND, |
| | | e.`start_time`, |
| | | COALESCE(e.`end_time`, wl.`modi_time`, wl.`io_time`, wl.`appe_time`, e.`start_time`) |
| | | ), |
| | | 0 |
| | | ) DIV 1000 |
| | | END |
| | | ) AS `fault_duration_ms` |
| | | FROM `asr_bas_rgv_err_log` e |
| | | LEFT JOIN `asr_wrk_mast_log` wl ON wl.`wrk_no` = e.`task_no` |
| | | GROUP BY e.`task_no` |
| | | ) r ON r.`wrk_no` = l.`wrk_no` |
| | | ON DUPLICATE KEY UPDATE |
| | | `wms_wrk_no` = VALUES(`wms_wrk_no`), |
| | | `io_type` = VALUES(`io_type`), |
| | | `final_wrk_sts` = VALUES(`final_wrk_sts`), |
| | | `source_sta_no` = VALUES(`source_sta_no`), |
| | | `sta_no` = VALUES(`sta_no`), |
| | | `source_loc_no` = VALUES(`source_loc_no`), |
| | | `loc_no` = VALUES(`loc_no`), |
| | | `crn_no` = VALUES(`crn_no`), |
| | | `dual_crn_no` = VALUES(`dual_crn_no`), |
| | | `rgv_no` = VALUES(`rgv_no`), |
| | | `appe_time` = VALUES(`appe_time`), |
| | | `finish_time` = VALUES(`finish_time`), |
| | | `total_duration_ms` = VALUES(`total_duration_ms`), |
| | | `station_duration_ms` = VALUES(`station_duration_ms`), |
| | | `crane_duration_ms` = VALUES(`crane_duration_ms`), |
| | | `has_fault` = VALUES(`has_fault`), |
| | | `fault_count` = VALUES(`fault_count`), |
| | | `fault_duration_ms` = VALUES(`fault_duration_ms`), |
| | | `crn_fault_count` = VALUES(`crn_fault_count`), |
| | | `crn_fault_duration_ms` = VALUES(`crn_fault_duration_ms`), |
| | | `dual_crn_fault_count` = VALUES(`dual_crn_fault_count`), |
| | | `dual_crn_fault_duration_ms` = VALUES(`dual_crn_fault_duration_ms`), |
| | | `rgv_fault_count` = VALUES(`rgv_fault_count`), |
| | | `rgv_fault_duration_ms` = VALUES(`rgv_fault_duration_ms`), |
| | | `metric_completeness` = VALUES(`metric_completeness`), |
| | | `update_time` = NOW(); |
| | | |
| | | SET @wrk_analysis_parent_id := COALESCE( |
| | | ( |
| | | SELECT `id` |
| | | FROM `sys_resource` |
| | | WHERE `code` = 'logReport' AND `level` = 1 |
| | | ORDER BY `id` |
| | | LIMIT 1 |
| | | ), |
| | | ( |
| | | SELECT `id` |
| | | FROM `sys_resource` |
| | | WHERE `code` = 'develop' AND `level` = 1 |
| | | ORDER BY `id` |
| | | LIMIT 1 |
| | | ) |
| | | ); |
| | | |
| | | INSERT INTO `sys_resource`(`code`, `name`, `resource_id`, `level`, `sort`, `status`) |
| | | SELECT 'wrkAnalysis/wrkAnalysis.html', '任务执行分析', @wrk_analysis_parent_id, 2, 996, 1 |
| | | FROM dual |
| | | WHERE @wrk_analysis_parent_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM `sys_resource` |
| | | WHERE `code` = 'wrkAnalysis/wrkAnalysis.html' AND `level` = 2 |
| | | ); |
| | | |
| | | UPDATE `sys_resource` |
| | | SET `name` = '任务执行分析', |
| | | `resource_id` = @wrk_analysis_parent_id, |
| | | `level` = 2, |
| | | `sort` = 996, |
| | | `status` = 1 |
| | | WHERE `code` = 'wrkAnalysis/wrkAnalysis.html' AND `level` = 2; |
| | | |
| | | SET @wrk_analysis_id := ( |
| | | SELECT `id` |
| | | FROM `sys_resource` |
| | | WHERE `code` = 'wrkAnalysis/wrkAnalysis.html' AND `level` = 2 |
| | | ORDER BY `id` |
| | | LIMIT 1 |
| | | ); |
| | | |
| | | INSERT INTO `sys_resource`(`code`, `name`, `resource_id`, `level`, `sort`, `status`) |
| | | SELECT 'wrkAnalysis/wrkAnalysis.html#view', '查看', @wrk_analysis_id, 3, 1, 1 |
| | | FROM dual |
| | | WHERE @wrk_analysis_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM `sys_resource` |
| | | WHERE `code` = 'wrkAnalysis/wrkAnalysis.html#view' AND `level` = 3 |
| | | ); |
| | | |
| | | UPDATE `sys_resource` |
| | | SET `name` = '查看', |
| | | `resource_id` = @wrk_analysis_id, |
| | | `level` = 3, |
| | | `sort` = 1, |
| | | `status` = 1 |
| | | WHERE `code` = 'wrkAnalysis/wrkAnalysis.html#view' AND `level` = 3; |
| New file |
| | |
| | | (function () { |
| | | "use strict"; |
| | | |
| | | function nowDate() { |
| | | return new Date(); |
| | | } |
| | | |
| | | function startOfToday() { |
| | | var date = new Date(); |
| | | date.setHours(0, 0, 0, 0); |
| | | return date; |
| | | } |
| | | |
| | | function createDefaultFilters() { |
| | | return { |
| | | mode: "TASK", |
| | | keyword: "", |
| | | ioType: "", |
| | | finalWrkSts: "", |
| | | sourceStaNo: "", |
| | | staNo: "", |
| | | deviceType: "", |
| | | timeField: "finish_time", |
| | | timeRange: [startOfToday(), nowDate()] |
| | | }; |
| | | } |
| | | |
| | | function createEmptyAnalysis() { |
| | | return { |
| | | summary: { |
| | | taskCount: 0, |
| | | avgTotalDurationMs: null, |
| | | avgStationDurationMs: null, |
| | | avgCraneDurationMs: null, |
| | | faultTaskCount: 0, |
| | | faultDurationMs: 0, |
| | | partialTaskCount: 0 |
| | | }, |
| | | durationCompare: [], |
| | | trend: [], |
| | | faultPie: [], |
| | | faultDuration: [], |
| | | detail: [] |
| | | }; |
| | | } |
| | | |
| | | new Vue({ |
| | | el: "#app", |
| | | data: function () { |
| | | return { |
| | | options: { |
| | | ioTypes: [], |
| | | statuses: [], |
| | | stations: [], |
| | | deviceTypes: [], |
| | | timeFields: [] |
| | | }, |
| | | filters: createDefaultFilters(), |
| | | tableData: [], |
| | | currentPage: 1, |
| | | pageSize: 20, |
| | | pageTotal: 0, |
| | | listLoading: false, |
| | | analyzeLoading: false, |
| | | selectedWrkNoMap: {}, |
| | | analysis: createEmptyAnalysis(), |
| | | analysisReady: false, |
| | | charts: { |
| | | duration: null, |
| | | trend: null, |
| | | faultPie: null, |
| | | faultDuration: null |
| | | }, |
| | | resizeHandler: null |
| | | }; |
| | | }, |
| | | computed: { |
| | | selectedWrkNos: function () { |
| | | return Object.keys(this.selectedWrkNoMap).map(function (key) { |
| | | return Number(key); |
| | | }).filter(function (value) { |
| | | return !!value; |
| | | }); |
| | | } |
| | | }, |
| | | mounted: function () { |
| | | var self = this; |
| | | this.loadOptions(); |
| | | this.loadList(); |
| | | this.resizeHandler = function () { |
| | | self.resizeCharts(); |
| | | }; |
| | | window.addEventListener("resize", this.resizeHandler); |
| | | }, |
| | | beforeDestroy: function () { |
| | | if (this.resizeHandler) { |
| | | window.removeEventListener("resize", this.resizeHandler); |
| | | } |
| | | this.disposeCharts(); |
| | | }, |
| | | methods: { |
| | | loadOptions: function () { |
| | | var self = this; |
| | | $.ajax({ |
| | | url: baseUrl + "/wrkAnalysis/options/auth", |
| | | headers: { token: localStorage.getItem("token") }, |
| | | method: "GET", |
| | | success: function (res) { |
| | | if (res && res.code === 200) { |
| | | self.options = Object.assign(self.options, res.data || {}); |
| | | return; |
| | | } |
| | | self.$message.error((res && res.msg) || "分析选项加载失败"); |
| | | }, |
| | | error: function () { |
| | | self.$message.error("分析选项加载失败"); |
| | | } |
| | | }); |
| | | }, |
| | | buildListParams: function () { |
| | | var params = { |
| | | curr: this.currentPage, |
| | | limit: this.pageSize, |
| | | keyword: this.filters.keyword, |
| | | ioType: this.filters.ioType, |
| | | finalWrkSts: this.filters.finalWrkSts, |
| | | sourceStaNo: this.filters.sourceStaNo, |
| | | staNo: this.filters.staNo, |
| | | deviceType: this.filters.deviceType |
| | | }; |
| | | if (this.filters.timeRange && this.filters.timeRange.length === 2) { |
| | | if (this.filters.timeField === "appe_time") { |
| | | params.appeTimeRange = this.formatRange(this.filters.timeRange); |
| | | } else { |
| | | params.finishTimeRange = this.formatRange(this.filters.timeRange); |
| | | } |
| | | } |
| | | return this.cleanParams(params); |
| | | }, |
| | | loadList: function () { |
| | | var self = this; |
| | | this.listLoading = true; |
| | | $.ajax({ |
| | | url: baseUrl + "/wrkAnalysis/list/auth", |
| | | headers: { token: localStorage.getItem("token") }, |
| | | method: "GET", |
| | | data: self.buildListParams(), |
| | | success: function (res) { |
| | | if (res && res.code === 200) { |
| | | var data = res.data || {}; |
| | | self.tableData = data.records || []; |
| | | self.pageTotal = data.total || 0; |
| | | self.$nextTick(function () { |
| | | self.restoreSelection(); |
| | | }); |
| | | return; |
| | | } |
| | | self.$message.error((res && res.msg) || "历史任务加载失败"); |
| | | }, |
| | | error: function () { |
| | | self.$message.error("历史任务加载失败"); |
| | | }, |
| | | complete: function () { |
| | | self.listLoading = false; |
| | | } |
| | | }); |
| | | }, |
| | | handleSearch: function () { |
| | | this.currentPage = 1; |
| | | this.loadList(); |
| | | }, |
| | | handleReset: function () { |
| | | this.filters = createDefaultFilters(); |
| | | this.currentPage = 1; |
| | | this.pageSize = 20; |
| | | this.selectedWrkNoMap = {}; |
| | | this.analysis = createEmptyAnalysis(); |
| | | this.analysisReady = false; |
| | | this.disposeCharts(); |
| | | this.loadList(); |
| | | }, |
| | | handleSizeChange: function (size) { |
| | | this.pageSize = size; |
| | | this.currentPage = 1; |
| | | this.loadList(); |
| | | }, |
| | | handleCurrentChange: function (page) { |
| | | this.currentPage = page; |
| | | this.loadList(); |
| | | }, |
| | | restoreSelection: function () { |
| | | var table = this.$refs.historyTable; |
| | | var self = this; |
| | | if (!table) { |
| | | return; |
| | | } |
| | | table.clearSelection(); |
| | | (this.tableData || []).forEach(function (row) { |
| | | if (self.selectedWrkNoMap[row.wrkNo]) { |
| | | table.toggleRowSelection(row, true); |
| | | } |
| | | }); |
| | | }, |
| | | syncCurrentPageSelection: function (selection) { |
| | | var selectedMap = {}; |
| | | (selection || []).forEach(function (row) { |
| | | selectedMap[row.wrkNo] = true; |
| | | }); |
| | | (this.tableData || []).forEach(function (row) { |
| | | delete this.selectedWrkNoMap[row.wrkNo]; |
| | | }, this); |
| | | Object.keys(selectedMap).forEach(function (key) { |
| | | this.selectedWrkNoMap[key] = true; |
| | | }, this); |
| | | }, |
| | | runAnalysis: function () { |
| | | var self = this; |
| | | var request = { |
| | | mode: this.filters.mode, |
| | | ioType: this.filters.ioType, |
| | | finalWrkSts: this.filters.finalWrkSts, |
| | | sourceStaNo: this.filters.sourceStaNo, |
| | | staNo: this.filters.staNo, |
| | | deviceType: this.filters.deviceType |
| | | }; |
| | | if (this.filters.mode === "TASK") { |
| | | if (!this.selectedWrkNos.length) { |
| | | this.$message.warning("请先勾选要分析的任务"); |
| | | return; |
| | | } |
| | | request.wrkNos = this.selectedWrkNos; |
| | | request.timeField = this.filters.timeField; |
| | | } else { |
| | | if (!this.filters.timeRange || this.filters.timeRange.length !== 2) { |
| | | this.$message.warning("请先选择分析时间范围"); |
| | | return; |
| | | } |
| | | request.timeField = this.filters.timeField; |
| | | request.startTime = this.filters.timeRange[0].getTime(); |
| | | request.endTime = this.filters.timeRange[1].getTime(); |
| | | } |
| | | this.analyzeLoading = true; |
| | | $.ajax({ |
| | | url: baseUrl + "/wrkAnalysis/analyze/auth", |
| | | headers: { |
| | | token: localStorage.getItem("token"), |
| | | "Content-Type": "application/json" |
| | | }, |
| | | method: "POST", |
| | | data: JSON.stringify(this.cleanParams(request)), |
| | | success: function (res) { |
| | | if (res && res.code === 200) { |
| | | self.analysis = Object.assign(createEmptyAnalysis(), res.data || {}); |
| | | self.analysisReady = true; |
| | | self.$nextTick(function () { |
| | | self.updateCharts(); |
| | | }); |
| | | return; |
| | | } |
| | | self.$message.error((res && res.msg) || "分析失败"); |
| | | }, |
| | | error: function () { |
| | | self.$message.error("分析失败"); |
| | | }, |
| | | complete: function () { |
| | | self.analyzeLoading = false; |
| | | } |
| | | }); |
| | | }, |
| | | updateCharts: function () { |
| | | if (!this.analysisReady) { |
| | | this.disposeCharts(); |
| | | return; |
| | | } |
| | | this.ensureCharts(); |
| | | this.renderDurationChart(); |
| | | this.renderTrendChart(); |
| | | this.renderFaultPieChart(); |
| | | this.renderFaultDurationChart(); |
| | | }, |
| | | ensureCharts: function () { |
| | | if (this.$refs.durationChart && !this.charts.duration) { |
| | | this.charts.duration = echarts.init(this.$refs.durationChart); |
| | | } |
| | | if (this.$refs.trendChart && !this.charts.trend) { |
| | | this.charts.trend = echarts.init(this.$refs.trendChart); |
| | | } |
| | | if (this.$refs.faultPieChart && !this.charts.faultPie) { |
| | | this.charts.faultPie = echarts.init(this.$refs.faultPieChart); |
| | | } |
| | | if (this.$refs.faultDurationChart && !this.charts.faultDuration) { |
| | | this.charts.faultDuration = echarts.init(this.$refs.faultDurationChart); |
| | | } |
| | | }, |
| | | renderDurationChart: function () { |
| | | if (!this.charts.duration) { |
| | | return; |
| | | } |
| | | var rows = this.analysis.durationCompare || []; |
| | | this.charts.duration.setOption({ |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["站点耗时", "堆垛机耗时", "总耗时"] }, |
| | | grid: { left: 50, right: 20, top: 40, bottom: 70 }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: rows.map(function (item) { return String(item.wrkNo); }), |
| | | axisLabel: { rotate: rows.length > 8 ? 30 : 0 } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { |
| | | formatter: function (value) { |
| | | return Math.round((value || 0) / 1000) + "s"; |
| | | } |
| | | } |
| | | }, |
| | | series: [ |
| | | { name: "站点耗时", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return item.stationDurationMs || 0; }) }, |
| | | { name: "堆垛机耗时", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return item.craneDurationMs || 0; }) }, |
| | | { name: "总耗时", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return item.totalDurationMs || 0; }) } |
| | | ] |
| | | }, true); |
| | | }, |
| | | renderTrendChart: function () { |
| | | if (!this.charts.trend) { |
| | | return; |
| | | } |
| | | var rows = this.analysis.trend || []; |
| | | this.charts.trend.setOption({ |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["平均总耗时", "平均站点耗时", "平均堆垛机耗时"] }, |
| | | grid: { left: 50, right: 20, top: 40, bottom: 70 }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: rows.map(function (item) { return item.bucketLabel; }), |
| | | axisLabel: { rotate: rows.length > 8 ? 25 : 0 } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { |
| | | formatter: function (value) { |
| | | return Math.round((value || 0) / 1000) + "s"; |
| | | } |
| | | } |
| | | }, |
| | | series: [ |
| | | { name: "平均总耗时", type: "line", smooth: true, data: rows.map(function (item) { return item.avgTotalDurationMs || 0; }) }, |
| | | { name: "平均站点耗时", type: "line", smooth: true, data: rows.map(function (item) { return item.avgStationDurationMs || 0; }) }, |
| | | { name: "平均堆垛机耗时", type: "line", smooth: true, data: rows.map(function (item) { return item.avgCraneDurationMs || 0; }) } |
| | | ] |
| | | }, true); |
| | | }, |
| | | renderFaultPieChart: function () { |
| | | if (!this.charts.faultPie) { |
| | | return; |
| | | } |
| | | this.charts.faultPie.setOption({ |
| | | tooltip: { trigger: "item" }, |
| | | legend: { bottom: 0 }, |
| | | series: [{ |
| | | type: "pie", |
| | | radius: ["42%", "68%"], |
| | | center: ["50%", "46%"], |
| | | label: { formatter: "{b}\n{d}%" }, |
| | | data: this.analysis.faultPie || [] |
| | | }] |
| | | }, true); |
| | | }, |
| | | renderFaultDurationChart: function () { |
| | | if (!this.charts.faultDuration) { |
| | | return; |
| | | } |
| | | var rows = this.analysis.faultDuration || []; |
| | | this.charts.faultDuration.setOption({ |
| | | tooltip: { trigger: "axis" }, |
| | | grid: { left: 50, right: 20, top: 20, bottom: 40 }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: rows.map(function (item) { return item.name; }) |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { |
| | | formatter: function (value) { |
| | | return Math.round((value || 0) / 1000) + "s"; |
| | | } |
| | | } |
| | | }, |
| | | series: [{ |
| | | name: "故障耗时", |
| | | type: "bar", |
| | | barMaxWidth: 36, |
| | | data: rows.map(function (item) { return item.value || 0; }) |
| | | }] |
| | | }, true); |
| | | }, |
| | | resizeCharts: function () { |
| | | Object.keys(this.charts).forEach(function (key) { |
| | | if (this.charts[key]) { |
| | | this.charts[key].resize(); |
| | | } |
| | | }, this); |
| | | }, |
| | | disposeCharts: function () { |
| | | Object.keys(this.charts).forEach(function (key) { |
| | | if (this.charts[key]) { |
| | | this.charts[key].dispose(); |
| | | this.charts[key] = null; |
| | | } |
| | | }, this); |
| | | }, |
| | | formatRange: function (range) { |
| | | return this.formatDateTime(range[0]) + " ~ " + this.formatDateTime(range[1]); |
| | | }, |
| | | formatDateTime: function (date) { |
| | | if (!date) { |
| | | return ""; |
| | | } |
| | | var year = date.getFullYear(); |
| | | var month = this.pad(date.getMonth() + 1); |
| | | var day = this.pad(date.getDate()); |
| | | var hour = this.pad(date.getHours()); |
| | | var minute = this.pad(date.getMinutes()); |
| | | var second = this.pad(date.getSeconds()); |
| | | return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second; |
| | | }, |
| | | pad: function (value) { |
| | | return value < 10 ? "0" + value : String(value); |
| | | }, |
| | | cleanParams: function (params) { |
| | | var result = {}; |
| | | Object.keys(params || {}).forEach(function (key) { |
| | | var value = params[key]; |
| | | if (value === "" || value === null || value === undefined) { |
| | | return; |
| | | } |
| | | if (Array.isArray(value) && !value.length) { |
| | | return; |
| | | } |
| | | result[key] = value; |
| | | }); |
| | | return result; |
| | | }, |
| | | formatNumber: function (value) { |
| | | var num = Number(value || 0); |
| | | if (!isFinite(num)) { |
| | | return "0"; |
| | | } |
| | | return num.toLocaleString("zh-CN"); |
| | | }, |
| | | formatDuration: function (value) { |
| | | if (value === null || value === undefined || value === "") { |
| | | return "--"; |
| | | } |
| | | var ms = Number(value); |
| | | if (!isFinite(ms)) { |
| | | return "--"; |
| | | } |
| | | if (ms < 1000) { |
| | | return Math.round(ms) + " ms"; |
| | | } |
| | | var totalSeconds = Math.floor(ms / 1000); |
| | | var hours = Math.floor(totalSeconds / 3600); |
| | | var minutes = Math.floor((totalSeconds % 3600) / 60); |
| | | var seconds = totalSeconds % 60; |
| | | if (hours > 0) { |
| | | return hours + "h " + this.pad(minutes) + "m " + this.pad(seconds) + "s"; |
| | | } |
| | | if (minutes > 0) { |
| | | return minutes + "m " + this.pad(seconds) + "s"; |
| | | } |
| | | return seconds + "s"; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | })(); |
| New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="UTF-8"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | | <title>任务执行分析</title> |
| | | <link rel="stylesheet" href="../../static/vue/element/element.css"> |
| | | <style> |
| | | [v-cloak] { display: none; } |
| | | * { box-sizing: border-box; } |
| | | html, body { |
| | | margin: 0; |
| | | min-height: 100%; |
| | | font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif; |
| | | color: #22364a; |
| | | background: |
| | | radial-gradient(960px 400px at 0% -10%, rgba(33, 91, 168, 0.10), transparent 60%), |
| | | radial-gradient(880px 360px at 100% 0%, rgba(16, 142, 120, 0.10), transparent 60%), |
| | | linear-gradient(180deg, #f3f7fb 0%, #edf3f8 100%); |
| | | } |
| | | .page-shell { |
| | | max-width: 1720px; |
| | | margin: 0 auto; |
| | | padding: 16px; |
| | | } |
| | | .page-head { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | gap: 12px; |
| | | margin-bottom: 14px; |
| | | flex-wrap: wrap; |
| | | } |
| | | .page-title { |
| | | font-size: 30px; |
| | | font-weight: 700; |
| | | color: #1e3245; |
| | | } |
| | | .page-subtitle { |
| | | margin-top: 6px; |
| | | font-size: 13px; |
| | | color: #7b8ca0; |
| | | } |
| | | .panel { |
| | | margin-top: 16px; |
| | | border-radius: 24px; |
| | | border: 1px solid rgba(216, 226, 235, 0.96); |
| | | background: rgba(255, 255, 255, 0.92); |
| | | box-shadow: 0 16px 36px rgba(38, 60, 88, 0.08); |
| | | overflow: hidden; |
| | | } |
| | | .panel-head { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: 12px; |
| | | padding: 18px 20px 0; |
| | | flex-wrap: wrap; |
| | | } |
| | | .panel-title { |
| | | font-size: 18px; |
| | | font-weight: 700; |
| | | color: #22364a; |
| | | } |
| | | .panel-body { |
| | | padding: 16px 20px 20px; |
| | | } |
| | | .filter-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(6, minmax(0, 1fr)); |
| | | gap: 12px; |
| | | } |
| | | .filter-item.span-2 { |
| | | grid-column: span 2; |
| | | } |
| | | .toolbar-row { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: 12px; |
| | | margin-bottom: 14px; |
| | | flex-wrap: wrap; |
| | | } |
| | | .toolbar-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | flex-wrap: wrap; |
| | | } |
| | | .selection-meta { |
| | | font-size: 13px; |
| | | color: #698096; |
| | | } |
| | | .summary-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(6, minmax(0, 1fr)); |
| | | gap: 12px; |
| | | } |
| | | .summary-card { |
| | | min-height: 92px; |
| | | padding: 14px 16px; |
| | | border-radius: 18px; |
| | | border: 1px solid #dfe8f2; |
| | | background: linear-gradient(180deg, #fcfdff 0%, #f5f9fd 100%); |
| | | } |
| | | .summary-label { |
| | | font-size: 12px; |
| | | color: #7d8ea2; |
| | | } |
| | | .summary-value { |
| | | margin-top: 8px; |
| | | font-size: 28px; |
| | | line-height: 1.1; |
| | | font-weight: 700; |
| | | color: #22364a; |
| | | } |
| | | .summary-sub { |
| | | margin-top: 8px; |
| | | font-size: 12px; |
| | | color: #8a99ab; |
| | | } |
| | | .quality-banner { |
| | | margin-bottom: 14px; |
| | | padding: 10px 14px; |
| | | border-radius: 14px; |
| | | border: 1px solid rgba(230, 187, 88, 0.35); |
| | | background: rgba(255, 246, 226, 0.92); |
| | | color: #8b5d10; |
| | | font-size: 13px; |
| | | } |
| | | .chart-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | gap: 16px; |
| | | } |
| | | .chart-card { |
| | | padding: 16px; |
| | | border-radius: 20px; |
| | | border: 1px solid #dfe7f0; |
| | | background: #fbfdff; |
| | | } |
| | | .chart-title { |
| | | margin-bottom: 12px; |
| | | font-size: 16px; |
| | | font-weight: 700; |
| | | color: #22364a; |
| | | } |
| | | .chart-box { |
| | | width: 100%; |
| | | height: 320px; |
| | | } |
| | | .empty-shell { |
| | | padding: 52px 16px; |
| | | border-radius: 18px; |
| | | border: 1px dashed #d5e0ec; |
| | | background: #fafcff; |
| | | text-align: center; |
| | | color: #8a99ab; |
| | | font-size: 14px; |
| | | } |
| | | .tag-complete { |
| | | color: #187c5a; |
| | | background: rgba(24, 162, 105, 0.16); |
| | | border-color: rgba(24, 162, 105, 0.18); |
| | | } |
| | | .tag-partial { |
| | | color: #b56c05; |
| | | background: rgba(245, 163, 74, 0.16); |
| | | border-color: rgba(245, 163, 74, 0.18); |
| | | } |
| | | @media (max-width: 1460px) { |
| | | .filter-grid, |
| | | .summary-grid { |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | } |
| | | .chart-grid { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | } |
| | | @media (max-width: 860px) { |
| | | .page-shell { padding: 12px; } |
| | | .filter-grid, |
| | | .summary-grid { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | .filter-item.span-2 { |
| | | grid-column: span 2; |
| | | } |
| | | .page-title { |
| | | font-size: 24px; |
| | | } |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <div id="app" v-cloak class="page-shell"> |
| | | <div class="page-head"> |
| | | <div> |
| | | <div class="page-title">任务执行分析</div> |
| | | <div class="page-subtitle">支持按任务批量分析和按时间范围分析,统一查看站点耗时、堆垛机耗时、总耗时和故障耗时。</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <section class="panel"> |
| | | <div class="panel-head"> |
| | | <div class="panel-title">筛选条件</div> |
| | | </div> |
| | | <div class="panel-body"> |
| | | <div class="toolbar-row"> |
| | | <el-radio-group v-model="filters.mode" size="small"> |
| | | <el-radio-button label="TASK">按任务批量分析</el-radio-button> |
| | | <el-radio-button label="TIME">按时间范围分析</el-radio-button> |
| | | </el-radio-group> |
| | | <div class="toolbar-actions"> |
| | | <el-button size="small" @click="handleReset">重置</el-button> |
| | | <el-button size="small" type="primary" @click="handleSearch">查询历史任务</el-button> |
| | | <el-button size="small" type="success" :loading="analyzeLoading" @click="runAnalysis">开始分析</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="filter-grid"> |
| | | <div class="filter-item span-2"> |
| | | <el-input v-model.trim="filters.keyword" clearable placeholder="关键字:工作号 / WMS任务号 / 库位 / 条码"></el-input> |
| | | </div> |
| | | <div class="filter-item"> |
| | | <el-select v-model="filters.ioType" clearable placeholder="任务类型" style="width: 100%;"> |
| | | <el-option label="全部" value=""></el-option> |
| | | <el-option v-for="item in options.ioTypes" :key="item.code" :label="item.label" :value="item.value"></el-option> |
| | | </el-select> |
| | | </div> |
| | | <div class="filter-item"> |
| | | <el-select v-model="filters.finalWrkSts" clearable placeholder="最终状态" style="width: 100%;"> |
| | | <el-option label="默认完成态" value=""></el-option> |
| | | <el-option v-for="item in options.statuses" :key="item.code" :label="item.label" :value="item.value"></el-option> |
| | | </el-select> |
| | | </div> |
| | | <div class="filter-item"> |
| | | <el-select v-model="filters.sourceStaNo" clearable placeholder="源站" style="width: 100%;"> |
| | | <el-option label="全部源站" value=""></el-option> |
| | | <el-option v-for="item in options.stations" :key="'source-' + item.code" :label="item.label" :value="item.value"></el-option> |
| | | </el-select> |
| | | </div> |
| | | <div class="filter-item"> |
| | | <el-select v-model="filters.staNo" clearable placeholder="目标站" style="width: 100%;"> |
| | | <el-option label="全部目标站" value=""></el-option> |
| | | <el-option v-for="item in options.stations" :key="'target-' + item.code" :label="item.label" :value="item.value"></el-option> |
| | | </el-select> |
| | | </div> |
| | | <div class="filter-item"> |
| | | <el-select v-model="filters.deviceType" clearable placeholder="设备类型" style="width: 100%;"> |
| | | <el-option label="全部设备" value=""></el-option> |
| | | <el-option v-for="item in options.deviceTypes" :key="item.code" :label="item.label" :value="item.value"></el-option> |
| | | </el-select> |
| | | </div> |
| | | <div class="filter-item"> |
| | | <el-select v-model="filters.timeField" placeholder="时间字段" style="width: 100%;"> |
| | | <el-option v-for="item in options.timeFields" :key="item.code" :label="item.label" :value="item.value"></el-option> |
| | | </el-select> |
| | | </div> |
| | | <div class="filter-item span-2"> |
| | | <el-date-picker |
| | | v-model="filters.timeRange" |
| | | type="datetimerange" |
| | | unlink-panels |
| | | range-separator="至" |
| | | start-placeholder="开始时间" |
| | | end-placeholder="结束时间" |
| | | style="width: 100%;"> |
| | | </el-date-picker> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | |
| | | <section class="panel"> |
| | | <div class="panel-head"> |
| | | <div class="panel-title">历史任务列表</div> |
| | | <div class="selection-meta">已选任务 {{ selectedWrkNos.length }} 条</div> |
| | | </div> |
| | | <div class="panel-body"> |
| | | <el-table |
| | | ref="historyTable" |
| | | :data="tableData" |
| | | border |
| | | stripe |
| | | size="mini" |
| | | row-key="wrkNo" |
| | | @selection-change="syncCurrentPageSelection" |
| | | v-loading="listLoading" |
| | | style="width: 100%;"> |
| | | <el-table-column type="selection" width="48" reserve-selection></el-table-column> |
| | | <el-table-column prop="wrkNo" label="工作号" width="100" align="center"></el-table-column> |
| | | <el-table-column prop="wmsWrkNo" label="WMS任务号" min-width="150"></el-table-column> |
| | | <el-table-column prop="ioType$" label="任务类型" width="90" align="center"></el-table-column> |
| | | <el-table-column prop="wrkSts$" label="最终状态" min-width="120"></el-table-column> |
| | | <el-table-column prop="sourceStaNo" label="源站" width="90" align="center"></el-table-column> |
| | | <el-table-column prop="staNo" label="目标站" width="90" align="center"></el-table-column> |
| | | <el-table-column prop="appeTime$" label="创建时间" width="165"></el-table-column> |
| | | <el-table-column prop="finishTime$" label="完成时间" width="165"></el-table-column> |
| | | <el-table-column label="总耗时" width="130" align="right"> |
| | | <template slot-scope="scope">{{ formatDuration(scope.row.totalDurationMs) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="站点耗时" width="130" align="right"> |
| | | <template slot-scope="scope">{{ formatDuration(scope.row.stationDurationMs) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="堆垛机耗时" width="130" align="right"> |
| | | <template slot-scope="scope">{{ formatDuration(scope.row.craneDurationMs) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="faultCount" label="故障次数" width="90" align="center"></el-table-column> |
| | | <el-table-column label="故障耗时" width="130" align="right"> |
| | | <template slot-scope="scope">{{ formatDuration(scope.row.faultDurationMs) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="数据完整性" width="110" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-tag size="mini" :class="scope.row.metricCompleteness === 'COMPLETE' ? 'tag-complete' : 'tag-partial'"> |
| | | {{ scope.row.metricCompleteness === 'COMPLETE' ? 'COMPLETE' : 'PARTIAL' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div style="margin-top: 14px; text-align: right;"> |
| | | <el-pagination |
| | | background |
| | | layout="total, sizes, prev, pager, next" |
| | | :current-page="currentPage" |
| | | :page-size="pageSize" |
| | | :page-sizes="[20, 50, 100, 200]" |
| | | :total="pageTotal" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange"> |
| | | </el-pagination> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | |
| | | <section class="panel"> |
| | | <div class="panel-head"> |
| | | <div class="panel-title">分析结果</div> |
| | | </div> |
| | | <div class="panel-body"> |
| | | <div v-if="analysis.summary.partialTaskCount > 0" class="quality-banner"> |
| | | 当前结果中有 {{ analysis.summary.partialTaskCount }} 条历史任务缺少阶段采集,仅参与总耗时和故障统计。 |
| | | </div> |
| | | |
| | | <div v-if="!analysisReady" class="empty-shell">先从上方筛选任务或时间范围,然后执行分析。</div> |
| | | <template v-else> |
| | | <div class="summary-grid"> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">任务数</div> |
| | | <div class="summary-value">{{ formatNumber(analysis.summary.taskCount) }}</div> |
| | | <div class="summary-sub">本次分析命中任务总数</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">平均总耗时</div> |
| | | <div class="summary-value">{{ formatDuration(analysis.summary.avgTotalDurationMs) }}</div> |
| | | <div class="summary-sub">创建到完成的平均耗时</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">平均站点耗时</div> |
| | | <div class="summary-value">{{ formatDuration(analysis.summary.avgStationDurationMs) }}</div> |
| | | <div class="summary-sub">仅统计完整阶段数据</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">平均堆垛机耗时</div> |
| | | <div class="summary-value">{{ formatDuration(analysis.summary.avgCraneDurationMs) }}</div> |
| | | <div class="summary-sub">仅统计完整阶段数据</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">故障任务数</div> |
| | | <div class="summary-value">{{ formatNumber(analysis.summary.faultTaskCount) }}</div> |
| | | <div class="summary-sub">出现设备故障的任务数</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">总故障耗时</div> |
| | | <div class="summary-value">{{ formatDuration(analysis.summary.faultDurationMs) }}</div> |
| | | <div class="summary-sub">单堆垛机 / 双工位 / RGV</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="chart-grid" style="margin-top: 16px;"> |
| | | <div class="chart-card"> |
| | | <div class="chart-title">任务耗时对比</div> |
| | | <div ref="durationChart" class="chart-box"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="chart-title">耗时趋势</div> |
| | | <div ref="trendChart" class="chart-box"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="chart-title">故障任务占比</div> |
| | | <div ref="faultPieChart" class="chart-box"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="chart-title">故障耗时分布</div> |
| | | <div ref="faultDurationChart" class="chart-box"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div style="margin-top: 16px;"> |
| | | <el-table :data="analysis.detail" border stripe size="mini" max-height="360" style="width: 100%;"> |
| | | <el-table-column prop="wrkNo" label="工作号" width="100" align="center"></el-table-column> |
| | | <el-table-column prop="wmsWrkNo" label="WMS任务号" min-width="150"></el-table-column> |
| | | <el-table-column prop="ioType$" label="任务类型" width="90" align="center"></el-table-column> |
| | | <el-table-column prop="finalWrkSts$" label="最终状态" min-width="120"></el-table-column> |
| | | <el-table-column prop="sourceStaNo" label="源站" width="90" align="center"></el-table-column> |
| | | <el-table-column prop="staNo" label="目标站" width="90" align="center"></el-table-column> |
| | | <el-table-column prop="appeTime$" label="创建时间" width="165"></el-table-column> |
| | | <el-table-column prop="finishTime$" label="完成时间" width="165"></el-table-column> |
| | | <el-table-column label="总耗时" width="130" align="right"> |
| | | <template slot-scope="scope">{{ formatDuration(scope.row.totalDurationMs) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="站点耗时" width="130" align="right"> |
| | | <template slot-scope="scope">{{ formatDuration(scope.row.stationDurationMs) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="堆垛机耗时" width="130" align="right"> |
| | | <template slot-scope="scope">{{ formatDuration(scope.row.craneDurationMs) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="faultCount" label="故障次数" width="90" align="center"></el-table-column> |
| | | <el-table-column label="故障耗时" width="130" align="right"> |
| | | <template slot-scope="scope">{{ formatDuration(scope.row.faultDurationMs) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="数据完整性" width="110" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-tag size="mini" :class="scope.row.metricCompleteness === 'COMPLETE' ? 'tag-complete' : 'tag-partial'"> |
| | | {{ scope.row.metricCompleteness === 'COMPLETE' ? 'COMPLETE' : 'PARTIAL' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </template> |
| | | </div> |
| | | </section> |
| | | </div> |
| | | |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script> |
| | | <script type="text/javascript" src="../../static/vue/element/element.js"></script> |
| | | <script type="text/javascript" src="../../static/js/echarts/echarts.min.js"></script> |
| | | <script type="text/javascript" src="../../static/js/wrkAnalysis/wrkAnalysis.js?v=20260322_01" charset="utf-8"></script> |
| | | </body> |
| | | </html> |