package com.sw.mes.outside.service.impl;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sw.mes.base.factory.init.entity.*;
import com.sw.mes.base.factory.init.service.EquipmentService;
import com.sw.mes.base.factory.init.service.FactoryModelService;
import com.sw.mes.base.factory.init.service.MachineCapacityService;
import com.sw.mes.base.factory.product.entity.NewProductModel;
import com.sw.mes.base.factory.product.entity.ProductModel;
import com.sw.mes.base.factory.product.service.NewProductModelDetailService;
import com.sw.mes.base.factory.product.service.NewProductModelService;
import com.sw.mes.base.global.GlobalSwitch;
import com.sw.mes.base.global.GlobalSwitchNamespace;
import com.sw.mes.base.global.SwitchConfig;
import com.sw.mes.base.system.entity.DictionaryItem;
import com.sw.mes.base.system.entity.SysLog;
import com.sw.mes.base.system.service.DictionaryItemService;
import com.sw.mes.base.system.service.SysLogService;
import com.sw.mes.base.technology.entity.MaterialPath;
import com.sw.mes.base.technology.service.MaterialPathService;
import com.sw.mes.base.workinprocess.dto.ProcessingOrderNewDto;
import com.sw.mes.base.workinprocess.entity.PlanProcessingOrder;
import com.sw.mes.base.workinprocess.service.PlanProcessingOrderService;
import com.sw.mes.base.workinprocess.service.WipStorageService;
import com.sw.mes.config.drabbitmq.DrabbitmqConfig;
import com.sw.mes.core.annotation.Jinko;
import com.sw.mes.core.annotation.ServiceLogs;
import com.sw.mes.core.api.system.AccessoryFeignClients;
import com.sw.mes.core.api.system.StoreFeignClients;
import com.sw.mes.core.context.BaseContextHandler;
import com.sw.mes.core.dto.AccessoryProductionOrderDto;
import com.sw.mes.core.dto.ProductionScheduleDto;
import com.sw.mes.core.dto.store.WarehouseInventoryDto;
import com.sw.mes.core.dto.store.WarehouseOutputBillBo;
import com.sw.mes.core.dto.store.WarehouseOutputBillDetailBo;
import com.sw.mes.core.entity.SearchFilter;
import com.sw.mes.core.enums.EnumValuesFactory;
import com.sw.mes.core.enums.SeedMassageEnum;
import com.sw.mes.core.exception.BusinessException;
import com.sw.mes.core.response.Result;
import com.sw.mes.core.response.ResultCode;
import com.sw.mes.core.response.ResultGenerator;
import com.sw.mes.core.utils.*;
import com.sw.mes.dingTalk.service.DingTalkService;
import com.sw.mes.enumvalue.EnumValuesEap;
import com.sw.mes.enumvalue.EnumValuesServer;
import com.sw.mes.enumvalue.SurplusStepEnum;
import com.sw.mes.mmcs.entity.MmcsDistributionPlan;
import com.sw.mes.mmcs.service.MmcsDistributionPlanService;
import com.sw.mes.mqtt.service.NotificationService;
import com.sw.mes.outside.dto.*;
import com.sw.mes.outside.entity.AlarmConfig;
import com.sw.mes.outside.entity.AlarmData;
import com.sw.mes.outside.service.AlarmConfigService;
import com.sw.mes.outside.service.AlarmDataService;
import com.sw.mes.outside.service.EapService;
import com.sw.mes.outside.util.EAPUtils;
import com.sw.mes.production.entity.FormulaWeightScheme;
import com.sw.mes.production.service.FormulaWeightSchemeService;
import com.sw.mes.rabbitmq.constants.RoutingKey;
import com.sw.mes.rabbitmq.enums.ExchangeEnum;
import com.sw.mes.rabbitmq.enums.QueueEnum;
import com.sw.mes.rabbitmq.service.RabbitMqService;
import com.sw.mes.redis.server.RedisUtils;
import com.sw.mes.sap.dao.ReportArgonErrorLogDao;
import com.sw.mes.sap.entity.ReportArgonErrorLog;
import com.sw.mes.sap.entity.ReportSapGoodsmvt;
import com.sw.mes.sap.service.ReportSapGoodsmvtService;
import com.sw.mes.yb.accessory.entity.EquipmentDeviceStatus;
import com.sw.mes.yb.accessory.service.EquipmentDeviceStatusService;
import com.sw.mes.yb.ipc.entity.ParamSet;
import com.sw.mes.yb.ipc.service.ParamSetService;
import com.sw.mes.yb.pc.entity.EstimatedRecord;
import com.sw.mes.yb.pc.service.EstimatedRecordService;
import com.sw.mes.yb.plan.entity.PlanProductionOrderBom;
import com.sw.mes.yb.plan.entity.ProductionOrder;
import com.sw.mes.yb.plan.entity.ProductionSchedule;
import com.sw.mes.yb.plan.service.PlanProductionOrderBomService;
import com.sw.mes.yb.plan.service.ProductionOrderService;
import com.sw.mes.yb.plan.service.ProductionScheduleService;
import com.sw.mes.yb.production.dto.AgainTask.AgainTaskDto;
import com.sw.mes.yb.production.dto.AgainTask.ConfirmWeightDto;
import com.sw.mes.yb.production.dto.AgainTaskPrepare.AgainTaskPrepareDto;
import com.sw.mes.yb.production.dto.LoadingTask.LoadingTaskDto;
import com.sw.mes.yb.production.dto.LongCrystalInfoDto;
import com.sw.mes.yb.production.dto.LongCrystalVO;
import com.sw.mes.yb.production.entity.*;
import com.sw.mes.yb.production.enums.SurplusInfoStatusEnum;
import com.sw.mes.yb.production.notification.SongYanMessage;
import com.sw.mes.yb.production.service.*;
import com.sw.mes.yb.production.utils.RecycledBatchGenerator;
import com.sw.mes.yb.report.dao.CrystalPullingInfoLogDao;
import com.sw.mes.yb.report.dto.BrokenBractBaseContinuousDto;
import com.sw.mes.yb.report.entity.*;
import com.sw.mes.yb.report.service.*;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.sw.mes.base.global.GlobalSwitchNamespace.CODE_SWITCH;
import static com.sw.mes.enumvalue.EnumValuesEap.EapSignalType.*;

@Service
@Slf4j
public class EapServiceImpl implements EapService {
    @Autowired
    private WipStorageService wipStorageService;
    @Autowired
    private EquipmentService equipmentService;
    @Autowired
    private ProductionScheduleService productionScheduleService;
    @Autowired
    private LongCrystalService longCrystalService;
    @Autowired
    private EAPDockingService eapDockingService;
    @Autowired
    private DictionaryItemService dictionaryItemService;
    @Autowired
    private MachineCapacityService machineCapacityService;
    @Autowired
    private NotificationService notificationService;
    @Autowired
    private CrystalPullingInfoService crystalPullingInfoService;
    @Autowired
    private CrystalPullingInfoLogService crystalPullingInfoLogService;
    @Autowired
    private ProcessPathService processPathService;
    @Autowired
    private ProcessWorkingHoursService processWorkingHoursService;
    @Autowired
    private ParamSetService paramSetService;
    @Autowired
    private EquipmentDeviceStatusService equipmentDeviceStatusService;
    @Autowired
    private ShoulderSurvivalRateService shoulderSurvivalRateService;
    @Autowired
    private ShoulderSeedingRateService shoulderSeedingRateService;
    @Autowired
    private EquipmentExtendStateService equipmentExtendStateService;
    @Autowired
    private ExpansionBaseService expansionBaseService;
    @Autowired
    private BrokenBractBaseService brokenBractBaseService;
    @Autowired
    private BrokenBractBaseContinuousService brokenBractBaseContinuousService;
    @Autowired
    private SuccessCrystalBaseService successCrystalBaseService;
    @Autowired
    private CrystalBrokenBaseService crystalBrokenBaseService;
    @Autowired
    private MmcsDistributionPlanService mmcsDistributionPlanService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private LoadingTaskService loadingTaskService;
    @Autowired
    private AgainTaskService againTaskService;
    @Autowired
    private InputFurnaceBurdenService inputFurnaceBurdenService;
    @Autowired
    private AgainTaskDetailService againTaskDetailService;
    @Autowired
    private MaterialPathService materialPathService;
    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private IpcButtonControlService ipcButtonControlService;

    @Autowired
    private ProductionOrderService productionOrderService;

    @Autowired
    private CrystalPullingInfoLogDao crystalPullingInfoLogDao;

    @Autowired
    private ProcessWorkingStepService processWorkingStepService;

    @Autowired
    EstimatedRecordService estimatedRecordService;

    @Autowired
    private OutputRecordService outputRecordService;
    @Autowired
    private SysLogService sysLogService;

    @Autowired
    private FactoryModelService factoryModelService;

    @Autowired
    private NewProductModelService newProductModelService;

    @Autowired
    private SurplusInfoService surplusInfoService;

    @Autowired
    private ReportSapGoodsmvtService reportSapGoodsmvtService;

    @Autowired
    private NewProductModelDetailService newProductModelDetailService;

    @Autowired
    StoreFeignClients storeFeignClients;

    @Autowired
    RecycledBatchGenerator recycledBatchGenerator;

    @Autowired
    private BlastFurnaceDetailService blastFurnaceDetailService;

    @Autowired
    AccessoryFeignClients accessoryFeignClients;

    @Autowired
    private RabbitMqService rabbitMqService;
    @Autowired
    private ProcessNoDiameterService processNoDiameterService;
    @Autowired
    private AlarmDataService alarmDataService;
    @Autowired
    private AlarmConfigService alarmConfigService;

    @Autowired
    private PlanProductionOrderBomService planProductionOrderBomService;

    @Autowired
    private FormulaWeightSchemeService formulaWeightSchemeService;

    /**
     * 当前设备等径最大长度
     */
    private static final String equalDiameterLengthKey = "equalDiameterLength";

    /**
     * 当前设备拆清开始时间
     */
    private static final String dismantlingStartKey = "dismantlingStart";

    @Autowired
    private DingTalkService dingTalkService;
    private static final String splicCode = "qwe123!@#";

    @Autowired
    private ReportArgonErrorLogDao reportArgonErrorLogDao;

    private static Map<Integer, String> convertNumMap = new HashMap<Integer, String>();

    private static Map<String, String> materialCountTaskCodeScope = new HashMap<String, String>();

    public EapServiceImpl() {
        materialCountTaskCodeScope.put("初始装料", "1424");
        materialCountTaskCodeScope.put("投料重量", "复投总重");
        materialCountTaskCodeScope.put("初始复投", "复投总重");


        convertNumMap.put(0, "零段保持");
        convertNumMap.put(1, "一段保持");
        convertNumMap.put(2, "二段保持");
        convertNumMap.put(3, "三段保持");
        convertNumMap.put(4, "四段保持");
        convertNumMap.put(5, "五段保持");
        convertNumMap.put(6, "六段保持");
        convertNumMap.put(7, "七段保持");
        convertNumMap.put(8, "八段保持");
        convertNumMap.put(9, "九段保持");
        convertNumMap.put(10, "十段保持");
        convertNumMap.put(11, "十一段保持");
        convertNumMap.put(12, "十二段保持");
        convertNumMap.put(13, "十三段保持");
        convertNumMap.put(14, "十四段保持");
        convertNumMap.put(15, "十五段保持");
        convertNumMap.put(16, "十六段保持");
        convertNumMap.put(17, "十七段保持");
        convertNumMap.put(18, "十八段保持");
        convertNumMap.put(19, "十九段保持");
        convertNumMap.put(20, "二十段保持");
        convertNumMap.put(21, "二十一段保持");
        convertNumMap.put(22, "二十二段保持");
        convertNumMap.put(23, "二十三段保持");
        convertNumMap.put(24, "二十四段保持");
        convertNumMap.put(25, "二十五段保持");
        convertNumMap.put(26, "二十六段保持");
        convertNumMap.put(27, "二十七段保持");
        convertNumMap.put(28, "二十八段保持");
        convertNumMap.put(29, "二十九段保持");
        convertNumMap.put(30, "三十段保持");
        convertNumMap.put(31, "三十一段保持");
        convertNumMap.put(32, "三十二段保持");
        convertNumMap.put(33, "三十三段保持");
        convertNumMap.put(34, "三十四段保持");
        convertNumMap.put(35, "三十五段保持");
        convertNumMap.put(36, "三十六段保持");
        convertNumMap.put(37, "三十七段保持");
        convertNumMap.put(38, "三十八段保持");
        convertNumMap.put(39, "三十九段保持");
        convertNumMap.put(40, "四十段保持");
        convertNumMap.put(41, "四十一段保持");
        convertNumMap.put(42, "四十二段保持");
        convertNumMap.put(43, "四十三段保持");
        convertNumMap.put(44, "四十四段保持");
        convertNumMap.put(45, "四十五段保持");
        convertNumMap.put(46, "四十六段保持");
        convertNumMap.put(47, "四十七段保持");
        convertNumMap.put(48, "四十八段保持");
        convertNumMap.put(49, "四十九段保持");
        convertNumMap.put(50, "五十段保持");
    }


    @Override
    @Transactional
    public Result sendEapSignal(SendEapSignalDto sendEapSignalDto, int source) {
        if (sendEapSignalDto.getCrystalInfoDto() == null) {
            sendEapSignalDto.setCrystalInfoDto(new CrystalInfoDto());
        }
        Equipment equipment = equipmentService.findByCode(sendEapSignalDto.getEquipmentCode());
        if (equipment == null) {
            return ResultGenerator.fail(sendEapSignalDto.getEquipmentCode() + "设备编号不存在");
        }
        EnumValuesEap.EapSignalType eapSignalenum = EnumValuesEap.EapSignalType.getEnum(sendEapSignalDto.getSignal());
        ProductionSchedule productionSchedule;
        if (eapSignalenum != null) {
            if (eapSignalenum == 开炉 || eapSignalenum == 备料) {
                MachineCapacity machineCapacity = machineCapacityService.findByEquipmentId(equipment.getId());
                if (machineCapacity != null) {
                    // 可排程时间
                    Date time = DateUtil.add(machineCapacity.getCreateTime(), 1, Calendar.MINUTE);
                    boolean klStatusBool = time.getTime() > new Date().getTime();
                    if (klStatusBool) {
                        return ResultGenerator.fail(equipment.getCode() +
                                "自动排程处理中，请在" + DateUtil.formatDate(time, DateUtil.longFormat) + "后，再点击备料");
                    }
                }
                productionSchedule = productionScheduleService.findTopByDeviceIdAndStatusOrderByIdDesc(equipment.getId(), EnumValuesServer.ProductionScheduleStatus.未开始.getKey());
                if (productionSchedule == null) {
                    log.error(sendEapSignalDto.getEquipmentCode() + "该设备没有的排产任务");
                    return ResultGenerator.fail(sendEapSignalDto.getEquipmentCode() + "该设备没有的排产任务");
                }
            } else {
                productionSchedule = productionScheduleService.findTopByDeviceIdAndStatusOrderByIdDesc(equipment.getId(), EnumValuesServer.ProductionScheduleStatus.进行中.getKey());
                if (productionSchedule == null) {
                    log.error(sendEapSignalDto.getEquipmentCode() + "该设备没有【进行中】状态的排产任务，或者检查IPC中是否完成【确认投料】操作");
                    return ResultGenerator.fail(sendEapSignalDto.getEquipmentCode() + "该设备没有【进行中】状态的排产任务，或者检查IPC中是否完成【确认投料】操作");
                }
            }
            if (eapSignalenum == 取段结晶) {
                final String key = "all_device_qdjj";
                ipcButtonControlService.ipcButtonControlTime(new IpcButtonControlTimeParam("取段结晶", 60, sendEapSignalDto.getEquipmentCode(), "取段结晶", key));

                Result result = handleQujj(sendEapSignalDto, equipment, productionSchedule, true, source);
                if (result.getCode() == 200) {
                    //按钮执行成功，更新redis对应key时间
                    redisUtils.hmSet(key, sendEapSignalDto.getEquipmentCode(), DateUtil.formatDate(new Date(), DateUtil.longFormat));
                }
                return result;
            } else if (eapSignalenum == 停炉) {
                final String key = "all_device_shutdown_time";
                ipcButtonControlService.ipcButtonControlTime(new IpcButtonControlTimeParam("停炉", 600, sendEapSignalDto.getEquipmentCode(), "停炉", key));

                equipmentDeviceStatusService.accessoryLifeRecord(sendEapSignalDto);
                if (StringUtils.isNotBlank(sendEapSignalDto.getCrystalInfoDto().getClosureIdentification()) && "W".equals(sendEapSignalDto.getCrystalInfoDto().getClosureIdentification())) {
                    //卡控:一个炉次只能有一根完结尾
                    LongCrystal longCrystal = longCrystalService.findBLotNoAndClosureIdentification(productionSchedule.getLotNo(), "W");
                    if (null != longCrystal) {
                        log.error(longCrystal.getLotNo() + "炉次存在完结尾晶棒,编号为 :" + longCrystal.getCrystalNo());
                        return ResultGenerator.fail(longCrystal.getLotNo() + "炉次存在完结尾晶棒,编号为 :" + longCrystal.getCrystalNo());
                    }
                }
                Result result = handleTl(sendEapSignalDto, equipment, productionSchedule, 0);
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date = new Date();
                String format = formatter.format(date);
                String content = "炉台:" + sendEapSignalDto.getEquipmentCode() + "已经停炉，" + "时间" + format;
                String groupName = "停炉推送";
                String source1 = "停炉推送";
                robotSeedMessage(content, groupName, source1);
                if (result.getCode() == 200) {
                    //按钮执行成功，更新redis对应key时间
                    redisUtils.hmSet(key, sendEapSignalDto.getEquipmentCode(), DateUtil.formatDate(new Date(), DateUtil.longFormat));

                    // 自动开炉
                    DrabbitmqConfig.sendAutomaticDelayedStartOfMaterialPreparation(sendEapSignalDto.getEquipmentCode());
                }
                return result;
            } else if (eapSignalenum == 开炉 || eapSignalenum == 备料) {
                final String key;
                if (eapSignalenum == 开炉) {
                    key = "all_device_kl";
                    ipcButtonControlService.ipcButtonControlTime(new IpcButtonControlTimeParam("开炉", 60, sendEapSignalDto.getEquipmentCode(), "开炉", key));
                } else {
                    key = "all_device_bl";
                    ipcButtonControlService.ipcButtonControlTime(new IpcButtonControlTimeParam("备料", 60, sendEapSignalDto.getEquipmentCode(), "备料", key));
                }
                Result result = handleKl(sendEapSignalDto, equipment, productionSchedule);
                if (result.getCode() == 200) {
                    //按钮执行成功，更新redis对应key时间
                    redisUtils.hmSet(key, sendEapSignalDto.getEquipmentCode(), DateUtil.formatDate(new Date(), DateUtil.longFormat));
                }
                return result;
            } else if (eapSignalenum == 熔料) {
                final String key = "all_device_rl";
                ipcButtonControlService.ipcButtonControlTime(new IpcButtonControlTimeParam("熔料", 60, equipment.getCode(), "熔料", key));
                //剩料重量
                BigDecimal residualWeight = sendEapSignalDto.getCrystalInfoDto().getResidualWeight();
                //总装锅料
                BigDecimal totalWeight = sendEapSignalDto.getCrystalInfoDto().getTotalWeight();
                PlanProcessingOrder planProcessingOrder = planProcessingOrderService.get(productionSchedule.getLotId());
                Result result = handleRl(equipment, productionSchedule, residualWeight, totalWeight, planProcessingOrder.getAgainNum());
                if (result.getCode() == 200) {
                    //按钮执行成功，更新redis对应key时间
                    redisUtils.hmSet(key, equipment.getCode(), DateUtil.formatDate(new Date(), DateUtil.longFormat));
                }
                return result;
            } else if (eapSignalenum == 取段稳温) {
                final String key = "all_device_qdww";
                ipcButtonControlService.ipcButtonControlTime(new IpcButtonControlTimeParam("取段稳温", 60, sendEapSignalDto.getEquipmentCode(), "取段稳温", key));
                Result result = handleQujj(sendEapSignalDto, equipment, productionSchedule, false, source);
                if (result.getCode() == 200) {
                    //按钮执行成功，更新redis对应key时间
                    redisUtils.hmSet(key, sendEapSignalDto.getEquipmentCode(), DateUtil.formatDate(new Date(), DateUtil.longFormat));
                }
                return result;
            } else if (eapSignalenum == 焖锅漏硅) {
                return handleTl(sendEapSignalDto, equipment, productionSchedule, 1);
            } else if (eapSignalenum == 抽真空) {
                final String key = "all_device_czk";
                ipcButtonControlService.ipcButtonControlTime(new IpcButtonControlTimeParam("开炉", 60, sendEapSignalDto.getEquipmentCode(), "抽真空", key));
                equipmentDeviceStatusService.accessoryLifeRecord(sendEapSignalDto);
                redisUtils.hmSet(key, sendEapSignalDto.getEquipmentCode(), DateUtil.formatDate(new Date(), DateUtil.longFormat));

                //需求变更：抽真空时，记录（更新）拉晶实时情况的开始时间
                updateStartTimeForPullingInfo(sendEapSignalDto.getEquipmentCode());

                return ResultGenerator.success();
            } else if (eapSignalenum == 预装料) {
                return handleYzl(sendEapSignalDto, equipment, productionSchedule, source);
            } else {
                log.info("该信号未处理");
                return ResultGenerator.fail("该信号未处理");
            }
        } else {
            log.info("信号类型未知");
            return ResultGenerator.fail("信号类型未知");
        }
    }

    /**
     * 发送消息到精彩群
     */
    private void robotSeedMessage(String content, String groupName, String source) {
        try {
            ArrayList<String> sceneList = new ArrayList<>();
            ArrayList<String> groupNameList = new ArrayList<>();
            sceneList.add("prod");
            groupNameList.add(groupName);
            dingTalkService.robotSeedMessage(content, groupNameList, source, sceneList, SeedMassageEnum.GROUP);

//            SeedMassageDto seedMassageDto = new SeedMassageDto();
//            seedMassageDto.setContent(content);
//            seedMassageDto.setScene(sceneList);
//            seedMassageDto.setGroupName(groupNameList);
//            seedMassageDto.setStatus(SeedMassageEnum.GROUP);
//            seedMassageDto.setSource(source);
//            dingTalkService.seedMassage(seedMassageDto);
        } catch (Exception e) {
            log.error("error_in_sendEapSignal", e);
        }
    }

    /**
     * 处理熔料信号
     *
     * @param equipment
     * @param productionSchedule
     * @return
     */
    private Result handleRl(Equipment equipment, ProductionSchedule productionSchedule, BigDecimal residualWeight, BigDecimal totalWeight, Integer againNum) {
        // 初始复投任务只能有1次
        List<AgainTask> taskList = againTaskService.findByLotNoAndAgainNum(productionSchedule.getLotNo(), 1);
        if (!CollectionUtils.isEmpty(taskList)) {
            for (AgainTask task : taskList) {
                if (!AgainTaskDto.rawStatus_2.equals(task.getRawStatus())) {
                    return ResultGenerator.fail("当前炉次【" + productionSchedule.getLotNo() + "】已存在初始复投任务");
                }
            }
        }

        productionSchedule.setStatusInfo("接收熔料信号");
        productionScheduleService.saveOrUpdate(productionSchedule);
        // 发送初始复投
        return ftTaskCreate(productionSchedule, equipment, 1, residualWeight, totalWeight, 1, 0, true);
    }


    @Autowired
    private PlanProcessingOrderService planProcessingOrderService;

    /**
     * 处理开炉信号
     *
     * @param sendEapSignalDto
     * @param equipment
     * @param productionSchedule
     * @return
     */
    private Result handleKl(SendEapSignalDto sendEapSignalDto, Equipment equipment, ProductionSchedule productionSchedule) {
        ProductionSchedule productionScheduleRun = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(equipment.getCode(), Arrays.asList(1, 4, 5));
        if (productionScheduleRun != null) {
            log.error(sendEapSignalDto.getEquipmentCode() + "该设备还有进行中的排产任务，无法开炉");
            return ResultGenerator.fail(sendEapSignalDto.getEquipmentCode() + "该设备还有进行中的排产任务，无法开炉");
        }

        // 检查排程是否唯一
        if (GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_YB_CHECK, "checkOnlySchedule", false)){
            List<ProductionSchedule> scheduleList = productionScheduleService.findByDeviceNoAndStatus(equipment.getCode(), Arrays.asList(1, 4, 5));
            if (!CollectionUtils.isEmpty(scheduleList)) {
                return ResultGenerator.fail("该炉台已有正在执行的排程，请检查确认");
            }
            ProductionOrder productionOrder = productionSchedule.getProductionOrder();
            if (ObjectUtils.isEmpty(productionOrder)) {
                return ResultGenerator.fail("排程关联订单不存在");
            }
            List<ProductionSchedule> normalList = productionScheduleService.findNormalListByOrderId(productionOrder.getId());
            if (!CollectionUtils.isEmpty(normalList) && normalList.size() >= 2){
                return ResultGenerator.fail("订单【"+productionOrder.getAufnr()+"】关联了多个排程，请检查确认");
            }
        }

        String moth = DateUtil.formatDate(new Date(), "yyMM");
        LocalDate localDate = LocalDate.now();
        String ym = (localDate.getYear() - 2020) + CodeMapUtils.NUM_CODE_MAP.get(localDate.getMonthValue());

        PlanProcessingOrder planProcessingOrderLast = planProcessingOrderService.findTopByEquipmentIdAndOrderTypeAndMothOrderByIdDesc(equipment.getId(), EnumValuesFactory.WipStorOrderType.炉次.getKey(), moth);
        // 开启新的炉次
        PlanProcessingOrder planProcessingOrder = new PlanProcessingOrder();
        planProcessingOrder.setWeight(BigDecimal.valueOf(productionSchedule.getCount()));
        planProcessingOrder.setModelId(productionSchedule.getProductModel().getId());
        planProcessingOrder.setModelType(EnumValuesFactory.PlanProcessingOrderModelType.产品.getKey());
        planProcessingOrder.setEquipmentId(equipment.getId());
        planProcessingOrder.setEquipmentCode(equipment.getCode());
        //设置复投次数第一次为1
        planProcessingOrder.setAgainNum(1);
        planProcessingOrder.setCrystalNum(1);
        if (planProcessingOrderLast == null) {
            planProcessingOrder.setLotTimes(1);
        } else {
            planProcessingOrder.setLotTimes(planProcessingOrderLast.getLotTimes() + 1);
        }
        planProcessingOrder.setMoth(moth);
        planProcessingOrder.setStatus(EnumValuesFactory.PlanProcessingOrderStatus.待加工.getKey());
        planProcessingOrder.setCode(ym + equipment.getCode() + planProcessingOrder.getLotTimes());
        planProcessingOrder.setOrderType(EnumValuesFactory.WipStorOrderType.炉次.getKey());
        planProcessingOrder.setOrderDataId(productionSchedule.getProductionOrder().getId());
        planProcessingOrder.setGoodsId(0L);
        MaterialPath materialPath = materialPathService.getMaterialPath(productionSchedule.getProductModel().getCode(), EnumValuesFactory.WipStorOrderType.炉次.getKey());
        if (materialPath == null || materialPath.getPathId() == null) {
            return ResultGenerator.fail("料号" + productionSchedule.getProductModel().getCode() + "未绑定工艺路线，无法开炉！");
        }
        planProcessingOrder.setPathId(materialPath.getPathId());
        planProcessingOrderService.createPlanProcessingOrder(planProcessingOrder, null, null);
        planProcessingOrder.setGoodsId(planProcessingOrder.getId());
        planProcessingOrderService.createPlanProcessingOrder(planProcessingOrder, null, null);
        wipStorageService.inStation(planProcessingOrder.getCode(), equipment.getCode());

        // 补充排程信息
        productionSchedule.setLotNo(planProcessingOrder.getCode());
        productionSchedule.setLotId(planProcessingOrder.getId());
        //炉次内切换排程
        if (StringUtils.isEmpty(productionSchedule.getProductionOrder().getFromMark())) {
            //炉次内切换排程
            productionSchedule.setDeliveryNo(ym + equipment.getCode() + planProcessingOrder.getLotTimes());
        } else {
            productionSchedule.setDeliveryNo(productionSchedule.getProductionOrder().getBaseMark() + productionSchedule.getProductionOrder().getProductType() + ym + equipment.getCode() + planProcessingOrder.getLotTimes());
        }
        productionSchedule.setStatusInfo("接收到开炉信号，开始新的炉次");
        productionSchedule.setStatus(EnumValuesServer.ProductionScheduleStatus.待备料.getKey());
        productionScheduleService.saveOrUpdate(productionSchedule);

        //开炉清空拉晶情况 - 重置炉次号
        resetCode(productionSchedule.getDeviceNo(), productionSchedule.getLotNo(), productionSchedule.getProductModel());

        //开炉时修改设备状态为运行
        equipment.setEnableState(true);
        equipmentService.saveOrUpdate(equipment);

        return handleStart(equipment, productionSchedule);
    }

    private String generateStoveFirstSegment(Boolean flag) {
        Calendar c = Calendar.getInstance();
        String year = String.valueOf(String.valueOf(c.get(Calendar.YEAR)).charAt(3));
        int intMonth = c.get(Calendar.MONTH) + 1;
        int day = c.get(Calendar.DAY_OF_MONTH);

        //未超过当月第一天早上八点半月份减少一月
        if (!flag) {
            if (intMonth == 1) {
                intMonth = 12;
                c.add(Calendar.YEAR, -1);
                year = String.valueOf(String.valueOf(c.get(Calendar.YEAR)).charAt(3));
            }
            if (intMonth != 1 && day == 1) {
                intMonth = intMonth - 1;
            }
        }
        String strMonth = String.valueOf(intMonth);
        if (intMonth > 9) {
            switch (intMonth) {
                case 10:
                    strMonth = "A";
                    break;
                case 11:
                    strMonth = "B";
                    break;
                case 12:
                    strMonth = "C";
                    break;
            }
        }

        return year + strMonth;
    }

    /**
     * 处理停炉信号
     *
     * @param sendEapSignalDto   eap 信号
     * @param equipment          设备
     * @param productionSchedule 设备排产任务
     * @param type               0：正常停炉，1：焖锅漏硅
     * @return
     */
    private Result handleTl(SendEapSignalDto sendEapSignalDto, Equipment equipment, ProductionSchedule productionSchedule, int type) {
        PlanProcessingOrder planProcessingOrder = planProcessingOrderService.get(productionSchedule.getLotId());
        equipment.setEnableState(true);
        equipmentService.saveOrUpdate(equipment);

        LongCrystal longCrystal = null;
        if (type == 0 && sendEapSignalDto.getCrystalInfoDto() != null && sendEapSignalDto.getCrystalInfoDto().getLength() != null && sendEapSignalDto.getCrystalInfoDto().getLength().doubleValue() > 350) {
            if (sendEapSignalDto.getCrystalInfoDto() != null && sendEapSignalDto.getCrystalInfoDto().getAgainNum() != null) {
                planProcessingOrder.setAgainNum(sendEapSignalDto.getCrystalInfoDto().getAgainNum());
            }
            if (sendEapSignalDto.getCrystalInfoDto() != null && sendEapSignalDto.getCrystalInfoDto().getCrystalNum() != null) {
                planProcessingOrder.setCrystalNum(sendEapSignalDto.getCrystalInfoDto().getCrystalNum());
            }
            // 6期遗留问题
            //execCrystalNumRule(sendEapSignalDto, planProcessingOrder);
            // 保存长棒信息
            longCrystal = saveLongCrystal(sendEapSignalDto, equipment, productionSchedule, planProcessingOrder, true);
        }
        planProcessingOrder.setStatus(EnumValuesFactory.PlanProcessingOrderStatus.加工完成.getKey());
        planProcessingOrder.setRemarks("接收到停炉信号，炉次结束");
        planProcessingOrder.setUpdateTime(new Date());
        planProcessingOrder.setUpdateUserName(BaseContextHandler.getUsername());
        planProcessingOrderService.saveOrUpdate(planProcessingOrder);

        // 没有未确认的晶棒，则关闭排程
        List<LongCrystal> crystalList = longCrystalService.findUnconfirmedByLotNo(productionSchedule.getLotNo());
        if (CollectionUtils.isEmpty(crystalList)) {
            productionSchedule.setStatus(EnumValuesServer.ProductionScheduleStatus.已完成.getKey());
            productionSchedule.setStatusInfo("接收停炉信号");
            productionSchedule.setCloseTime(new Date());
            productionScheduleService.saveOrUpdate(productionSchedule);
        }


        // 上报石墨件消耗
        reportGraphiteToAccessory(productionSchedule);

        //修改设备加工能力的已开炉次数量
        MachineCapacity machineCapacity = machineCapacityService.findByEquipmentId(planProcessingOrder.getEquipmentId());
        if (machineCapacity != null) {
            //防止eap错误，关闭自动增长已开炉次
            machineCapacity.setUsedCount((machineCapacity.getUsedCount() == null ? 0 : machineCapacity.getUsedCount()) + 1);
            if (machineCapacity.getCapacityInfoNext() != null) {
                MachineCapacity machineCapacity1 = JSON.parseObject(machineCapacity.getCapacityInfoNext(), MachineCapacity.class);
                machineCapacity.setCapacityInfo(machineCapacity1.getCapacityInfo());
                machineCapacity.setProductType(machineCapacity1.getProductType());
                machineCapacity.setRdlogo(machineCapacity1.getRdlogo());
                machineCapacity.setRdonlylogo(machineCapacity1.getRdonlylogo());
                machineCapacity.setCapacityInfoNext(null);
                machineCapacity.setCreateTime(new Date());
            }
        }
        machineCapacityService.saveOrUpdate(machineCapacity);
        if (longCrystal != null) {
            //处理完所有业务，发送取晶体通知
            sendGetCrystalNotification(longCrystal);
        }
        CrystalPullingInfo info = crystalPullingInfoService.getByDeviceNo(equipment.getCode());
        clearCrystalPullingInfo(info, equipment.getCode());
        // 上报SAP氩气消耗
        SwitchConfig config = GlobalSwitch.switchConfig(CODE_SWITCH, "reportArgon", false);
        if(null != productionSchedule && config.isEnable()){
            this.reportArgonToSap(productionSchedule.getDeviceNo(), productionSchedule.getProductionOrder().getAufnr(), productionSchedule.getLotNo(), "停炉");
        }
        redisUtils.hmSet("all_device_shutdown_time", equipment.getCode(), DateUtil.formatDate(new Date(), DateUtil.longFormat));
        return ResultGenerator.success();
    }


    /**
     * 更新石墨件消耗。 上报消耗,工时
     *
     * @param schedule
     */
    private void reportGraphiteToAccessory(ProductionSchedule schedule) {
        // 石墨件消耗更新
        AccessoryProductionOrderDto dto = new AccessoryProductionOrderDto();
        dto.setOrderNoId(schedule.getId());
        dto.setProductionNo(schedule.getProductionOrder().getAufnr());
        dto.setDeviceCode(schedule.getDeviceNo());
        dto.setMaterialCode(schedule.getProductionOrder().getMatnr());
        dto.setEndTime(new Date());
        Result<String> result = accessoryFeignClients.update(dto);
        if (result.getCode() != ResultCode.SUCCESS.getCode()) {
            log.error("修改石墨件消耗异常: " + result.getMessage());
            log.error("修改石墨件消耗异常报文: " + JSON.toJSONString(dto));
        }

//        ProductionScheduleDto scheduleDto = new ProductionScheduleDto();
//        scheduleDto.setId(schedule.getId());
//        Result result1 = accessoryFeignClients.productionOrderToSAP(scheduleDto);
//        if (result1.getCode() != ResultCode.SUCCESS.getCode()) {
//            log.error("石墨件工单报工时异常: " + result1.getMessage());
//            log.error("石墨件工单报工时异常报文: " + JSON.toJSONString(scheduleDto));
//        }
//
//        Result result2 = accessoryFeignClients.productionMaterialToSAP(scheduleDto);
//        if (result2.getCode() != ResultCode.SUCCESS.getCode()) {
//            log.error("石墨件物料报消耗异常: " + result2.getMessage());
//            log.error("石墨件物料报消耗异常报文: " + JSON.toJSONString(scheduleDto));
//        }
    }


    /**
     * 停炉时上报SAP氩气消耗
     * @param equipmentCode
     * @param orderNo
     * @param lotNo
     */

    @Override
    public void reportArgonToSap(String equipmentCode, String orderNo, String lotNo, String type) {

        try {
            ReportSapGoodsmvt reportSap = new ReportSapGoodsmvt();
            reportSap.setBwart("261");        // 移动类型
            reportSap.setKostl(null);        // 成本中心
            reportSap.setProcName(EnumValuesFactory.OrderProgress.氩气消耗.getValue());    // 工序
            reportSap.setCrystalNo(type);
            List<String> list = new ArrayList<>();
            list.add("261");
            // SAP生产订单
            reportSap.setAufnr(orderNo);
            // 设备号
            reportSap.setDeviceNo(equipmentCode);
            //物料号
            ProductionOrder byeErpOrderNo = productionOrderService.findByAufnr(orderNo);
            if(null == byeErpOrderNo){
                return;
            }
            PlanProductionOrderBom byPlanProductionOrderId = planProductionOrderBomService.findByPlanProductionOrderId(byeErpOrderNo.getId());
            if(null != byPlanProductionOrderId){
                reportSap.setMatnr(byPlanProductionOrderId.getMatnr());    // 物料编码
            } else {
                log.info("氩气：未查询到BOM信息，工单号为：" + orderNo);
                ReportArgonErrorLog reportArgonErrorLog = new ReportArgonErrorLog();
                BeanUtil.copyProperties(reportSap,reportArgonErrorLog, "id");
                reportArgonErrorLogDao.save(reportArgonErrorLog);
            }
            DictionaryItem warehouseCode = dictionaryItemService.findByThemeValueAndNameAndEnable("reportArgonToSap", "warehouseCode", true);
            //库位
            reportSap.setLgort(warehouseCode.getValue());
            reportSap.setWerks("5041");

            // 从字典中获取数量和单位
            DictionaryItem weight = dictionaryItemService.findByThemeValueAndNameAndEnable("reportArgonToSap", "weight", true);
            float weightValue = weight == null? 0: Float.valueOf(weight.getValue());
            LoadingTask byLotNo = loadingTaskService.findSuccessByLotNo(lotNo);
            if(null != byLotNo && null != byLotNo.getConfirmTime()){
                long satrtTime = byLotNo.getConfirmTime().getTime();
                long endTime = new Date().getTime();
                reportSap.setCreateDate(new Date());
                List<ReportSapGoodsmvt> byProcNameAndOrderNoAndBwart1 = reportSapGoodsmvtService.findByProcNameAndOrderNoAndBwartAndLotNo(EnumValuesFactory.OrderProgress.氩气消耗.getValue(), orderNo, list, lotNo);
                if(byProcNameAndOrderNoAndBwart1.size() > 0){
                    // remark记录已运行时长
                    long unReportTime = endTime - byProcNameAndOrderNoAndBwart1.get(0).getCreateDate().getTime();
                    log.info("氩气2：炉次：{}，运行时长：{}", lotNo, unReportTime);
                    double result = weightValue * unReportTime / (24 * 60 * 60 * 1000);
                    reportSap.setRemark(lotNo);
                    // 数量
                    reportSap.setMenge(String.valueOf(Math.round(result*1000)/1000));
                } else if(byProcNameAndOrderNoAndBwart1.size() == 0) {
                    // remark记录已运行时长
                    long unReportTime = endTime - satrtTime;
                    log.info("氩气1：炉次：{}，运行时长：{}", lotNo, unReportTime);
                    double result = weightValue * unReportTime / (24 * 60 * 60 * 1000);
                    reportSap.setRemark(lotNo);
                    // 数量
                    reportSap.setMenge(String.valueOf(Math.round(result*1000)/1000));
                }
                if (null != byPlanProductionOrderId.getMeins()) {
                    // 单位
                    reportSap.setMeins(byPlanProductionOrderId.getMeins());
                }
            }

            // 查询氩气库存信息
            Result<List<WarehouseInventoryDto>> result = storeFeignClients.getByMaterialCode(byPlanProductionOrderId.getMatnr());
            if (ResultCode.SUCCESS.getCode() != result.getCode()) {
                log.error("氩气：未查询到线边仓库存信息失败，工单号为：" + orderNo);
                ReportArgonErrorLog reportArgonErrorLog = new ReportArgonErrorLog();
                BeanUtil.copyProperties(reportSap,reportArgonErrorLog, "id");
                reportArgonErrorLogDao.save(reportArgonErrorLog);
                return;
            }
            log.info("氩气：线边仓库存查询结果:" + JSONObject.toJSONString(result.getData()));
            List<WarehouseInventoryDto> resultData = result.getData();
            if (CollectionUtils.isEmpty(resultData)) {
                log.info("氩气：未查询到线边仓库存信息，工单号为：" + orderNo);
                ReportArgonErrorLog reportArgonErrorLog = new ReportArgonErrorLog();
                BeanUtil.copyProperties(reportSap,reportArgonErrorLog, "id");
                reportArgonErrorLogDao.save(reportArgonErrorLog);
                return;
            }

            //线边仓出库
            WarehouseOutputBillBo warehouseOutputBillBo = new WarehouseOutputBillBo();
            warehouseOutputBillBo.setType("氩气消耗");
            warehouseOutputBillBo.setAutoAudit(true);

            WarehouseOutputBillDetailBo warehouseOutputBillDetailBo = new WarehouseOutputBillDetailBo();
            warehouseOutputBillDetailBo.setMaterialModelCode(reportSap.getMatnr());
            warehouseOutputBillDetailBo.setWarehouseCode(warehouseCode.getValue());

            BigDecimal menge = new BigDecimal(reportSap.getMenge());
            String charg = null;
            String lgort = null;

            for (WarehouseInventoryDto dto : resultData) {
                if (dto.getBatchNo() == null || dto.getWarehouseCode() == null) {
                    continue;
                }
                ReportSapGoodsmvt reportSapLog = new ReportSapGoodsmvt();
                String[] ignores = new String[]{"id","zuuid","status","uuidValue","mblnr","lgortSapnumAgo","lgortSapnumAfter","umlgoSapnumAgo","umlgoSapnumAfter","sendType"};
                BeanUtil.copyProperties(reportSap,reportSapLog,ignores);

                BigDecimal quantity = dto.getQuantity();
                reportSapLog.setCharg(dto.getBatchNo());  charg =  dto.getBatchNo();     // 批次号
                reportSapLog.setLgort(dto.getWarehouseCode()); lgort = dto.getWarehouseCode();   // 库存地点
                warehouseOutputBillDetailBo.setBatchNo(dto.getBatchNo());
                if(menge.compareTo(quantity) <= 0){
                    List<WarehouseOutputBillDetailBo> warehouseOutputBillDetailBoList = new ArrayList<>();
                    warehouseOutputBillDetailBo.setRequireCount(menge);
                    warehouseOutputBillDetailBo.setOutputCount(menge);
                    warehouseOutputBillDetailBoList.add(warehouseOutputBillDetailBo);
                    warehouseOutputBillBo.setOutputBillDetails(warehouseOutputBillDetailBoList);
                    log.info("氩气消耗：库存充足出库信息：" + JSON.toJSONString(warehouseOutputBillBo));
                    Result<String> stringResult = storeFeignClients.outputApply(warehouseOutputBillBo);
                    if (ResultCode.SUCCESS.getCode() != stringResult.getCode()) {
                        throw new RuntimeException("SAP产出上报失败，异常信息为：" + stringResult.getMessage());
                    }
                    reportSapLog.setMenge(String.valueOf(menge));
                    log.info("氩气消耗上报sap信息："  + JSON.toJSONString(reportSapLog));
                    reportSapGoodsmvtService.saveOrUpdate(reportSapLog);
                    menge = menge.subtract(menge);
                    break;
                } else {
                    List<WarehouseOutputBillDetailBo> warehouseOutputBillDetailBoList = new ArrayList<>();
                    warehouseOutputBillDetailBo.setRequireCount(quantity);
                    warehouseOutputBillDetailBo.setOutputCount(quantity);
                    warehouseOutputBillDetailBoList.add(warehouseOutputBillDetailBo);
                    warehouseOutputBillBo.setOutputBillDetails(warehouseOutputBillDetailBoList);
                    log.info("氩气消耗：库存不充足出库信息：" + JSON.toJSONString(warehouseOutputBillBo));
                    Result<String> stringResult = storeFeignClients.outputApply(warehouseOutputBillBo);
                    if (ResultCode.SUCCESS.getCode() != stringResult.getCode()) {
                        throw new RuntimeException("SAP产出上报失败，异常信息为：" + stringResult.getMessage());
                    }
                    log.info("氩气消耗上报sap信息："  + JSON.toJSONString(reportSapLog));
                    reportSapLog.setMenge(String.valueOf(quantity));
                    reportSapGoodsmvtService.saveOrUpdate(reportSapLog);
                    menge = menge.subtract(quantity);
                }
            }
            if(menge.compareTo(new BigDecimal(0)) > 0){

                ReportSapGoodsmvt reportSapLog = new ReportSapGoodsmvt();
                String[] ignores = new String[]{"id","zuuid","status","uuidValue","mblnr","lgortSapnumAgo","lgortSapnumAfter","umlgoSapnumAgo","umlgoSapnumAfter","sendType"};
                BeanUtil.copyProperties(reportSap,reportSapLog,ignores);
                reportSapLog.setCharg(charg);        // 批次号
                reportSapLog.setLgort(lgort);
                reportSapLog.setMenge(String.valueOf(menge));
                reportSapGoodsmvtService.saveOrUpdate(reportSapLog);

                SysLog syslog = new SysLog();
                syslog.setCreateTime(new Date());
                syslog.setType("info");
                syslog.setCreateUserName("SYSTEM");
                syslog.setContent(JSON.toJSONString(reportSapLog));
                syslog.setResult("氩气消耗上报未上报数量：炉次【" + lotNo + "】库存不同步导致多上报数量"+menge);
                syslog.setMethodName("reportArgonToSap()");
                syslog.setServerAddr("");
                syslog.setReqType("HTTP");
                syslog.setReqIp(IpUtils.getLocalIP());
                sysLogService.save(syslog);
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
            log.info("氩气消耗上报错误：" + e.getMessage());
        }

    }


    /**
     * 采集的引晶剩料重量，并且小于900，并且指定课次为null，并且当前颗次小于2，才将当前颗次改为2
     *
     * @param sendEapSignalDto
     * @param planProcessingOrder
     */
    private void execCrystalNumRule(SendEapSignalDto sendEapSignalDto, PlanProcessingOrder planProcessingOrder) {
        if (planProcessingOrder.getCrystalNum() < 2 && sendEapSignalDto.getCrystalInfoDto().getCrystalIntroductionResidualWeight() != null
                && sendEapSignalDto.getCrystalInfoDto().getCrystalNum() == null) {
            BigDecimal crystalIntroductionResidualWeight = new BigDecimal("900");
            Boolean isExists = null;
            try {
                isExists = redisUtils.exists("CrystalNumRule");
            } catch (Exception ignored) {
            }
            if (isExists != null && isExists) {
                Object crystalNumRule = redisUtils.get("CrystalNumRule");
                crystalIntroductionResidualWeight = NumberUtil.Object2BigDecimal(crystalNumRule, crystalIntroductionResidualWeight);
            } else {
                DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable("CrystalNumRule", "引晶剩料重量", true);
                crystalIntroductionResidualWeight = NumberUtil.Object2BigDecimal(dictionaryItem.getValue(), crystalIntroductionResidualWeight);
                if (isExists != null) {
                    redisUtils.set("CrystalNumRule", crystalIntroductionResidualWeight, 10L, TimeUnit.MINUTES);
                }
            }
            if (sendEapSignalDto.getCrystalInfoDto().getCrystalIntroductionResidualWeight().compareTo(crystalIntroductionResidualWeight) < 0) {
                planProcessingOrder.setCrystalNum(2);
            }
        }
    }

    /**
     * 处理取段结晶信号，取段稳温信号
     *
     * @param sendEapSignalDto
     * @param equipment
     * @param productionSchedule
     * @return
     */
    private Result handleQujj(SendEapSignalDto sendEapSignalDto, Equipment equipment, ProductionSchedule productionSchedule, Boolean isFt, int source) {

        // PC补信号：复投次数录入是否正确
        AgainTask againTask = againTaskService.findCompletedTopByLotNo(productionSchedule.getLotNo());
        if (GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_YB_CHECK, "QDJJCheckAgainNum", false)
                && !ObjectUtils.isEmpty(sendEapSignalDto.getCrystalInfoDto().getAgainNum())
                && EnumValuesEap.SignalSource.PC.getKey() == source) {

            if (ObjectUtils.isEmpty(againTask)) {
                return ResultGenerator.fail("未查询到炉次【" + productionSchedule.getLotNo() + "】存在完成状态的复投任务");
            }
            if (againTask.getAgainNum().compareTo(sendEapSignalDto.getCrystalInfoDto().getAgainNum()) != 0) {
                return ResultGenerator.fail("最近一次的加料次数为" + againTask.getAgainNum() + "，请确认产出的加料次数是否符合产出平衡逻辑！");
            }
        }

        // 设置输入的复投次数和颗次
        PlanProcessingOrder planProcessingOrder = planProcessingOrderService.get(productionSchedule.getLotId());
        if (sendEapSignalDto.getCrystalInfoDto().getAgainNum() != null) {
            planProcessingOrder.setAgainNum(sendEapSignalDto.getCrystalInfoDto().getAgainNum());
        }
        if (sendEapSignalDto.getCrystalInfoDto().getCrystalNum() != null) {
            planProcessingOrder.setCrystalNum(sendEapSignalDto.getCrystalInfoDto().getCrystalNum());
        }

        // 本次投料状态卡控：PC + EAP
        if (GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_YB_CHECK, "checkInputMaterial", false)) {
            Result result = null;
            if (EnumValuesEap.SignalSource.PC.getKey() == source){
                result = checkInputMaterial(productionSchedule.getLotNo(), planProcessingOrder.getAgainNum());
            }else if (GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_YB_CHECK, "checkInputMaterialForEap", false)){
                result = checkInputMaterial(productionSchedule.getLotNo(), planProcessingOrder.getAgainNum());
            }
            if (!ObjectUtils.isEmpty(result) && ResultCode.SUCCESS.getCode() != result.getCode()){
                return result;
            }
        }

        // PC补信号：检查上次复投产出状态
        if (GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_YB_CHECK, "QDJJCheckLastTimeOutPut", false)
                && planProcessingOrder.getAgainNum() >= 2
                && EnumValuesEap.SignalSource.PC.getKey() == source
                && againTask.getOutputCrystal() != null && againTask.getOutputCrystal()) {

            // 获取上次投料产出
            Integer lastAgainNum = planProcessingOrder.getAgainNum() - 1;
            List<LongCrystal> LastCrystalList = longCrystalService.findByLotNoAndAgainNum(productionSchedule.getLotNo(), lastAgainNum);
            if (CollectionUtils.isEmpty(LastCrystalList))
                return ResultGenerator.fail("上次（第" + lastAgainNum + "次）加料没有晶棒产出，请确认！");
            boolean isConfirmAll = true;
            for (LongCrystal crystal : LastCrystalList) {
                if (crystal.getCrystalStatus() == EnumValuesFactory.CrystalStatus.待确认.getKey()) {
                    isConfirmAll = false;
                    break;
                }
            }
            if (!isConfirmAll)
                return ResultGenerator.fail("上次（第" + lastAgainNum + "次）加料产出的晶棒有未确认状态，请先确认产出！");
        }

        // PC补信号：检查需要创建的复投任务是否重复
        if (isFt && GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_YB_CHECK, "checkOnlyFtTask", false)) {
            // 获取下次复投任务
            Integer nextAgainNum = planProcessingOrder.getAgainNum() + 1;
            List<AgainTask> nextTaskList = againTaskService.findNormalTasks(productionSchedule.getLotNo(), nextAgainNum);
            if (!CollectionUtils.isEmpty(nextTaskList)) {
                return ResultGenerator.fail("下次（第" + nextAgainNum + "次）已有正在进行的复投任务，不能重复创建！");
            }
        }

        // 完结投料不许继续报料
        if (isFt && GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_YB_CHECK, "checkFillEndTask", false)
            && 1 == againTask.getIsFillEnd()) {
            return ResultGenerator.fail("本次为完结投料，不能继续报料！");
        }

        boolean isOutput = false;
        LongCrystal longCrystal = null;

        //获取当前产出单晶对应订单的规格，再根据规格查询最小长度的长晶棒
        double lengthControl;
        DictionaryItem defaultItem = dictionaryItemService.findByThemeValueAndNameAndEnable("longCrystalLengthControl", "default", true);
        String defaultValue = Optional.ofNullable(defaultItem).map(DictionaryItem::getValue).orElse("1");

        String spec = Optional.of(productionSchedule).map(ProductionSchedule::getProductionOrder).map(ProductionOrder::getProductModel).map(ProductModel::getSpecs).orElse("-1");
        if ("-1".equals(spec)) {
            lengthControl = Convert.toDouble(defaultValue);
        } else {
            DictionaryItem item = dictionaryItemService.findByThemeValueAndNameAndEnable("longCrystalLengthControl", spec, true);
            String value = Optional.ofNullable(item).map(DictionaryItem::getValue).orElse(defaultValue);
            lengthControl = Convert.toDouble(value, 1d);
        }

        // 默认需要产出
        if (ObjectUtils.isEmpty(sendEapSignalDto.getOutputCrystal())) {
            sendEapSignalDto.setOutputCrystal(true);
        }
        if (sendEapSignalDto.getCrystalInfoDto() != null && sendEapSignalDto.getCrystalInfoDto().getLength() != null
                && sendEapSignalDto.getCrystalInfoDto().getLength().doubleValue() >= lengthControl
                && sendEapSignalDto.getOutputCrystal()) {
            isOutput = true;
            // 6期遗留问题
            //execCrystalNumRule(sendEapSignalDto, planProcessingOrder);
            longCrystal = saveLongCrystal(sendEapSignalDto, equipment, productionSchedule, planProcessingOrder, false);
        }

        // 复投次数及颗次自增
        if (isFt) {
            //复投次数加1
            planProcessingOrder.setAgainNum(planProcessingOrder.getAgainNum() + 1);
            //取段结晶，重置当前颗次 1
            planProcessingOrder.setCrystalNum(1);
            planProcessingOrder.setOutputMode(0);
        } else {
            //取段稳温，重置当前颗次 1
            planProcessingOrder.setCrystalNum(planProcessingOrder.getCrystalNum() + 1);
            planProcessingOrder.setOutputMode(1);
        }
        planProcessingOrderService.saveOrUpdate(planProcessingOrder);

        // 修改生产排程说明信息
        productionSchedule.setStatusInfo("接收到取段结晶信号，发送复投任务" + (!isOutput ? "，当前产出长度为：" + sendEapSignalDto.getCrystalInfoDto().getLength() + "，不生成产出记录" : ""));
        productionScheduleService.saveOrUpdate(productionSchedule);

        // 获取未开始的新排程
        ProductionSchedule productionSchedule1 = productionScheduleService.findTopByDeviceIdAndStatusOrderByIdDesc(
                equipment.getId(), EnumValuesServer.ProductionScheduleStatus.未开始.getKey());

        // 炉内切换订单开关
        if (GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_YB_CHECK, "witchOrder", false)
            && !ObjectUtils.isEmpty(productionSchedule1) && productionSchedule1.getWitchOrder()) {

            // 获取最新设备加工能力
            MachineCapacity machineCapacity = machineCapacityService.findByEquipmentId(equipment.getId());
            ProductionOrder order1 = productionSchedule1.getProductionOrder();
            // 当前设备有新排程，
            // 且新排程与当前生产排程订单不一致
            // 且设备加工能力修改后下一颗次生效
            // 且新排程订单加工能力与设备加工能力一致
            if (order1 != null
                    && order1.getId().intValue() != productionSchedule.getProductionOrder().getId()
                    && StringUtils.isEmpty(machineCapacity.getCapacityInfoNext())
                    && !ObjectUtils.isEmpty(order1.getMachineCapacityId())
                    && order1.getMachineCapacityId().equals(machineCapacity.getCapacityInfo().getId())) {
                //处理炉次任务，改成完成
                productionSchedule.setStatus(EnumValuesServer.ProductionScheduleStatus.已完成.getKey());
                productionSchedule.setStatusInfo("加工能力改变，当前任务结束");
                productionSchedule.setCloseTime(new Date());
                productionScheduleService.saveOrUpdate(productionSchedule);

                //加工能改变，按照新的加工能力生产
                productionSchedule1.setLotNo(productionSchedule.getLotNo());
                productionSchedule1.setLotId(productionSchedule.getLotId());
                productionSchedule1.setStatusInfo("匹配到新的加工能力，任务开始");
                productionSchedule1.setStatus(EnumValuesServer.ProductionScheduleStatus.进行中.getKey());
                //开始新的任务
                productionScheduleService.saveOrUpdate(productionSchedule1);
            }else {
                productionSchedule1 = null;
            }
        }else {
            productionSchedule1 = null;
        }

        //处理完所有业务，发送取晶体通知
        if (longCrystal != null) {
            sendGetCrystalNotification(longCrystal);
        }

        log.info("晶棒产出记录：来源(" + EnumValuesEap.SignalSource.getText(source) + "),晶棒信息(" + JSON.toJSONString(longCrystal) + ")");

        if (isFt) {
            //剩料重量
            BigDecimal residualWeight = sendEapSignalDto.getCrystalInfoDto().getResidualWeight();
            //总装埚料
            BigDecimal totalWeight = sendEapSignalDto.getCrystalInfoDto().getTotalWeight();

            // 发送复投任务
            if (productionSchedule1 == null) {
                return ftTaskCreate(productionSchedule, equipment, 2, residualWeight, totalWeight, planProcessingOrder.getAgainNum(),
                        sendEapSignalDto.getCrystalInfoDto().getIsDone(), sendEapSignalDto.getOutputCrystal());
            } else {
                return ftTaskCreate(productionSchedule1, equipment, 2, residualWeight, totalWeight, planProcessingOrder.getAgainNum(),
                        sendEapSignalDto.getCrystalInfoDto().getIsDone(), sendEapSignalDto.getOutputCrystal());
            }
        }
        return ResultGenerator.success();
    }

    // 检查投料信息
    private Result checkInputMaterial(String lotNo, Integer againNum){
        if (againNum == 1) {
            LoadingTask task = loadingTaskService.findSuccessByLotNo(lotNo);
            if (ObjectUtils.isEmpty(task)){
                return ResultGenerator.fail("当前炉次" + lotNo + "的配料任务未完成，请检查！");
            }
        }
        List<AgainTask> againTaskList = againTaskService.findSuccessByLotNoAndAgainNum(lotNo, againNum);
        if (CollectionUtils.isEmpty(againTaskList)){
            return ResultGenerator.fail("加料次数" + againNum + "的灌料任务未完成，请检查！");
        }
        // 是否存在未确认的投料
        List<InputFurnaceBurden> taskList1 = inputFurnaceBurdenService.findTask(lotNo, againNum, 0);
        if (!CollectionUtils.isEmpty(taskList1)){
            return ResultGenerator.fail("加料次数" + againNum + "存在未确认的加料信息，请先在IPC中确认投料！");
        }
        // 是否存在未加料的情况
        List<InputFurnaceBurden> taskList2 = inputFurnaceBurdenService.findTask(lotNo, againNum, 1);
        if (CollectionUtils.isEmpty(taskList2)){
            return ResultGenerator.fail("加料次数" + againNum + "没有确认完成的加料信息，请确认是否完成配料或灌料！");
        }

        return ResultGenerator.success();
    }

    //处理完所有业务，发送取晶体通知
    private void sendGetCrystalNotification(LongCrystal longCrystal) {
        try {
            //TODO 李儒添加代码
            if (longCrystal.getLength().floatValue() >= 1500) {
                //发送MQTT通知
                SongYanMessage message = new SongYanMessage();
                message.setArea(longCrystal.getArea());
                message.setEquipmentCode(longCrystal.getEquipmentCode());
                message.setId(longCrystal.getId());
                message.setLotNo(longCrystal.getLotNo());
                message.setSeries(longCrystal.getSeries());
                notificationService.sendNotification(message);
            }
        } catch (Exception e) {
        }
    }


    /**
     * 发送复投任务
     *
     * @param productionSchedule
     * @param equipment
     * @param rawType
     * @return
     */
    private Result ftTaskCreate(ProductionSchedule productionSchedule, Equipment equipment, Integer rawType, BigDecimal residualWeight, BigDecimal totalWeight, Integer againNum, Integer isDone, boolean outputCrystal) {
        try {
            AgainTaskDto againTaskDto = new AgainTaskDto();
            againTaskDto.setSeries(equipment.getSeries());
            againTaskDto.setArea(equipment.getArea());
            againTaskDto.setRawType(rawType);
            againTaskDto.setSapNo(productionSchedule.getProductionOrder().getAufnr());
            againTaskDto.setFurnaceType(productionSchedule.getFurnaceType());
            againTaskDto.setEquipmentCode(productionSchedule.getDeviceNo());
            againTaskDto.setLotNo(productionSchedule.getLotNo());
            againTaskDto.setProductCode(productionSchedule.getProductModel().getCode());
            againTaskDto.setProductGrade(productionSchedule.getProductModel().getProductGrade());
            againTaskDto.setSpecs(productionSchedule.getCapacityInfo().getSpecification());
            againTaskDto.setDopant(productionSchedule.getCapacityInfo().getDopant());
            againTaskDto.setProductType(productionSchedule.getProductType());
            againTaskDto.setSurplusWeigth(residualWeight);//剩料重量
            againTaskDto.setTotalUseWeigth(totalWeight);//总装锅料
            againTaskDto.setAgainNum(againNum);
            againTaskDto.setIsDone(isDone);
            againTaskDto.setOutputCrystal(outputCrystal);
            eapDockingService.ftTaskCreate(againTaskDto);
            return ResultGenerator.success();
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResultGenerator.fail("复投任务发送失败：" + e.getMessage());
        }
    }

    /**
     * 保存单晶信息
     *
     * @param sendEapSignalDto
     * @param equipment
     * @param productionSchedule
     * @param planProcessingOrder
     */
    private LongCrystal saveLongCrystal(SendEapSignalDto sendEapSignalDto, Equipment equipment, ProductionSchedule productionSchedule, PlanProcessingOrder planProcessingOrder, boolean isStop) {
        LongCrystal longCrystal = new LongCrystal();
        if (sendEapSignalDto.getCrystalInfoDto() != null) {
            longCrystal.setWeight(sendEapSignalDto.getCrystalInfoDto().getWeight());
            longCrystal.setLength(sendEapSignalDto.getCrystalInfoDto().getLength());
            longCrystal.setWindUp(sendEapSignalDto.getCrystalInfoDto().getWindUp());
            //添加完结方式
            if (StringUtils.isEmpty(sendEapSignalDto.getCrystalInfoDto().getClosureIdentification())) {
                longCrystal.setClosureIdentification("无");
            } else {
                longCrystal.setClosureIdentification(sendEapSignalDto.getCrystalInfoDto().getClosureIdentification());
            }
            //处理 产出时间 ：完结尾的长晶棒产出时间特殊处理，增加9个小时
            if (null != sendEapSignalDto.getCrystalInfoDto().getClosureIdentification() &&
                    !StringUtils.isEmpty(sendEapSignalDto.getCrystalInfoDto().getClosureIdentification()) &&
                    sendEapSignalDto.getCrystalInfoDto().getClosureIdentification().equals("W")) {
                Date newDate = new Date();
                newDate.setTime(newDate.getTime() + 145 * 6 * 60 * 1000);
                longCrystal.setGmtCreateAdd(newDate);
                longCrystal.setGmtCreate(new Date());

            } else {
                longCrystal.setGmtCreate(new Date());
                longCrystal.setGmtCreateAdd(new Date());
            }
        } else {
            longCrystal.setGmtCreate(new Date());
        }
        longCrystal.setEquipmentCode(sendEapSignalDto.getEquipmentCode());
        longCrystal.setSeries(equipment.getSeries());
        longCrystal.setArea(equipment.getArea());
        longCrystal.setWindUp((sendEapSignalDto.getCrystalInfoDto() == null
                || sendEapSignalDto.getCrystalInfoDto().getWindUp().length() == 0
                || !"W".equals(sendEapSignalDto.getCrystalInfoDto().getWindUp())) ? "N" : sendEapSignalDto.getCrystalInfoDto().getWindUp());
        longCrystal.setAgainNum(planProcessingOrder.getAgainNum());
        longCrystal.setLotTimes(planProcessingOrder.getLotTimes());
        longCrystal.setCrystalNum(planProcessingOrder.getCrystalNum());
        //生成单晶编号
        String crystalNo = "X";
        try {
            crystalNo = generateCrystalNo(productionSchedule, planProcessingOrder, sendEapSignalDto.getCrystalInfoDto().getWindUp(), longCrystal);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("晶棒产出时生成晶棒编号异常：" + e.getMessage());
        }
        longCrystal.setCrystalNo(crystalNo);
        // 生成单晶批次号
        String batchNo = "X";
        try {
            batchNo = generateBatchNo(productionSchedule);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("晶棒产出时生成晶棒批次号异常：" + e.getMessage());
        }
        longCrystal.setBatchNo(batchNo);

        // 删除追溯 - WXL标记
        /*String fromMark = longCrystal.getCrystalNo().substring(SectionIndexRangeEnum.基地追溯产品类型.getBeginIndex(), SectionIndexRangeEnum.基地追溯产品类型.getEndIndex());
        if (fromMark.matches("[A-Z]+")) {
            longCrystal.setDeliveryNo(longCrystal.getCrystalNo().substring(SectionIndexRangeEnum.发料编号.getBeginIndex(), SectionIndexRangeEnum.发料编号.getEndIndex()));
        }*/

        longCrystal.setLotNo(productionSchedule.getLotNo());
        longCrystal.setLotId(planProcessingOrder.getId());
        longCrystal.setCrystalStatus(EnumValuesFactory.CrystalStatus.待确认.getKey());
        longCrystal.setFurnaceType(productionSchedule.getFurnaceType());
        longCrystal.setProductCode(productionSchedule.getProductModel().getCode());
        longCrystal.setProductName(productionSchedule.getProductModel().getName());
        longCrystal.setProductGrade(productionSchedule.getProductModel().getProductGrade());
        longCrystal.setSpecs(productionSchedule.getCapacityInfo().getSpecification());
        longCrystal.setDopant(productionSchedule.getCapacityInfo().getDopant());
        longCrystal.setSimpleName(productionSchedule.getCapacityInfo().getSimpleName());
        longCrystal.setProductType(productionSchedule.getCapacityInfo().getProductType());
        longCrystal.setConductiveType(productionSchedule.getCapacityInfo().getConductionType());
        longCrystal.setAgainNum(planProcessingOrder.getAgainNum());
        longCrystal.setCrystalNum(planProcessingOrder.getCrystalNum());
        //longCrystal.setGmtCreate(new Date());
        longCrystal.setUserCreate(BaseContextHandler.getUsername());
        //长晶棒产出-订单编号在划线时挂上，但需提前暴露个订单id供长棒检验时使用
        ProductionOrder oldProductionOrder = productionSchedule.getProductionOrder();
        longCrystal.setOrderId(oldProductionOrder.getId());
        longCrystal.setOrderNo(oldProductionOrder.getAufnr());
        longCrystal.setOrderType(oldProductionOrder.getAuart());
        longCrystal.setBaseMark(oldProductionOrder.getBaseMark());
        longCrystal.setWorkShop(oldProductionOrder.getWorkShop());
        longCrystal.setScheduleId(productionSchedule.getId());
        //继承当前炉次
        longCrystal.setCrystalErrorLevelOne(planProcessingOrder.getLotErrorLevelOne());
        longCrystal.setCrystalErrorLevelTwo(planProcessingOrder.getLotErrorLevelTwo());
        longCrystal.setHeadLen(sendEapSignalDto.getCrystalInfoDto().getHeadLen());
        longCrystal.setTailLen(sendEapSignalDto.getCrystalInfoDto().getTailLen());
        longCrystal.setIsDone(sendEapSignalDto.getCrystalInfoDto().getIsDone());
        longCrystal.setMaxDiam(sendEapSignalDto.getCrystalInfoDto().getMaxDiam());
        longCrystal.setMinDiam(sendEapSignalDto.getCrystalInfoDto().getMinDiam());
        longCrystal.setIsometricLength(sendEapSignalDto.getCrystalInfoDto().getIsometricLength());
        longCrystal.setCrystalCrucibleRotation(sendEapSignalDto.getCrystalInfoDto().getCrystalCrucibleRotation());

        longCrystal.setFromMark(oldProductionOrder.getFromMark());
        String productType = productionSchedule.getProductType();
        if (productType != null && productType.length() > 1) {
            longCrystal.setProductCategory(productType.substring(productType.length() - 1));
        }
        // 满锅重量
        setFullPotWeight(longCrystal);

        if (isStop) {
            wipStorageService.outStation2(planProcessingOrder.getCode(), new ArrayList<>(), isStop, new HashMap<>());
        }

        /*Date rodProductionTime = sendEapSignalDto.getCrystalInfoDto().getRodProductionTime();
        if(null != rodProductionTime){
            //进入抽空时间
            getRodProductionTime(rodProductionTime, longCrystal, SendDeviceEapSignalDto.ENTRY_EVACUATION_TIME);
        } else {
            //进入取熔料时间
            Date meltingTime = sendEapSignalDto.getCrystalInfoDto().getMeltingTime();
            getRodProductionTime(meltingTime, longCrystal, SendDeviceEapSignalDto.MELTING_TIME);
        }*/

        longCrystalService.saveOrUpdate(longCrystal);
        return longCrystal;
    }

    // 设置晶棒的满锅重量
    public void setFullPotWeight(LongCrystal longCrystal){
        AgainTask againTask = againTaskService.findCompletedByLotNoAndAgainNum(longCrystal.getLotNo(), longCrystal.getAgainNum());
        if (ObjectUtils.isEmpty(againTask) || ObjectUtils.isEmpty(againTask.getWeigthId())) return;

        FormulaWeightScheme weightScheme = formulaWeightSchemeService.get(againTask.getWeigthId());
        if (ObjectUtils.isEmpty(weightScheme)) return;

        // 设置满锅重量
        if (longCrystal.getAgainNum() == 1 && !ObjectUtils.isEmpty(weightScheme.getInitFullPotWeight())){
            longCrystal.setFullPotWeight(weightScheme.getInitFullPotWeight().setScale(0, RoundingMode.UP));

        } else if (longCrystal.getAgainNum() >= 2 && !CollectionUtils.isEmpty(weightScheme.getAgainFullPotWeight())
                && againTask.getIsFillEnd() == 0
                && !ObjectUtils.isEmpty(weightScheme.getAgainFullPotWeight().get(longCrystal.getAgainNum().toString()))){
            longCrystal.setFullPotWeight(weightScheme.getAgainFullPotWeight().get(longCrystal.getAgainNum().toString()).setScale(0, RoundingMode.UP));

        }else if (longCrystal.getAgainNum() >= 2 && !CollectionUtils.isEmpty(weightScheme.getAgainFullPotWeight())
                && againTask.getIsFillEnd() == 1
                && !ObjectUtils.isEmpty(weightScheme.getAgainFullPotWeight().get("99"))){
            longCrystal.setFullPotWeight(weightScheme.getAgainFullPotWeight().get("99").setScale(0, RoundingMode.UP));

        }else {
            longCrystal.setFullPotWeight(weightScheme.getFullPotWeight().setScale(0, RoundingMode.UP));
        }

    }

    public void getRodProductionTime(Date rodProductionTime, LongCrystal longCrystal, String type) {
        //todo 棒次时间    抽空时间不为空则是首棒
        //判断是否为首棒
        LongCrystal lastCrystalNo = longCrystalService.getLastCrystalNo(longCrystal.getEquipmentCode());
        Date date = new Date();
        if (null != lastCrystalNo && !longCrystal.getLotNo().equals(lastCrystalNo.getLotNo())) {
            Date gmtCreate = lastCrystalNo.getGmtCreate();
            //首棒 如果抽空时间-上一炉停炉时间《=18小时（数据字典），那么时间为产出时间-上一炉完结尾晶棒的产出时间，
            // 大于18小时=产出时间-上一炉停炉时间+14.5
            //或
            //首棒 如果取熔料时间+1.5-上一炉停炉时间《=18小时（数据字典），那么时间为产出时间-上一炉完结尾晶棒的产出时间，
            // 大于18小时=产出时间-上一炉停炉时间+14.5
            Date stopTime = null;
            CrystalPullingInfoLog crystalPullingInfoLogByDeviceNo = crystalPullingInfoLogService.getCrystalPullingInfoLogByDeviceNo(longCrystal.getEquipmentCode());
            if (null != crystalPullingInfoLogByDeviceNo) {
                stopTime = crystalPullingInfoLogByDeviceNo.getStopTime();
            }
            List<DictionaryItem> rodProductionTime1 = dictionaryItemService.findByThemeValueAndEnable("rodProductionTime", true);
            DictionaryItem dictionaryItem = rodProductionTime1.get(0);
            Double aDouble = Double.valueOf(dictionaryItem.getValue());
            if (null != rodProductionTime && null != stopTime) {
                if (type.equals(SendDeviceEapSignalDto.ENTRY_EVACUATION_TIME)) {
                    if ((rodProductionTime.getTime() - stopTime.getTime()) / 1000 / 60 / 60 <= aDouble) {
                        longCrystal.setRodProductionTime(new BigDecimal(date.getTime() - lastCrystalNo.getGmtCreate().getTime()).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP));
                    } else {
                        longCrystal.setRodProductionTime(new BigDecimal(date.getTime() - rodProductionTime.getTime()).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP).add(new BigDecimal(14.5)));
                    }
                } else if (type.equals(SendDeviceEapSignalDto.MELTING_TIME)) {
                    if ((rodProductionTime.getTime() - 5400000 - stopTime.getTime()) / 1000 / 60 / 60 <= aDouble) {
                        longCrystal.setRodProductionTime(new BigDecimal(date.getTime() - lastCrystalNo.getGmtCreate().getTime()).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP));
                    } else {
                        longCrystal.setRodProductionTime(new BigDecimal(date.getTime() - rodProductionTime.getTime() + 5400000).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP).add(new BigDecimal(14.5)));
                    }
                }
            } else {
                longCrystal.setRodProductionTime(new BigDecimal(date.getTime() - gmtCreate.getTime()).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP));
            }
        } else {
            //非首棒产出时间-上一棒产出时间
            if (null != lastCrystalNo) {
                Date gmtCreate = lastCrystalNo.getGmtCreate();
                longCrystal.setRodProductionTime(new BigDecimal(date.getTime() - gmtCreate.getTime()).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP));
            }
        }
        //新装炉的首颗
        if (type.equals(SendDeviceEapSignalDto.ENTRY_EVACUATION_TIME)) {
            if (null == lastCrystalNo && null != rodProductionTime) {
                longCrystal.setRodProductionTime(new BigDecimal(date.getTime() - rodProductionTime.getTime()).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP));
            }
        } else if (type.equals(SendDeviceEapSignalDto.MELTING_TIME)) {
            if (null == lastCrystalNo && null != rodProductionTime) {
                longCrystal.setRodProductionTime(new BigDecimal(date.getTime() - rodProductionTime.getTime() + 5400000).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP));
            }
        }
    }

    /**
     * 确认单晶编号
     *
     * @param longCrystal 单晶信息
     * @param outputMode  单晶产出方式
     */
    @Override
    public void generateCrystalNo(LongCrystal longCrystal, Integer outputMode, String windUp) {
        if (longCrystal == null) {
            throw new BusinessException("长晶棒对象信息为空");
        }
        PlanProcessingOrder planProcessingOrder = planProcessingOrderService.get(longCrystal.getLotId());
        if (planProcessingOrder == null) {
            throw new BusinessException("该单晶的炉次工单不存在");
        }
        ProductionSchedule productionSchedule = productionScheduleService.get(longCrystal.getScheduleId());
        if (productionSchedule == null) {
            throw new BusinessException("该单晶的生产任务不存在");
        }
        Equipment equipment = equipmentService.get(productionSchedule.getDeviceId());
        LongCrystal longCrystal1 = longCrystalService.findByLotIdAndAgainNumAndCrystalNumAndCrystalStatusNotAndIdNot(longCrystal.getLotId(), longCrystal.getAgainNum(), longCrystal.getCrystalNum(), EnumValuesFactory.CrystalStatus.待确认.getKey(), longCrystal.getId());
        if (longCrystal1 != null) {
            throw new BusinessException("该【" + longCrystal.getLotNo() + "】已存在复投次数【" + longCrystal.getAgainNum() + "】" + "颗次【" + longCrystal.getCrystalNum() + "】的单晶");
        }
        // 生成新的晶棒编号
        String crystalNo = generateCrystalNo(productionSchedule, planProcessingOrder, windUp, longCrystal);

        // 检查新晶棒编号是否存在
        List<LongCrystal> checkExists = longCrystalService.findByCrystalNoAndNotId(crystalNo, longCrystal.getId());
        if (!CollectionUtils.isEmpty(checkExists)) {
            throw new BusinessException("新晶棒编号【" + crystalNo + "】已存在，请检查是否存在异常数据！");
        }

        longCrystal.setCrystalNo(crystalNo);
        longCrystal.setBatchNo(generateBatchNo(productionSchedule));
        //修改工单编号
        PlanProcessingOrder planProcessingOrderLongCrystal = planProcessingOrderService.findByGoodsIdAndOrderType(longCrystal.getId(), EnumValuesFactory.PlanProcessingOrderOrderType.单晶.getKey());
        if (planProcessingOrderLongCrystal != null) {
            planProcessingOrderLongCrystal.setCode(crystalNo);
            planProcessingOrderService.createPlanProcessingOrder(planProcessingOrderLongCrystal, null, null);
            //修改在制品
            wipStorageService.updatePlanProcessingOrderCode(planProcessingOrderLongCrystal.getId(), crystalNo);
        } else {
            List<ProcessingOrderNewDto> processingOrderNewDtos = new ArrayList<>();
            ProcessingOrderNewDto processingOrderNewDto = new ProcessingOrderNewDto();
            processingOrderNewDto.setCode(longCrystal.getCrystalNo());
            processingOrderNewDto.setLength(longCrystal.getLength());
            processingOrderNewDto.setWeight(longCrystal.getWeight() == null ? new BigDecimal("1") : longCrystal.getWeight());
            processingOrderNewDto.setGoodsId(longCrystal.getId());
            processingOrderNewDto.setOrderType(EnumValuesFactory.PlanProcessingOrderOrderType.单晶.getKey());
            processingOrderNewDtos.add(processingOrderNewDto);

            Result result = wipStorageService.outStation2(planProcessingOrder.getCode(), processingOrderNewDtos, false, new HashMap<>(), false);
            if (result.getCode() != ResultCode.SUCCESS.getCode()) {
                //手动回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                throw new RuntimeException(result.getMessage());
            }
        }
        //修改炉次工单的投次数，颗次计数
        LongCrystal longCrystalLotLast = longCrystalService.findTopByLotId(longCrystal.getLotId());
        //修改长晶棒编号
        longCrystalService.saveOrUpdate(longCrystal);
        if (longCrystalLotLast != null && longCrystal.getId().equals(longCrystalLotLast.getId())) {
            // 如果本次确认是取段结晶，则复投次数+1，颗次变为0
            if (outputMode == 0) {
                planProcessingOrder.setAgainNum(longCrystal.getAgainNum() + 1);
                planProcessingOrder.setCrystalNum(1);
            }
            // 如果本次确认是取段稳温，则复投次数不变，颗次+1
            else {
                planProcessingOrder.setAgainNum(longCrystal.getAgainNum());
                planProcessingOrder.setCrystalNum(longCrystal.getCrystalNum() + 1);
            }
            planProcessingOrderService.createPlanProcessingOrder(planProcessingOrder, null, null);
        }

    }

    //todo:长晶棒产出表 修改晶编
    @Override
    public String generateCrystalNo(String crystalNo, LongCrystalInfoDto dto) {
        // 复投次数
        crystalNo = replaceIndex((13 - 1), crystalNo, dto.getAgainNum().toString());

        // 颗次
        crystalNo = replaceIndex((14 - 1), crystalNo, dto.getCrystalNum().toString());

        // 收尾方式
        crystalNo = replaceIndex((15 - 1), crystalNo, ("W".equals(dto.getWindUp()) ? "W" : "N"));

        return crystalNo;
    }

    private static String replaceIndex(int index, String res, String str) {
        return res.substring(0, index) + str + res.substring(index + 1);
    }

    /**
     * 生成单晶编号
     * todo: 修改成云南晶编
     *
     * @param productionSchedule
     * @param planProcessingOrder
     * @param windUp
     * @param longCrystal
     * @return
     */
    /*private String generateCrystalNo(ProductionSchedule productionSchedule, PlanProcessingOrder planProcessingOrder, String windUp, LongCrystal longCrystal) {
        //todo:追溯改造
        //获取单晶生产订单(计划)对应追溯标识
        String fromMark = productionSchedule.getProductionOrder().getFromMark();
        String baseMark = productionSchedule.getProductionOrder().getBaseMark();
        String productType = productionSchedule.getProductionOrder().getProductType();

        StringBuilder crystalNo = new StringBuilder();
        if (StringUtils.isEmpty(fromMark)) {
            crystalNo.append("Y");
            crystalNo.append(productionSchedule.getProductType());
            crystalNo.append(productionSchedule.getLotNo());
            crystalNo.append("R");
            crystalNo.append(NumberUtil.getLTE10English(longCrystal.getAgainNum()));
            if (longCrystal.getCrystalNum() == 1) {
                if (("Y").equals(windUp) || ("W").equals(windUp)) {
                    crystalNo.append("0");
                } else {
                    crystalNo.append(NumberUtil.getLTE10English(longCrystal.getCrystalNum()));
                }
            } else {
                crystalNo.append(NumberUtil.getLTE10English(longCrystal.getCrystalNum()));
            }
            return crystalNo.toString();
        } else {
//            crystalNo.append(baseMark);
//            crystalNo.append(productType);
            crystalNo.append(baseMark);
            crystalNo.append(productionSchedule.getProductType());
            crystalNo.append(productionSchedule.getLotNo());
            crystalNo.append(NumberUtil.getLTE10English(longCrystal.getAgainNum()));
            if (longCrystal.getCrystalNum() == 1) {
                if (("Y").equals(windUp) || ("W").equals(windUp)) {
                    crystalNo.append("0");
                } else {
                    crystalNo.append(NumberUtil.getLTE10English(longCrystal.getCrystalNum()));
                }
            } else {
                crystalNo.append(NumberUtil.getLTE10English(longCrystal.getCrystalNum()));
            }
            return crystalNo.toString();
        }
    }*/

    /**
     * 太原晶科生成单晶批次号
     */
    @Jinko(value = "生成晶棒批次号", author = "wxl")
    private String generateBatchNo(ProductionSchedule schedule) {
        StringBuilder batchNo = new StringBuilder();
        // 1基地：太原
        batchNo.append("T");

        // 2定向：非定向-B，定向-C，供欧定向-D，等外-E
        ProductionOrder productionOrder = schedule.getProductionOrder();
        if (ObjectUtils.isEmpty(productionOrder)) {
            throw new RuntimeException("与排程关联的【订单数据】不存在");
        }
        if (StringUtils.isEmpty(productionOrder.getZorientationind())) {
            batchNo.append("B");
        }else {
            DictionaryItem directionalSign = dictionaryItemService.findByThemeValueAndNameAndEnable("creatCrystalNo",
                    productionOrder.getZorientationind(), true);
            if (directionalSign == null || StringUtils.isEmpty(directionalSign.getValue())) {
                throw new RuntimeException("【创建晶棒编号】字典中，未配置定向【" + productionOrder.getZorientationind() + "】的对应编号");
            }
            batchNo.append(directionalSign.getValue());
        }

        // 3掺杂类型: P型掺镓-U，N型掺磷-N
        batchNo.append(dictionaryItemService.findByThemeValueAndNameAndEnable("creatCrystalNo",
                schedule.getCapacityInfo().getDopant(), true).getValue());

        LocalDate now = LocalDate.now();
        // 4-5年
        batchNo.append(now.getYear() - 2000);
        // 6月
        batchNo.append(CodeMapUtils.NUM_CODE_MAP.get(now.getMonthValue()));
        // 7-8日
        int day = now.getDayOfMonth();
        batchNo.append((day <= 9) ? ("0" + day) : day);

        // 9车间
        // 10片区
        batchNo.append(schedule.getDeviceNo().substring(0, 2));

        return batchNo.toString();
    }

    /**
     * 太原晶科生成单晶编号
     */
    @Jinko(value = "生成晶棒编码", author = "wxl")
    private String generateCrystalNo(ProductionSchedule schedule, PlanProcessingOrder order, String windUp, LongCrystal longCrystal) {
        // 获取编码循环界限
        int loop = 35;     // 实际是1-9，A-Z总共35项，不包含0

        StringBuilder crystalNo = new StringBuilder();
        // 1基地：太原
        crystalNo.append("T");

        ProductModel productModel = schedule.getProductModel();
        if (productModel == null) throw new RuntimeException("与排程关联的【物料主数据】不存在");

        // 2定向：非定向-B，定向-C，供欧定向-D，等外-E
        ProductionOrder productionOrder = schedule.getProductionOrder();
        if (ObjectUtils.isEmpty(productionOrder)) {
            throw new RuntimeException("与排程关联的【订单数据】不存在");
        }
        if (StringUtils.isEmpty(productionOrder.getZorientationind())) {
            crystalNo.append("B");
        }else {
            DictionaryItem directionalSign = dictionaryItemService.findByThemeValueAndNameAndEnable("creatCrystalNo",
                    productionOrder.getZorientationind(), true);
            if (directionalSign == null || StringUtils.isEmpty(directionalSign.getValue())) {
                throw new RuntimeException("【创建晶棒编号】字典中，未配置定向【" + productionOrder.getZorientationind() + "】的对应编号");
            }
            crystalNo.append(directionalSign.getValue());
        }

        MachineCapacityInfo capacityInfo = schedule.getCapacityInfo();
        // 3掺杂类型: P型掺镓-U，N型掺磷-N
        DictionaryItem dopant = dictionaryItemService.findByThemeValueAndNameAndEnable("creatCrystalNo",
                capacityInfo.getDopant(), true);
        if (dopant == null) throw new RuntimeException("creatCrystalNo字典中【掺杂类型】不存在");
        crystalNo.append(dopant.getValue());

        // 4尺寸：10吋-X，11吋-F，12吋-K，矩形-R，提纯棒-T，黄金片-H，多晶：D
        if (longCrystal.getIsCrystal() != null && longCrystal.getIsCrystal() == 0) {     // 多晶
            crystalNo.append("D");
        } else {
            DictionaryItem specification = dictionaryItemService.findByThemeValueAndNameAndEnable("creatCrystalNo",
                    capacityInfo.getSpecification(), true);
            if (specification == null) throw new RuntimeException("creatCrystalNo字典中【尺寸】不存在");
            crystalNo.append(specification.getValue());
        }

        LocalDate now = LocalDate.now();
        // 5年
        // 6月
        // 7车间
        // 8片区
        // 9-11炉台号
        // 12炉数
        crystalNo.append(schedule.getLotNo());

        // 13加料次数
        int againNum = longCrystal.getAgainNum();
        crystalNo.append(CodeMapUtils.NUM_CODE_MAP.get((againNum == loop) ? loop : (againNum % loop)));

        // 14加料后产出根数
        int crystalNum = longCrystal.getCrystalNum();
        crystalNo.append(CodeMapUtils.NUM_CODE_MAP.get((crystalNum == loop) ? loop : (crystalNum % loop)));

        // 15是否完结
        crystalNo.append("W".equals(longCrystal.getWindUp()) ? "W" : "N");

        // 16高低阻
        NewProductModel newProductModel = newProductModelService.findByCode(productModel.getCode());
        if (newProductModel == null) throw new RuntimeException("产品模型不存在");
        DictionaryItem resistanceType = dictionaryItemService.findByThemeValueAndNameAndEnable("creatCrystalNo",
                newProductModel.getResistanceType(), true);
        if (resistanceType == null) throw new RuntimeException("creatCrystalNo字典中【高低阻】不存在");
        crystalNo.append(resistanceType.getValue());

        return crystalNo.toString();
    }

    /**
     * 发起装料任务
     *
     * @param equipment
     * @param productionSchedule
     */
    private Result handleStart(Equipment equipment, ProductionSchedule productionSchedule) {
        try {
            LoadingTaskDto loadingTaskDto = new LoadingTaskDto();
            loadingTaskDto.setScheduleId(productionSchedule.getId());
            loadingTaskDto.setEquipmentCode(productionSchedule.getDeviceNo());
            loadingTaskDto.setLotNo(productionSchedule.getLotNo());
            loadingTaskDto.setSeries(equipment.getSeries());
            loadingTaskDto.setArea(equipment.getArea());
            loadingTaskDto.setFurnaceType(productionSchedule.getFurnaceType());
            loadingTaskDto.setSapNo(productionSchedule.getProductionOrder().getAufnr());
            loadingTaskDto.setProductCode(productionSchedule.getProductModel().getCode());
            loadingTaskDto.setProductGrade(productionSchedule.getProductModel().getProductGrade());
            loadingTaskDto.setSpecs(productionSchedule.getCapacityInfo().getSpecification());
            loadingTaskDto.setDopant(productionSchedule.getCapacityInfo().getDopant());
            loadingTaskDto.setSimpleName(productionSchedule.getCapacityInfo().getSimpleName());
            loadingTaskDto.setProductType(productionSchedule.getProductType());
            eapDockingService.zlTaskCreate(loadingTaskDto);
            return ResultGenerator.success();
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            log.error("发起装料任务", e);
            return ResultGenerator.fail("发起装料任务失败：" + e.getMessage());
        }
    }


    /**
     * 停炉时清空上炉次数据
     * 用于eap上传异常停炉信息
     *
     * @param info
     */
    private void clearCrystalPullingInfoForEAP(CrystalPullingInfo info, String deviceCode) {
        try {

            if (info == null) {
                info = new CrystalPullingInfo();
                info.setDeviceNo(deviceCode);
            }

            info.setStopTime(new Date());


            CrystalPullingInfoLog crystalPullingInfoLog = new CrystalPullingInfoLog();
            crystalPullingInfoLog.setSeries(info.getSeries());
            crystalPullingInfoLog.setDistrict(info.getDistrict());
            crystalPullingInfoLog.setSpecs(info.getSpecs());
            crystalPullingInfoLog.setConductionType(info.getConductionType());
            crystalPullingInfoLog.setDeviceNo(info.getDeviceNo());
            crystalPullingInfoLog.setLotNo(info.getLotNo());
            crystalPullingInfoLog.setMaterialCount(info.getMaterialCount());
            crystalPullingInfoLog.setLineLength(info.getLineLength());
            crystalPullingInfoLog.setInfo(info.getInfo());

            List<DictionaryItem> items = dictionaryItemService.findByThemeValueAndEnable(EnumValuesServer.DictionaryTheme.划线重量折算系数.getKey(), true);

            Map<String, DictionaryItem> collect = items.stream().collect(Collectors.toMap(DictionaryItem::getName, DictionaryItem -> DictionaryItem));

            try {
                double lineLength = info.getLineLength().doubleValue();
                double xishu = Double.valueOf(collect.get(info.getSpecs()).getValue());

                crystalPullingInfoLog.setLineWeight(BigDecimal.valueOf(lineLength / xishu));
            } catch (Exception e) {
                log.error("计算划线重量发生错误" + e.getMessage());
            }

            if (crystalPullingInfoLog.getMaterialCount() == null || crystalPullingInfoLog.getMaterialCount().doubleValue() <= 0) {
                crystalPullingInfoLog.setLineCompletionRate(new BigDecimal(0));
            } else {
                try {
                    crystalPullingInfoLog.setLineCompletionRate(BigDecimal.valueOf(crystalPullingInfoLog.getLineWeight().doubleValue() / crystalPullingInfoLog.getMaterialCount().doubleValue()));
                } catch (Exception e) {
                    log.error("计算划线成晶率发生错误" + e.getMessage());
                }
            }

            crystalPullingInfoLog.setStopTime(info.getStopTime());
            crystalPullingInfoLog.setStartTime(info.getStartTime());

            // 运行时间  当前时间-确认投料时间
            List<LoadingTask> byLotNoV2 = loadingTaskService.findByLotNoV2("拉晶情况-正常停炉", crystalPullingInfoLog.getLotNo());
            if(!byLotNoV2.isEmpty() && null != byLotNoV2.get(0).getConfirmTime()){
                LoadingTask loadingTask = byLotNoV2.get(0);
                long runTimeLen = new Date().getTime() - loadingTask.getConfirmTime().getTime();
                double _runTime = runTimeLen / (1000 * 60 * 60d);
                BigDecimal runTime = new BigDecimal(_runTime).setScale(2, BigDecimal.ROUND_DOWN);
                crystalPullingInfoLog.setRunTime(runTime);
            }
//            if (info.getStopTime() != null && info.getStartTime() != null) {
//                long runTimeLen = info.getStopTime().getTime() - info.getStartTime().getTime();
//                double _runTime = runTimeLen / 1000 / 60 / 60d;
//                BigDecimal runTime = new BigDecimal(_runTime).setScale(2, BigDecimal.ROUND_DOWN);
//                crystalPullingInfoLog.setRunTime(runTime);
//            }


            //0714 新运行时间=当前炉次的停炉时间(当前时间 new date()) - 上一炉次停炉时间 - 异常停炉时间
            if (info.getStopTime() != null) {
                CrystalPullingInfoLog pullingInfoLogOld = crystalPullingInfoLogService.getCrystalPullingInfoLogByDeviceNo(info.getDeviceNo());//上一炉次的拉晶情况记录表
                if (null != pullingInfoLogOld && null != pullingInfoLogOld.getStopTime()) {
                    long runTimeLen1 = info.getStopTime().getTime() - pullingInfoLogOld.getStopTime().getTime();
                    double _runTime1 = runTimeLen1 / 1000 / 60 / 60d;
                    if (null == info.getStandbyTime()) {
                        BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                        crystalPullingInfoLog.setRunTimeActual(runTimeActual);
                    } else {
                        BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                        BigDecimal subtract = runTimeActual.subtract(info.getStandbyTime());
                        crystalPullingInfoLog.setRunTimeActual(subtract);
                    }
                } else {
                    //新开炉次情况，没有上一炉次信息，开始时间取开炉时间
                    if (null != info.getStartTime()) {
                        long runTimeLen1 = info.getStopTime().getTime() - info.getStartTime().getTime();
                        double _runTime1 = runTimeLen1 / 1000 / 60 / 60d;
                        if (null == info.getStandbyTime()) {
                            BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                            crystalPullingInfoLog.setRunTimeActual(runTimeActual);
                        } else {
                            BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                            BigDecimal subtract = runTimeActual.subtract(info.getStandbyTime());
                            crystalPullingInfoLog.setRunTimeActual(subtract);
                        }
                    }
                }
            }


            if (null != info.getStandbyTime()) {
                crystalPullingInfoLog.setStandbyTime(info.getStandbyTime());
            }
            if (null != info.getAbnormalStandbyInfo()) {
                crystalPullingInfoLog.setAbnormalStandbyInfo(info.getAbnormalStandbyInfo());
            }


            crystalPullingInfoLog.setPowerConsumption(info.getUseKwh());

            if (crystalPullingInfoLog.getRunTime() != null && crystalPullingInfoLog.getLineWeight() != null
                    && !(crystalPullingInfoLog.getRunTime().compareTo(BigDecimal.ZERO) == 0)) {
                BigDecimal runTime = crystalPullingInfoLog.getRunTime().add(new BigDecimal(14.5));
                crystalPullingInfoLog.setOneHourOut(crystalPullingInfoLog.getLineWeight().divide(runTime, 2, BigDecimal.ROUND_HALF_UP));
            }

            crystalPullingInfoLogService.saveOrUpdate(crystalPullingInfoLog);
            if (info.getLineLength() != null) {
                info.setLineLength(new BigDecimal(0));
            }
            //info.setLotNo(lotNo);//炉次号
//            info.setCrucible("");
//            info.setConductionType("");//型号
//            info.setSpecs("");//规格
//            info.setMaterialCount(null);
//            info.setInfo(null);
//
            //eap 异常上传停炉信号的情况下，清除开始时间，后续正常停炉再写入开始时间
//            info.setStartTime(null);
//            info.setStopTime(null);
//            info.setUseKwh(null);
//            info.setDataUpdateTime(null);
//            crystalPullingInfoService.saveOrUpdate(info);
            //异常停炉不清空拉晶实时情况，防止后续正常停炉采集不到开始时间

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 停炉时清空上炉次数据
     * 用于eap上传正常停炉信号用
     *
     * @param info
     */
    private void clearCrystalPullingInfo(CrystalPullingInfo info, String deviceCode) {
        try {

            if (info == null) {
                info = new CrystalPullingInfo();
                info.setDeviceNo(deviceCode);
            }

            info.setStopTime(new Date());


            CrystalPullingInfoLog crystalPullingInfoLog = new CrystalPullingInfoLog();
            crystalPullingInfoLog.setSeries(info.getSeries());
            crystalPullingInfoLog.setSpecs(info.getSpecs());
            crystalPullingInfoLog.setConductionType(info.getConductionType());
            crystalPullingInfoLog.setDeviceNo(info.getDeviceNo());
            crystalPullingInfoLog.setLotNo(info.getLotNo());
            crystalPullingInfoLog.setMaterialCount(info.getMaterialCount());
            crystalPullingInfoLog.setLineLength(info.getLineLength());
            crystalPullingInfoLog.setInfo(info.getInfo());

            List<DictionaryItem> items = dictionaryItemService.findByThemeValueAndEnable(EnumValuesServer.DictionaryTheme.划线重量折算系数.getKey(), true);

            Map<String, DictionaryItem> collect = items.stream().collect(Collectors.toMap(DictionaryItem::getName, DictionaryItem -> DictionaryItem));

            try {
                double lineLength = info.getLineLength().doubleValue();
                double xishu = Double.valueOf(collect.get(info.getSpecs()).getValue());

                crystalPullingInfoLog.setLineWeight(BigDecimal.valueOf(lineLength / xishu));
            } catch (Exception e) {
                log.error("计算划线重量发生错误" + e.getMessage());
            }

            if (crystalPullingInfoLog.getMaterialCount() == null || crystalPullingInfoLog.getMaterialCount().doubleValue() <= 0) {
                crystalPullingInfoLog.setLineCompletionRate(new BigDecimal(0));
            } else {
                try {
                    crystalPullingInfoLog.setLineCompletionRate(BigDecimal.valueOf(crystalPullingInfoLog.getLineWeight().doubleValue() / crystalPullingInfoLog.getMaterialCount().doubleValue()));
                } catch (Exception e) {
                    log.error("计算划线成晶率发生错误" + e.getMessage());
                }
            }

            crystalPullingInfoLog.setStopTime(info.getStopTime());
            crystalPullingInfoLog.setStartTime(info.getStartTime());

            // 运行时间  当前时间-确认投料时间
            List<LoadingTask> byLotNoV2 = loadingTaskService.findByLotNoV2("停炉产出", crystalPullingInfoLog.getLotNo());
            if(!byLotNoV2.isEmpty() && null != byLotNoV2.get(0).getConfirmTime()){
                LoadingTask loadingTask = byLotNoV2.get(0);
                long runTimeLen = new Date().getTime() - loadingTask.getConfirmTime().getTime();
                double _runTime = runTimeLen / (1000 * 60 * 60d);
                BigDecimal runTime = new BigDecimal(_runTime).setScale(2, BigDecimal.ROUND_DOWN);
                crystalPullingInfoLog.setRunTime(runTime);
            }

//            if (info.getStopTime() != null && info.getStartTime() != null) {
//                long runTimeLen = info.getStopTime().getTime() - info.getStartTime().getTime();
//                double _runTime = runTimeLen / 1000 / 60 / 60d;
//                BigDecimal runTime = new BigDecimal(_runTime).setScale(2, BigDecimal.ROUND_DOWN);
//                crystalPullingInfoLog.setRunTime(runTime);
//            }


            //0714 新运行时间=当前炉次的停炉时间(当前时间 new date()) - 上一炉次停炉时间 - 异常停炉时间
            if (info.getStopTime() != null) {
                CrystalPullingInfoLog pullingInfoLogOld = crystalPullingInfoLogService.getCrystalPullingInfoLogByDeviceNo(info.getDeviceNo());//上一炉次的拉晶情况记录表
                if (null != pullingInfoLogOld && null != pullingInfoLogOld.getStopTime()) {
                    long runTimeLen1 = info.getStopTime().getTime() - pullingInfoLogOld.getStopTime().getTime();
                    double _runTime1 = runTimeLen1 / 1000 / 60 / 60d;
                    if (null == info.getStandbyTime()) {
                        BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                        crystalPullingInfoLog.setRunTimeActual(runTimeActual);
                    } else {
                        BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                        BigDecimal subtract = runTimeActual.subtract(info.getStandbyTime());
                        crystalPullingInfoLog.setRunTimeActual(subtract);
                    }
                } else {
                    //新开炉次情况，没有上一炉次信息，开始时间取开炉时间
                    if (null != info.getStartTime()) {
                        long runTimeLen1 = info.getStopTime().getTime() - info.getStartTime().getTime();
                        double _runTime1 = runTimeLen1 / 1000 / 60 / 60d;
                        if (null == info.getStandbyTime()) {
                            BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                            crystalPullingInfoLog.setRunTimeActual(runTimeActual);
                        } else {
                            BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                            BigDecimal subtract = runTimeActual.subtract(info.getStandbyTime());
                            crystalPullingInfoLog.setRunTimeActual(subtract);
                        }
                    }
                }
            }


            if (null != info.getStandbyTime()) {
                crystalPullingInfoLog.setStandbyTime(info.getStandbyTime());
            }
            if (null != info.getAbnormalStandbyInfo()) {
                crystalPullingInfoLog.setAbnormalStandbyInfo(info.getAbnormalStandbyInfo());
            }


            crystalPullingInfoLog.setPowerConsumption(info.getUseKwh());

            if (crystalPullingInfoLog.getRunTime() != null && crystalPullingInfoLog.getLineWeight() != null
                    && !(crystalPullingInfoLog.getRunTime().compareTo(BigDecimal.ZERO) == 0)) {
                BigDecimal runTime = crystalPullingInfoLog.getRunTime().add(new BigDecimal(14.5));
                crystalPullingInfoLog.setOneHourOut(crystalPullingInfoLog.getLineWeight().divide(runTime, 2, BigDecimal.ROUND_HALF_UP));
            }


            //先清除之前的记录，再插入最新记录
            crystalPullingInfoLogDao.deleteLogByLotNo(info.getLotNo());
            crystalPullingInfoLogService.saveOrUpdate(crystalPullingInfoLog);
            if (info.getLineLength() != null) {
                info.setLineLength(new BigDecimal(0));
            }
            //info.setLotNo(lotNo);//炉次号
            info.setCrucible("");
            info.setConductionType("");//型号
            info.setSpecs("");//规格
            info.setMaterialCount(null);
            info.setInfo(null);
            info.setStartTime(null);
            info.setStopTime(null);
            info.setUseKwh(null);
            info.setDataUpdateTime(null);
            crystalPullingInfoService.saveOrUpdate(info);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 0810，手动停炉时传入异常原因，同时变更停炉时间计算逻辑
     * 手动停炉时清空上炉次数据
     *
     * @param info
     * @param deviceCode    设备号
     * @param abnormalCause 异常原因
     */
    private void clearCrystalPullingInfoForManual(CrystalPullingInfo info, String deviceCode, String abnormalCause) {
        try {

            if (info == null) {
                info = new CrystalPullingInfo();
                info.setDeviceNo(deviceCode);
            }

            info.setStopTime(new Date());


            CrystalPullingInfoLog crystalPullingInfoLog = new CrystalPullingInfoLog();
            crystalPullingInfoLog.setSeries(info.getSeries());
            crystalPullingInfoLog.setSpecs(info.getSpecs());
            crystalPullingInfoLog.setConductionType(info.getConductionType());
            crystalPullingInfoLog.setDeviceNo(info.getDeviceNo());
            crystalPullingInfoLog.setLotNo(info.getLotNo());
            crystalPullingInfoLog.setMaterialCount(info.getMaterialCount());
            crystalPullingInfoLog.setLineLength(info.getLineLength());
            crystalPullingInfoLog.setInfo(info.getInfo());

            List<DictionaryItem> items = dictionaryItemService.findByThemeValueAndEnable(EnumValuesServer.DictionaryTheme.划线重量折算系数.getKey(), true);

            Map<String, DictionaryItem> collect = items.stream().collect(Collectors.toMap(DictionaryItem::getName, DictionaryItem -> DictionaryItem));

            try {
                double lineLength = info.getLineLength().doubleValue();
                double xishu = Double.valueOf(collect.get(info.getSpecs()).getValue());

                crystalPullingInfoLog.setLineWeight(BigDecimal.valueOf(lineLength / xishu));
            } catch (Exception e) {
                log.error("计算划线重量发生错误" + e.getMessage());
            }

            if (crystalPullingInfoLog.getMaterialCount() == null || crystalPullingInfoLog.getMaterialCount().doubleValue() <= 0) {
                crystalPullingInfoLog.setLineCompletionRate(new BigDecimal(0));
            } else {
                try {
                    crystalPullingInfoLog.setLineCompletionRate(BigDecimal.valueOf(crystalPullingInfoLog.getLineWeight().doubleValue() / crystalPullingInfoLog.getMaterialCount().doubleValue()));
                } catch (Exception e) {
                    log.error("计算划线成晶率发生错误" + e.getMessage());
                }
            }

            crystalPullingInfoLog.setStopTime(info.getStopTime());
            crystalPullingInfoLog.setStartTime(info.getStartTime());

            // 运行时间  当前时间-确认投料时间
            List<LoadingTask> byLotNoV2 = loadingTaskService.findByLotNoV2("手动停炉", crystalPullingInfoLog.getLotNo());
            if(!byLotNoV2.isEmpty() && null != byLotNoV2.get(0).getConfirmTime()){
                LoadingTask loadingTask = byLotNoV2.get(0);
                long runTimeLen = new Date().getTime() - loadingTask.getConfirmTime().getTime();
                double _runTime = runTimeLen / (1000 * 60 * 60d);
                BigDecimal runTime = new BigDecimal(_runTime).setScale(2, BigDecimal.ROUND_DOWN);
                crystalPullingInfoLog.setRunTime(runTime);
            }

//            if (info.getStopTime() != null && info.getStartTime() != null) {
//                long runTimeLen = info.getStopTime().getTime() - info.getStartTime().getTime();
//                double _runTime = runTimeLen / 1000 / 60 / 60d;
//                BigDecimal runTime = new BigDecimal(_runTime).setScale(2, BigDecimal.ROUND_DOWN);
//                crystalPullingInfoLog.setRunTime(runTime);
//            }


            //0714 新运行时间=当前炉次的停炉时间(当前时间 new date()) - 上一炉次停炉时间 - 异常停炉时间
            if (info.getStopTime() != null) {
                CrystalPullingInfoLog pullingInfoLogOld = crystalPullingInfoLogService.getCrystalPullingInfoLogByDeviceNo(info.getDeviceNo());//上一炉次的拉晶情况记录表
                if (null != pullingInfoLogOld && null != pullingInfoLogOld.getStopTime()) {
                    long runTimeLen1 = info.getStopTime().getTime() - pullingInfoLogOld.getStopTime().getTime();
                    double _runTime1 = runTimeLen1 / 1000 / 60 / 60d;
                    if (null == info.getStandbyTime()) {
                        BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                        crystalPullingInfoLog.setRunTimeActual(runTimeActual);
                    } else {
                        BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                        BigDecimal subtract = runTimeActual.subtract(info.getStandbyTime());
                        crystalPullingInfoLog.setRunTimeActual(subtract);
                    }
                } else {
                    //新开炉次情况，没有上一炉次信息，开始时间取开炉时间
                    if (null != info.getStartTime()) {
                        long runTimeLen1 = info.getStopTime().getTime() - info.getStartTime().getTime();
                        double _runTime1 = runTimeLen1 / 1000 / 60 / 60d;
                        if (null == info.getStandbyTime()) {
                            BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                            crystalPullingInfoLog.setRunTimeActual(runTimeActual);
                        } else {
                            BigDecimal runTimeActual = new BigDecimal(_runTime1).setScale(2, BigDecimal.ROUND_DOWN);
                            BigDecimal subtract = runTimeActual.subtract(info.getStandbyTime());
                            crystalPullingInfoLog.setRunTimeActual(subtract);
                        }
                    }
                }
            }


            if (null != info.getStandbyTime()) {
                crystalPullingInfoLog.setStandbyTime(info.getStandbyTime());
            }
            if (null != info.getAbnormalStandbyInfo()) {
                crystalPullingInfoLog.setAbnormalStandbyInfo(info.getAbnormalStandbyInfo());
            }


            crystalPullingInfoLog.setPowerConsumption(info.getUseKwh());

            if (crystalPullingInfoLog.getRunTime() != null && crystalPullingInfoLog.getLineWeight() != null
                    && !(crystalPullingInfoLog.getRunTime().compareTo(BigDecimal.ZERO) == 0)) {
                BigDecimal runTime = crystalPullingInfoLog.getRunTime().add(new BigDecimal(13));
                crystalPullingInfoLog.setOneHourOut(crystalPullingInfoLog.getLineWeight().divide(runTime, 2, BigDecimal.ROUND_HALF_UP));
            }

            //新增异常拉晶原因 字段
            if (null != abnormalCause && !abnormalCause.isEmpty()) {
                crystalPullingInfoLog.setAbnormalCause(abnormalCause);
            }

            //先清除之前的记录，再插入最新记录
            crystalPullingInfoLogDao.deleteLogByLotNo(info.getLotNo());

            crystalPullingInfoLogService.saveOrUpdate(crystalPullingInfoLog);
            if (info.getLineLength() != null) {
                info.setLineLength(new BigDecimal(0));
            }
            //info.setLotNo(lotNo);//炉次号
            info.setCrucible("");
            info.setConductionType("");//型号
            info.setSpecs("");//规格
            info.setMaterialCount(null);
            info.setInfo(null);
            info.setStartTime(null);
            info.setStopTime(null);
            info.setUseKwh(null);
            info.setDataUpdateTime(null);
            //初始化待机相关信息
            info.setStandbyTime(null);
            info.setAbnormalStandbyInfo(null);
            crystalPullingInfoService.saveOrUpdate(info);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 开炉时清空上炉次数据
     *
     * @param deviceCode
     * @param lotNo      切换后炉次号
     */
    private void resetCode(String deviceCode, String lotNo, ProductModel productModel) {
        try {
            CrystalPullingInfo info = crystalPullingInfoService.getByDeviceNo(deviceCode);
            if (info == null) {
                info = new CrystalPullingInfo();
                info.setDeviceNo(deviceCode);
            }
            info.setLotNo(lotNo);//炉次号
            info.setConductionType(productModel.getConductionType());//型号
            info.setSpecs(productModel.getSpecs());//规格
            //开炉不记录开始时间，抽真空时记录
//            info.setStartTime(new Date());
            info.setDataUpdateTime(new Date());
            crystalPullingInfoService.saveOrUpdate(info);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * eap发送抽真空信号时更新实时新拉晶情况的开始时间
     *
     * @param deviceCode
     */
    private void updateStartTimeForPullingInfo(String deviceCode) {
        try {
            CrystalPullingInfo info = crystalPullingInfoService.getByDeviceNo(deviceCode);
            if (info == null) {
                info = new CrystalPullingInfo();
                info.setDeviceNo(deviceCode);
            }

            //初次发送抽真空记录开始时间，后面再发送抽真空不记录
            if (null == info.getStartTime()) {
                info.setStartTime(new Date());
            }
            info.setDataUpdateTime(new Date());
            crystalPullingInfoService.saveOrUpdate(info);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 拉晶情况: 接收EAP数据处理
     */
    @Override
    public void crystalpullingHandler(ToMESData dto) {
        if (StringUtils.isBlank(dto.getDeviceCode())) {
            throw new RuntimeException("未传输炉台号");
        }

        Equipment equipment = equipmentService.findByCode(dto.getDeviceCode());

        if (equipment == null) {
            throw new RuntimeException("MES未配置此炉台信息");
        }


        //处理拉晶情况-实时数据
        realTimeCrystalPullingInfo(dto, equipment);

        //处理拉晶情况-记录数据

    }

    @Override
    public void crystalpullingRealTime(ToMESData dto) {
        if (StringUtils.isBlank(dto.getDeviceCode())) {
            throw new RuntimeException("未传输炉台号");
        }

        Equipment equipment = equipmentService.findByCode(dto.getDeviceCode());
        if (equipment == null) {
            throw new RuntimeException("MES未配置此炉台信息");
        }

        CrystalPullingInfo info = crystalPullingInfoService.getByDeviceNo(dto.getDeviceCode());
        if (info == null) {
            throw new RuntimeException("炉台" + dto.getDeviceCode() + "对应拉晶情况数据不存在！");
        }

        String realTimeInfo = "";
        String step = EAPUtils.getProcessStep(EAPUtils.getEventValue(dto, "4647"));
        if (step.equals("等径")) {
            info.setRealTimeInfo("等径:" + EAPUtils.getEventValue(dto, "1274Max"));
        } else {
            info.setRealTimeInfo(step);
        }
        crystalPullingInfoService.saveOrUpdate(info);
    }

    @Override
    public void shoulderSurvivalRate(ToMESData dto) {

        Equipment equipment = equipmentService.findByCode(dto.getDeviceCode());

        if (StringUtils.isBlank(dto.getDeviceCode())) {
            throw new RuntimeException("MES未配置此炉台信息");
        }

        ShoulderSurvivalRate rate = new ShoulderSurvivalRate();
        rate.setIntoShouldering(1);

        String lotNo = null;
        ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(dto.getDeviceCode(), Arrays.asList(1, 4, 5));
        if (current_productionSchedule != null) {
            lotNo = current_productionSchedule.getLotNo();
        }

        //系列
        String series = equipment.getSeries();
        //模块
        String area = equipment.getArea();
        rate.setArea(area);
        rate.setSeries(series);
        //系列
        //片区
        FactoryModel factoryModel = factoryModelService.findByCode(equipment.getArea());
        if(factoryModel != null){
            rate.setWorkshop(factoryModel.getName()); // 车间
        }

        factoryModel = factoryModelService.findByCode(equipment.getSeries());
        if(factoryModel != null){
            rate.setDistrict(factoryModel.getName().replace("片区","")); // 片区
        }

        rate.setDeviceNo(dto.getDeviceCode());
        rate.setLotNo(lotNo);


        rate.setCreateTime(new Date());


        String infoShoulderingTimeStr = EAPUtils.getEventValue(dto, "进入放肩时间");
        try {
            Date infoShoulderingTime = DateUtil.toDate(infoShoulderingTimeStr, "yyyy-MM-dd HH:mm:ss");
            rate.setInfoShoulderingTime(infoShoulderingTime);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        //计算班次
        String classNo = ReportDateUtil.getClassNo(new Date());
        rate.setClassNo(classNo);


        String infoIsodiametricTimeStr = EAPUtils.getEventValue(dto, "扩肩进入等径时间");
        try {
            Date infoIsodiametricTime = DateUtil.toDate(infoIsodiametricTimeStr, "yyyy-MM-dd HH:mm:ss");
            rate.setInfoIsodiametricTime(infoIsodiametricTime);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        int breakTake = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "是否断取")).intValue();
        rate.setBreakTake(breakTake);


        Double residueWeight = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶剩料重量")).doubleValue();
        rate.setResidueWeight(residueWeight);


        //扩肩成活率
        BigDecimal shoulderSurvivalRate = new BigDecimal(rate.getBreakTake() / rate.getIntoShouldering() * 100);
        rate.setShoulderSurvivalRate(shoulderSurvivalRate);

        //引晶功率
        BigDecimal crystalPower = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶功率"));
        rate.setCrystalPower(crystalPower);

        //放肩长度
        BigDecimal infoShoulderingLength = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "放肩长度"));
        rate.setInfoShoulderingLength(infoShoulderingLength);

        shoulderSurvivalRateService.saveOrUpdate(rate);
    }

    public boolean timeCalendar(Date nowTime, Date dayTimeStart, Date dayTimeEnd) {
        //设置当前时间
        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);
        //设置开始时间
        Calendar timeStart = Calendar.getInstance();
        timeStart.setTime(dayTimeStart);
        //设置结束时间
        Calendar timeEnd = Calendar.getInstance();
        timeEnd.setTime(dayTimeEnd);
        if (date.after(timeStart) && date.before(timeEnd)) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 处理拉晶情况-实时数据
     **/
    private void realTimeCrystalPullingInfo(ToMESData dto, Equipment equipment) {
        CrystalPullingInfo info = crystalPullingInfoService.getByDeviceNo(dto.getDeviceCode());
        if (info == null) {
            info = new CrystalPullingInfo();
            info.setDeviceNo(dto.getDeviceCode());
        }

        if (StringUtils.isBlank(info.getLotNo()) || StringUtils.isBlank(info.getConductionType()) || StringUtils.isBlank(info.getSpecs())) {
            ProductionSchedule productionSchedule = productionScheduleService.findTopByDeviceIdAndStatusOrderByIdDesc(equipment.getId(), EnumValuesServer.ProductionScheduleStatus.进行中.getKey());
            if (productionSchedule != null) {
                info.setLotNo(productionSchedule.getLotNo());//炉次号
                ProductModel productModel = productionSchedule.getProductModel();
                if (productModel != null) {
                    info.setConductionType(productModel.getConductionType());//型号
                    info.setSpecs(productModel.getSpecs());//规格
                }
                //未点开炉无生产工单(排程),去设备加工能力中取生产规格
                if (StringUtils.isBlank(info.getSpecs())) {
                    MachineCapacity machineCapacity = machineCapacityService.findByEquipmentId(equipment.getId());
                    if (machineCapacity != null) {
                        MachineCapacityInfo machineCapacityInfo = machineCapacity.getCapacityInfo();
                        if (machineCapacityInfo != null) {
                            info.setSpecs(machineCapacityInfo.getSpecification());//1.7规格
                        }
                    }
                }
            }
        }

        //info.setCrucible(crucible);//坩埚规格    --- IpcOperationController.initCrucibleInfo  370行更新此数据
        info.setSeries(equipment.getSeries());//系列
        info.setDeviceNo(dto.getDeviceCode());//炉台号
        FactoryModel factoryModel = factoryModelService.findByCode(equipment.getSeries());
        if(factoryModel != null){
            info.setDistrict(factoryModel.getName().replace("片区","")); // 片区
        }
        //投料量
        BigDecimal materialCount = splicingMaterialCount(info.getMaterialCount(), dto.getTaskCode(), dto);
        if (materialCount != null) info.setMaterialCount(materialCount.setScale(0, BigDecimal.ROUND_HALF_UP));
        //拉晶情况
        String infoStr = splicingInfo(info.getInfo(), dto.getTaskCode(), dto);
        if (infoStr != null) info.setInfo(infoStr);

        //划线长度
        BigDecimal lineLength = splicingLineLength(info.getLineLength(), dto.getTaskCode(), dto);
        info.setLineLength(lineLength);

        //耗电量
        BigDecimal useKwh = splicingUseKwh(info.getUseKwh(), dto.getTaskCode(), dto);
        info.setUseKwh(useKwh);

        info.setDataUpdateTime(new Date());
        // 运行时间
        BigDecimal runTime = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "1038"), null);
        if(null != runTime) {
            info.setRunTime(runTime);
        }

        crystalPullingInfoService.saveOrUpdate(info);

        //停炉处理
//        if (dto.getTaskCode().equals("ShutdownFurnace")) {
//            if (!"停炉异常".equals(EAPUtils.getStartCollectionEventValue(dto, "停炉类型"))) {
//                clearCrystalPullingInfo(equipment.getCode());
//            }
//        }
//        else if(dto.getTaskCode().equals("晶棒情况") && EAPUtils.getEventValue(dto, "sectionNoStation").equals("停炉收尾")){
//        	clearCrystalPullingInfo(equipment.getCode());
//        }

        if ("1.0".equals(EAPUtils.getStartCollectionEventValue(dto, "是否停炉"))
                && !"停炉异常".equals(EAPUtils.getStartCollectionEventValue(dto, "停炉类型"))) {
            clearCrystalPullingInfoForEAP(info, equipment.getCode());
        }
    }

    /**
     * 计算耗电量
     **/
    private BigDecimal splicingUseKwh(BigDecimal source, String taskCode, ToMESData data) {
        Double useKwh = source == null ? 0 : source.doubleValue();

        switch (taskCode) {
            case "ShutdownFurnace": {//停炉
                //String _useKwh = EAPUtils.getEventValue(data, "1365");//当前体现的是 总加热电能   目前数值比较小
                String _useKwh = EAPUtils.getEventValue(data, "3304");//本次加热电能  目前数值很多 看着不像本次像总耗电
                useKwh = Double.valueOf(_useKwh);
                break;
            }
            case "OpenFurnace": {//开炉
                //String _useKwh = EAPUtils.getEventValue(data, "1365");//当前体现的是 总加热电能   目前数值比较小
                String _useKwh = EAPUtils.getEventValue(data, "3304");//本次加热电能  目前数值很多 看着不像本次像总耗电
                useKwh = Double.valueOf(_useKwh);

                break;
            }
            case "初始装料": {
                //String _useKwh = EAPUtils.getEventValue(data, "1365");//当前体现的是 总加热电能   目前数值比较小
                String _useKwh = EAPUtils.getEventValue(data, "3304");//本次加热电能  目前数值很多 看着不像本次像总耗电
                useKwh = Double.valueOf(_useKwh);
                break;
            }
            case "投料重量": {
                //String _useKwh = EAPUtils.getEventValue(data, "1365");//当前体现的是 总加热电能   目前数值比较小
                String _useKwh = EAPUtils.getEventValue(data, "3304");//本次加热电能  目前数值很多 看着不像本次像总耗电
                useKwh = Double.valueOf(_useKwh);
                break;
            }
            case "断提": {
                //String _useKwh = EAPUtils.getEventValue(data, "1365");//当前体现的是 总加热电能   目前数值比较小
                String _useKwh = EAPUtils.getEventValue(data, "3304");//本次加热电能  目前数值很多 看着不像本次像总耗电
                useKwh = Double.valueOf(_useKwh);
                break;
            }
            case "肩长情况": {
                //String _useKwh = EAPUtils.getEventValue(data, "1365");//当前体现的是 总加热电能   目前数值比较小
                String _useKwh = EAPUtils.getEventValue(data, "3304");//本次加热电能  目前数值很多 看着不像本次像总耗电
                useKwh = Double.valueOf(_useKwh);
                break;
            }
            case "晶棒情况": {
                //String _useKwh = EAPUtils.getEventValue(data, "1365");//当前体现的是 总加热电能   目前数值比较小
                String _useKwh = EAPUtils.getEventValue(data, "3304");//本次加热电能  目前数值很多 看着不像本次像总耗电
                useKwh = Double.valueOf(_useKwh);
                break;
            }
            default:
                break;
        }

        return BigDecimal.valueOf(useKwh);
    }


    /**
     * 累加划线长度
     **/
    private BigDecimal splicingLineLength(BigDecimal source, String taskCode, ToMESData data) {
        Double lineLength = source == null ? 0d : source.doubleValue();

        switch (taskCode) {
            case "晶棒情况": {

                DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable(EnumValuesServer.DictionaryTheme.长晶棒减长.getKey(), "ratio", true);

                //lineLength = 0d;
                String sectionNo = EAPUtils.getEventValue(data, "sectionNo");
                String len = EAPUtils.getEventValue(data, "1274Max");
                String sectionNoStation = EAPUtils.getEventValue(data, "sectionNoStation");

                if (sectionNoStation.equals("断苞")) {
                    lineLength += (Double.valueOf(len) - (dictionaryItem == null ? 200d : Double.valueOf(dictionaryItem.getValue())));
                } else if (sectionNoStation.equals("断苞拉料")) {

                } else {
                    lineLength += Double.valueOf(len);
                }

                break;
            }
            default:
                break;
        }

        return BigDecimal.valueOf(lineLength);
    }

    /**
     * 累加复投总重
     **/
    private BigDecimal splicingMaterialCount(BigDecimal source, String taskCode, ToMESData data) {
        BigDecimal materialCount = source;

        if (materialCountTaskCodeScope.keySet().contains(taskCode)) {
            String key = materialCountTaskCodeScope.get(taskCode);

            if (taskCode.equals("初始装料")) {
                materialCount = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(data, key));
            } else {
                if (materialCount == null) materialCount = new BigDecimal(0);

                BigDecimal value = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(data, key));
                if (value == null) value = new BigDecimal(0);
                materialCount = BigDecimal.valueOf(materialCount.doubleValue() + value.doubleValue());
            }
        } else {
            materialCount = null;
        }

        return materialCount;
    }


    /**
     * 拼接拉晶情况信息
     **/
    private String splicingInfo(String source, String taskCode, ToMESData data) {
        String info = null;


        switch (taskCode) {
            case "初始装料": {
                //初始装料只添加一次
                if (!source.contains("初始装料")) {
                    info = "初始装料" + nullformart(EAPUtils.getEventValue(data, "1424", 0)) + "Kg";
                }
                break;
            }
            case "初始复投": {
                info = "初始复投" + nullformart(EAPUtils.getEventValue(data, "复投总重", 0)) + "Kg";
                break;
            }
            case "投料重量": {
                info = "投料重量" + nullformart(EAPUtils.getEventValue(data, "复投总重", 0)) + "Kg";
                break;
            }
            case "断提": {
                info = "保持" + nullformart(EAPUtils.getEventValue(data, "1274Max", 0)) + "断提";
                break;
            }
            case "肩长情况": {
                info = nullformart(EAPUtils.getEventValue(data, "扩断类型")) + nullformart(EAPUtils.getEventValue(data, "肩部长度", 0));
                break;
            }
            case "晶棒情况": {
                info = "";
                String sectionNo = EAPUtils.getEventValue(data, "sectionNo");
                info += nullformart(convertNumMap.get(Double.valueOf(sectionNo).intValue()));
                if ("断苞拉料".equals(EAPUtils.getEventValue(data, "sectionNoStation"))) {
                    info += nullformart(EAPUtils.getEventValue(data, "断拉长度", 0));
                } else {
                    info += nullformart(EAPUtils.getEventValue(data, "1274Max", 0));
                }
                info += nullformart(EAPUtils.getEventValue(data, "sectionNoStation"));
                if ("断苞拉料".equals(EAPUtils.getEventValue(data, "sectionNoStation"))) {
                    info += (Double.valueOf(EAPUtils.getEventValue(data, "3287", 0)) - Double.valueOf(EAPUtils.getEventValue(data, "断拉重量", 0))) + "KG";
                }
                break;
            }
            default:
                break;
        }

        if (info != null && !StringUtils.isBlank(source)) {
            info = source + "," + info;
        }

        return info;
    }


    private String nullformart(String value) {
        if (StringUtils.isBlank(value) || value.equals("null")) {
            //return "(未采集到)";
            return "";
        } else {

            return value;
        }
    }


    @Override
    public void processReceive(ToMESData data) {
        if (StringUtils.isBlank(data.getDeviceCode())) {
            throw new RuntimeException("未传输炉台号");
        }

        Equipment equipment = equipmentService.findByCode(data.getDeviceCode());

        if (equipment == null) {
            throw new RuntimeException("MES未配置此炉台信息");
        }
        Date now = new Date();
        String length = EAPUtils.getEventValue(data, "1274Max");
        Double equalDiameterLength = NumberUtil.Object2Double(length, null);
        // 运行时间
        BigDecimal runTime = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(data, "1038"), null);
        // 生长速率设定
        BigDecimal power = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(data, "1278"), null);
        String data4647 = EAPUtils.getProcessStep(EAPUtils.getEventValue(data, "4647"));
        if (data.getTaskCode().equals("工艺变动")) {


            if (data4647 != null) {
                equipment.setCurrentStep(data4647);
                equipment.setStepUpdateTime(new Date());
                equipment.setRunTime(runTime);
                equipment.setPower(power);
                equipmentService.saveOrUpdate(equipment);

                ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(data.getDeviceCode(), Arrays.asList(1, 4, 5));

                ProcessPath processPath = new ProcessPath();
                processPath.setEquipmentCode(data.getDeviceCode());
                processPath.setLotNo(current_productionSchedule == null ? null : current_productionSchedule.getLotNo());
                processPath.setDeviceStartTime(data.getStartTime());
                processPath.setRemark(JSON.toJSONString(data.getStartValidateMap()));
                processPath.setStepName(data4647);
                processPath.setStartTime(now);
                processPath.setEqualDiameterLength(equalDiameterLength);
                //查询复投次数
                AgainTaskDetail successByLotNo = againTaskDetailService.findSuccessByLotNo(processPath.getLotNo());
                if(null == successByLotNo){
                    processPath.setAgainNum("0");
                } else {
                    processPath.setAgainNum(successByLotNo.getAgainNum().toString());
                }

                ProcessPath topByEquipmentCodeOrderByIdDesc = processPathService.findTopByEquipmentCodeOrderByIdDesc(data.getDeviceCode());
                List<ProcessPath> processPathList = new ArrayList<>();
                if(null != topByEquipmentCodeOrderByIdDesc){
                    topByEquipmentCodeOrderByIdDesc.setEndTime(processPath.getStartTime());
                    topByEquipmentCodeOrderByIdDesc.setDeviceEndTime(processPath.getDeviceStartTime());
                    processPathList.add(topByEquipmentCodeOrderByIdDesc);
                }
                processPathList.add(processPath);
                processPathService.saveAll(processPathList);
                // 产出后未灌料
                if ("引晶".equals(data4647)) {
                    String deviceCode = data.getDeviceCode();
                    saveOutputRecord(deviceCode);
                }
                if("熔料".equals(data4647)){
                    Object o = redisUtils.hmGet(dismantlingStartKey, data.getDeviceCode());
                    if(null != o){
                        try {
                            String startTime = NumberUtil.object2String(o, null);
                            Date startDate = DateUtil.toDate(startTime, DateUtil.longFormat);
                            ProcessWorkingHours workingHours = new ProcessWorkingHours();
                            workingHours.setTimeLong(now.getTime() - startDate.getTime());
                            workingHours.setStepName("拆清到熔料工时");
                            workingHours.setLotNo(processPath.getLotNo());
                            workingHours.setEquipmentCode(processPath.getEquipmentCode());
                            workingHours.setStartTime(startDate);
                            workingHours.setEndTime(now);
                            workingHours.setArea(equipment.getArea());
                            workingHours.setSeries(equipment.getSeries());
                            processWorkingHoursService.saveOrUpdate(workingHours);
                        } catch (ParseException e) {
                            log.error("拆清到熔料工时异常：", e);
                        }
                    }

                }
                if (null == current_productionSchedule || null == current_productionSchedule.getProductModel()
                        || StringUtils.isEmpty(current_productionSchedule.getProductModel().getSpecs())) {
                    log.info("存储预取棒时,排程尺寸为空,不记录!");
                } else {
                    if (EAPUtils.stepMapping.get(57).equals(data4647) ||
                            EAPUtils.stepMapping.get(55).equals(data4647) ||
                            EAPUtils.stepMapping.get(13).equals(data4647)) {
                        //存储到预取棒记录表
                        EstimatedRecord estimatedRecord = new EstimatedRecord();
                        String specs = current_productionSchedule.getProductModel().getSpecs();
                        estimatedRecord.setDeviceCode(data.getDeviceCode())
                                .setRemarkFirst(specs)
                                .setIsDeleted(0)
                                .setLength(length)
                                .setNodeTime(data.getStartTime())
                                .setCreateTime(new Date())
                                .setCreateUser(BaseContextHandler.getUsername())
                                .setNodeName(data4647);
                        if (EAPUtils.stepMapping.get(57).equals(data4647)) {
                            //触发节点由 取段变更为取段收尾
                            if ("10吋".equals(specs)) {
                                estimatedRecord.setTakeTime(DateUtil.addMinute(estimatedRecord.getNodeTime(), 30));
                            } else if ("12吋".equals(specs)) {
                                estimatedRecord.setTakeTime(DateUtil.addMinute(estimatedRecord.getNodeTime(), 90));
                            }

                        } else if (EAPUtils.stepMapping.get(55).equals(data4647)) {
                            //断线提出
                            if ("10吋".equals(specs)) {
                                estimatedRecord.setTakeTime(DateUtil.addMinute(estimatedRecord.getNodeTime(), 30));
                            } else if ("12吋".equals(specs)) {
                                estimatedRecord.setTakeTime(DateUtil.addMinute(estimatedRecord.getNodeTime(), 90));
                            }
                        } else if (EAPUtils.stepMapping.get(13).equals(data4647)) {
                            //停炉
                            estimatedRecord.setTakeTime(DateUtil.addMinute(estimatedRecord.getNodeTime(), 540));
                        }
                        estimatedRecordService.saveOrUpdate(estimatedRecord);
                    }
                }
            }
        } else if (data.getTaskCode().equals("在线状态")) {
            String onlineState = EAPUtils.getEventValue(data, "deviceStatus");
            equipment.setOnlineStatus(onlineState);
            equipment.setRunTime(runTime);
            equipment.setPower(power);
            equipment.setCurrentStep(data4647);
            equipmentService.saveOrUpdate(equipment);
        }

        redisUtils.hmSet(equalDiameterLengthKey,data.getDeviceCode(),equalDiameterLength);
    }

    @Override
    public void delayed(ToMESData data) {
        String deviceCode = data.getDeviceCode();
        if (StringUtils.isBlank(deviceCode)) {
            throw new RuntimeException("未传输炉台号");
        }

        Equipment equipment = equipmentService.findByCode(deviceCode);

        if (equipment == null) {
            throw new RuntimeException("MES未配置此炉台信息");
        }
        ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(deviceCode, Arrays.asList(1, 4, 5));
        if(null == current_productionSchedule || StringUtils.isEmpty(current_productionSchedule.getLotNo())){
            throw new RuntimeException("当前设备【" + deviceCode +"】没有炉次号");
        }
        String lotNo = current_productionSchedule.getLotNo();

        Integer time = getTime();
        if(data.getTaskCode().equals("12小时未等径")){
            RunTimeDto runTimeDto = new RunTimeDto();
            runTimeDto.setEquipmentCode(deviceCode);
            runTimeDto.setLotNo(lotNo);
            runTimeDto.setStepName(data.getTaskCode());
            String json = JSONObject.toJSONString(runTimeDto);
            try {
                rabbitMqService.sendDelayedMessage(json, ExchangeEnum.DELAYED_EXCHANGE, QueueEnum.DELAYED_QUEUE, RoutingKey.DELAYED_ROUTING_KEY,time * 60 * 1000);
            } catch (Exception e){
                log.error("delayed MQ发送数据异常",e);
                throw new RuntimeException("当前设备【" + deviceCode +"】MQ发送数据异常");
            }


        }
    }

    public Integer  getTime(){
        int delayedTime = 12 * 60;
        List<DictionaryItem> dictionaryItemList = dictionaryItemService.findItemByThemeValue("delayed_time", true);
        Map<String, List<DictionaryItem>> map = null;
        if(!dictionaryItemList.isEmpty()){
            map = dictionaryItemList.stream().collect(Collectors.groupingBy(DictionaryItem::getName));
        }
        if(null != map && !map.isEmpty()){
            List<DictionaryItem> dictionaryItems = map.get("12小时未等径");
            if(!dictionaryItems.isEmpty()) {
                DictionaryItem dictionaryItem = dictionaryItems.get(0);
                delayedTime = NumberUtil.Object2Integer(dictionaryItem.getValue(), 12 * 60);
            }
        }
        return delayedTime;
    }

    @Override
    @ServiceLogs(description = "未等径")
    public RunTimeDto delayedData(RunTimeDto runTimeDto) {
        String equipmentCode = runTimeDto.getEquipmentCode();
        String lotNo = runTimeDto.getLotNo();
        try {
            Integer time = getTime();
            // 12小时前
            String startTime = DateUtil.formatDate(new Date(new Date().getTime() - time*60*1000),DateUtil.longFormat);
            List<ProcessPath> processPathList = processPathService.findData(equipmentCode, null, "等径", startTime, null, null);
            if(processPathList.isEmpty()){
                Object o = redisUtils.hmGet(equalDiameterLengthKey, equipmentCode);
                // 12小时内没有进入等径
                ProcessNoDiameter processNoDiameter = new ProcessNoDiameter();
                processNoDiameter.setEquipmentCode(equipmentCode);
                processNoDiameter.setLotNo(lotNo);
                processNoDiameter.setStepName("12小时未等径");
                processNoDiameter.setCreateTime(new Date());
                processNoDiameter.setEqualDiameterLength(NumberUtil.Object2Double(o,null));
                processNoDiameterService.saveOrUpdate(processNoDiameter);
                StringBuilder sb = new StringBuilder();
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String date = formatter.format(processNoDiameter.getCreateTime());
                sb.append("["+date+"]MES12小时未等径预警推送:");
                sb.append("\n");
                sb.append(".内容:").append("12小时未等径");
                sb.append("\n");
                sb.append(".炉台号:"+processNoDiameter.getEquipmentCode());
                ArrayList<String> groupNameList = new ArrayList<>();
                groupNameList.add("12小时未等径预警消息推送");
                ArrayList<String> sceneList = new ArrayList<>();
                sceneList.add("prod");
                String source = "12小时未等径预警消息推送";
                dingTalkService.robotSeedMessage(sb.toString(), groupNameList, source, sceneList, SeedMassageEnum.GROUP);

            }
        } catch (Exception e){
            log.error("未等径异常：",e);
            runTimeDto.setErrorMsg(e.getMessage());
        }
        return runTimeDto;
    }

    @Override
    public void dismantlingStart(DismantlingDto dismantlingDto) {
        String startTime = DateUtil.formatDate(new Date(), DateUtil.longFormat);
        Equipment byCode = equipmentService.findByCode(dismantlingDto.getDeviceCode());
        if(null != byCode) {
            if(!StringUtils.isEmpty(byCode.getCurrentStep())
                    && ("停炉".equals(byCode.getCurrentStep()) || "手动".equals(byCode.getCurrentStep()))){
                redisUtils.hmSet(dismantlingStartKey, dismantlingDto.getDeviceCode(), startTime);
            } else {
                throw new RuntimeException("当前炉台状态未处于停炉或手动状态，不支持点击拆清按钮，请确认！");
            }
        } else {
            throw new RuntimeException("未找到当前设备的设备台账");
        }
    }

    /**
     * 查询当前正在生产的棒子,是否有灌料
     *
     * @param deviceCode
     */
    public void saveOutputRecord(String deviceCode) {
        SysLog sysLog = new SysLog();
        Date date = new Date();
        Map<String, Object> map = new HashMap<>();
        try {
            LongCrystal lastCrystalNo = longCrystalService.getLastCrystalNo(deviceCode);
            Integer nowAgainNum = null;
            Integer againNum = null;
            BigDecimal againWeigth = null;
            String lotNo = null;
            /**
             * 完结棒       V
             * 完结尾       W
             * 无
             */
            String closureIdentification = null;
            /**
             * 完结棒       V
             * 完结尾       W
             * 正常收尾     Y
             * 等径断线     N1
             * 收尾断线     N2
             * 多晶棒       D
             */
            String windUp = null;
            String crystalNo = null;
            BigDecimal length = null;
            if (null != lastCrystalNo && !StringUtils.isEmpty(lastCrystalNo.getLotNo()) && !StringUtils.isEmpty(lastCrystalNo.getCrystalNo())) {
                closureIdentification = lastCrystalNo.getClosureIdentification();
                windUp = lastCrystalNo.getWindUp();
                crystalNo = lastCrystalNo.getCrystalNo();
                length = lastCrystalNo.getLength();
                if (!StringUtils.isEmpty(closureIdentification)) {
                    if ("W".equalsIgnoreCase(closureIdentification)) {
                        // 是完结尾
                        nowAgainNum = 1;
                    } else if ("V".equalsIgnoreCase(windUp)) {
                        // 完结棒
                        nowAgainNum = lastCrystalNo.getAgainNum();

                    } else {
                        if (!StringUtils.isEmpty(windUp)) {
                            if (!"N1".equalsIgnoreCase(windUp)) {
                                // 不是等径断线
                                nowAgainNum = lastCrystalNo.getAgainNum() + 1;
                            } else {
                                // 首棒
                                if (crystalNo.endsWith("10") || crystalNo.endsWith("11")) {
                                    // 首棒
                                    if (length.doubleValue() > 1000) {
                                        // 首棒且长度大于1000
                                        nowAgainNum = lastCrystalNo.getAgainNum() + 1;
                                    } else {
                                        nowAgainNum = lastCrystalNo.getAgainNum();
                                    }
                                } else {
                                    if (length.doubleValue() > 1500) {
                                        // 非首棒且长度大于1500
                                        nowAgainNum = lastCrystalNo.getAgainNum() + 1;
                                    } else {
                                        nowAgainNum = lastCrystalNo.getAgainNum();
                                    }
                                }
                            }

                        }
                    }
                }
                AgainTaskDetail againTaskDetail = againTaskDetailService.findTopByEquipmentCodeOrderByIdDesc(deviceCode);
                if (null != againTaskDetail) {
                    lotNo = againTaskDetail.getLotNo();
                    AgainTask againTask = againTaskService.get(againTaskDetail.getAgainTaskId());
                    if (null != againTask) {
                        againWeigth = againTask.getAgainWeigth();
                        againNum = againTask.getAgainNum();
                        map.put("againTask", againTask.getId());
                    }
                    if (null != nowAgainNum && !nowAgainNum.equals(againNum)) {
//                    LoadingTask loadingTask = loadingTaskService.getFinishTask(deviceCode, againTaskDetail.getLotNo());
//                    if (null == loadingTask) {
                        // 复投次数不一致，查询当前计算的复投次数是否完成，没有完成保存数据
                        List<AgainTask> againTaskList = againTaskService.findSuccessByEquipmentCodeAndAgainNum(deviceCode, nowAgainNum);
                        boolean empty = againTaskList.isEmpty();
                        map.put("empty", empty);
                        if (againTaskList.isEmpty()) {
                            OutputRecord outputRecord = new OutputRecord();
                            outputRecord.setAgainNum(nowAgainNum);
                            outputRecord.setCreateTime(date);
                            outputRecord.setDeviceNo(deviceCode);
                            outputRecord.setLotNo(lotNo);
                            outputRecord.setWeigth(null == againWeigth ? null : againWeigth.doubleValue());
                            OutputRecord topByDeviceNo = outputRecordService.findTopByDeviceNo(deviceCode);
                            if (null != topByDeviceNo) {
                                outputRecord.setId(topByDeviceNo.getId());
                            }
                            outputRecordService.saveOrUpdate(outputRecord);
//                    } else {
//                        map.put("loadingTask",loadingTask.getId());
//                    }
                        }
                    }
                    map.put("lotNo", lotNo);
                    map.put("nowAgainNum", nowAgainNum);
                    map.put("againNum", againNum);
                    map.put("againWeigth", againWeigth);
                    map.put("closureIdentification", closureIdentification);
                    map.put("windUp", windUp);
                    map.put("againTaskDetail", againTaskDetail.getId());
                    map.put("length", length);
                }
            }

        } catch (Exception e) {
            sysLog.setResult(e.getMessage());
            e.printStackTrace();
        }
        sysLog.setCreateTime(date);
        sysLog.setType("info");
        sysLog.setCreateUserName(BaseContextHandler.getUsername());
        sysLog.setContent(JSON.toJSONString(map));
        sysLog.setMethodName("产出后未灌料");
        sysLog.setName("产出后未灌料");
        sysLog.setReqIp(IpUtils.getLocalIP());
        sysLogService.save(sysLog);
    }


    //工时-工步名称
    //operatorUser
    //url {deviceCode:'' operatorUser:''}  还要token
    @Override
    public void workingHours(ToMESData data) {
        if (StringUtils.isBlank(data.getDeviceCode())) {
            throw new RuntimeException("未传输炉台号");
        }

        Equipment equipment = equipmentService.findByCode(data.getDeviceCode());

        if (StringUtils.isBlank(data.getDeviceCode())) {
            throw new RuntimeException("MES未配置此炉台信息");
        }


        String taskCode = data.getTaskCode();
        String[] taskCodeNames = taskCode.split("-");
        if (taskCodeNames.length != 2) throw new RuntimeException("工序名称格式不正确:" + taskCode);

        String name = taskCodeNames[1];


        String deviceTypeCode = null;//设备类型编号
        String lotNo = null;//炉次号

        Device device = equipment.getDevice();
        if (device != null) {
            deviceTypeCode = device.getName();
        }

        if (StringUtils.isBlank(deviceTypeCode)) {
            throw new RuntimeException("MES未配置设备类型编码:" + deviceTypeCode);
        }


        ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(data.getDeviceCode(), Arrays.asList(1, 4, 5));
        if (current_productionSchedule != null) {
            lotNo = current_productionSchedule.getLotNo();
        }


        List<ParamSet> paramSets = paramSetService.findByDeviceName(deviceTypeCode);
        Map<String, ParamSet> _paramSetMap = paramSets.stream().collect(Collectors.toMap(ParamSet::getProcessName, t -> t));


//		if(lotNo==null) {
//			workingHours = processWorkingHoursService.getByEquipmentCodeAndLotNoIsNull(data.getDeviceCode());
//			if(workingHours==null) {
//				workingHours = new ProcessWorkingHours();
//			}
//		}else {
//			workingHours = processWorkingHoursService.getByEquipmentCodeAndLotNoIsNull(data.getDeviceCode());
//			if(workingHours==null) {
//				workingHours = processWorkingHoursService.getByEquipmentCodeAndLotNo(data.getDeviceCode(),lotNo);
//				if(workingHours==null) {
//					workingHours = new ProcessWorkingHours();
//				}
//			}
//		}
        ProcessWorkingHours workingHours = new ProcessWorkingHours();
        workingHours.setEquipmentCode(data.getDeviceCode());
        workingHours.setLotNo(lotNo);
        workingHours.setStepName(name);

//        if(name.equals("取段复投")) {
//        	String startTimeStr = EAPUtils.getEndEventValue(data, "取单晶时间");
//
//        	Date startTime = null;
//			try {
//				startTime = DateUtil.toDate(startTimeStr, "yyyy-MM-dd HH:mm:ss");
//			} catch (ParseException e) {
//				e.printStackTrace();
//			}
//        	workingHours.setStartTime(startTime);
//        }else {

        String startTimeStr = EAPUtils.getEventValue(data, taskCode + "-开始时间");
        Date startTime = null;
        try {
            startTime = DateUtil.toDate(startTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        workingHours.setStartTime(startTime);
//        }

        workingHours.setArea(equipment.getArea());
        workingHours.setSeries(equipment.getSeries());

        //系列
        //片区
        FactoryModel factoryModel = factoryModelService.findByCode(equipment.getArea());
        if(factoryModel != null){
            workingHours.setWorkshop(factoryModel.getName()); // 车间
        }

        factoryModel = factoryModelService.findByCode(equipment.getSeries());
        if(factoryModel != null){
            workingHours.setDistrict(factoryModel.getName().replace("片区","")); // 片区
        }

        ParamSet paramSet = _paramSetMap.get(name);
        if (paramSet != null && paramSet.getParamValue() != null) {
            workingHours.setTheoryTime(paramSet.getParamValue());
        }

        if (data.getEndTime() != null) {

            String endTimeStr = EAPUtils.getEventValue(data, taskCode + "-结束时间");
            Date endTime = null;
            if (endTimeStr != null) {
                try {
                    endTime = DateUtil.toDate(endTimeStr, "yyyy-MM-dd HH:mm:ss");
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }

            workingHours.setEndTime(endTime);

            String end_operatorUser = EAPUtils.getEndEventValue(data, "operatorUser");
            workingHours.setEndOperatorUser(end_operatorUser);
        }

        //获取等径断苞稳温单晶长度
        workingHours.setLength(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(data, "1274Max")));

        if (workingHours.getStartTime() != null && workingHours.getEndTime() != null) {
            workingHours.setTimeLong(workingHours.getEndTime().getTime() - workingHours.getStartTime().getTime());

            //计算浪费工时
            if (workingHours.getTheoryTime() != null) {
                Long _theoryTime = new BigDecimal(workingHours.getTheoryTime().doubleValue() * 60 * 60 * 1000).longValue();

                Long wasteTime = _theoryTime - workingHours.getTimeLong();
//				if(wasteTime<0) {
//					wasteTime = 0l;
//				}
                workingHours.setWasteTime(wasteTime);
            }
        }

        String start_operatorUser = EAPUtils.getStartEventValue(data, "operatorUser");
        workingHours.setStartOperatorUser(start_operatorUser);

        ProcessWorkingHours processWorkingHours = processWorkingHoursService.findTopByEquipmentCodeAndLotNoAndStartTimeAndEndTimeOrderByIdDesc(data.getDeviceCode(), lotNo, workingHours.getStartTime(), workingHours.getEndTime());
        if(null != processWorkingHours){
            throw new RuntimeException("工时已重复");
        }

        //复投次数 - 取段复投专用字段 有值就取
        //String end_againNum = EAPUtils.getEndEventValue(data, "复投次数");
        workingHours.setAgainNum(0);
        //查询炉次已完成的罐料任务
        AgainTaskDetail againTaskDetail = againTaskDetailService.findSuccessByLotNo(lotNo);
        if (null != againTaskDetail) {
            AgainTask againTask = againTaskService.get(againTaskDetail.getAgainTaskId());
            if (null != againTask) {
                workingHours.setAgainNum(againTask.getAgainNum());
            }
        }
        //workingHours.setAgainNum(NumberUtil.String2Integer(end_againNum, null));
//        Double length = NumberUtil.Object2Double(EAPUtils.getEndEventValue(data, "1274Max"), null);
//        // 取棒工时（长度0~600 减去30分钟，长度大于600减去20分钟）
//        if("取棒工时".equals(workingHours.getStepName()) &&  null != length && length >= 0) {
//            if(length >= 0 && length < 600){
//                workingHours.setTimeLong(workingHours.getTimeLong() - 30 * 60 * 1000);
//            } else if(length >= 600){
//                workingHours.setTimeLong(workingHours.getTimeLong() - 20 * 60 * 1000);
//            }
//        }


        processWorkingHoursService.saveOrUpdate(workingHours);

    }

    @Override
    public void workingStep(ToMESData data) {
        if (StringUtils.isBlank(data.getDeviceCode())) {
            throw new RuntimeException("未传输炉台号");
        }

        Equipment equipment = equipmentService.findByCode(data.getDeviceCode());

        if (StringUtils.isBlank(data.getDeviceCode())) {
            throw new RuntimeException("MES未配置此炉台信息");
        }


        String taskCode = data.getTaskCode();
        String[] taskCodeNames = taskCode.split("-");
        if (taskCodeNames.length != 2) throw new RuntimeException("工序名称格式不正确:" + taskCode);

        String name = taskCodeNames[1];


        String deviceTypeCode = null;//设备类型编号
        String lotNo = null;//炉次号

        Device device = equipment.getDevice();
        if (device != null) {
            deviceTypeCode = device.getCode();
        }

        if (StringUtils.isBlank(deviceTypeCode)) {
            throw new RuntimeException("MES未配置设备类型编码:" + deviceTypeCode);
        }
        ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(data.getDeviceCode(), Arrays.asList(1, 4, 5));
        if (current_productionSchedule != null) {
            lotNo = current_productionSchedule.getLotNo();
        }
        ProcessWorkingStep workingStep = new ProcessWorkingStep();
        workingStep.setEquipmentCode(data.getDeviceCode());
        workingStep.setLotNo(lotNo);
        workingStep.setStepName(name);

        String data4647 = EAPUtils.getProcessStep(EAPUtils.getEndEventValue(data, "4647"));
        workingStep.setLastStepName(data4647);


        workingStep.setStartTime(data.getEndTime());

        workingStep.setArea(equipment.getArea());
        workingStep.setSeries(equipment.getSeries());

        String start_operatorUser = EAPUtils.getEventValue(data, "operatorUser");
        workingStep.setStartOperatorUser(start_operatorUser);


        //复投次数
        String end_againNum = EAPUtils.getEndEventValue(data, "复投次数");
        workingStep.setAgainNum(NumberUtil.String2Integer(end_againNum, null));

        processWorkingStepService.saveOrUpdate(workingStep);

    }


    @Override
    public void accidentAnomaly(ToMESData data) {
        String deviceCode = data.getDeviceCode();
        if (StringUtils.isBlank(deviceCode)) {
            throw new RuntimeException("未传输炉台号");
        }

        Equipment equipment = equipmentService.findByCode(deviceCode);
        if (null == equipment) {
            throw new RuntimeException("MES未配置此炉台信息");
        }

        String taskCode = data.getTaskCode();
        String[] taskCodeNames = taskCode.split("-");
        if (taskCodeNames.length != 2) throw new RuntimeException("工序名称格式不正确:" + taskCode);

        String name = taskCodeNames[1];
        String nameType = taskCodeNames[0];
        AlarmData alarmData = new AlarmData();
        alarmData.setDeviceNo(deviceCode);
        alarmData.setDescription(name);
        alarmData.setDeviceType(equipment.getDevice().getName());
        alarmData.setCreateTime(new Date());
        List<AlarmConfig> all = alarmConfigService.findByDeviceType("单晶炉");
        Map<String, String> map = all.stream().collect(Collectors.toMap(AlarmConfig::getCode,AlarmConfig::getName,(v1,v2)->v1));
        Map<String,Object> map1 = new HashMap<>();
        Map<String, String> startValidateMap = data.getStartCollectionData();
        if(!startValidateMap.isEmpty()){
            Set<Map.Entry<String, String>> entries = startValidateMap.entrySet();
            for(Map.Entry<String, String> entry : entries){
                String key = entry.getKey();
                String value = entry.getValue();
                if(map.containsKey(key)){
                    map1.put(map.get(key),value);
                } else if(map.containsKey("s-" + key)){
                    map1.put(map.get(key),value);
                } else if(map.containsKey("S-" + key)){
                    map1.put(map.get(key),value);
                } else {
                    map1.put(key,value);
                }
            }
        }
        if(map1.containsKey("工步")){
            Object stepName = map1.get("工步");
            if(null != stepName){
                BigDecimal bigDecimal = NumberUtil.Object2BigDecimal(stepName, null);
                if(null != bigDecimal){
                    int i = bigDecimal.intValue();
                    if(EAPUtils.stepMapping.containsKey(i)){
                        map1.put("工步",EAPUtils.stepMapping.get(i));
                    }
                }
            }
        }
        String json = JSONObject.toJSONString(map1);
        alarmData.setCode(json);
        alarmDataService.saveOrUpdate(alarmData);
        if ("事故".equals(nameType)){
            StringBuilder sb = new StringBuilder();
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String date = formatter.format(alarmData.getCreateTime());
            sb.append("["+date+"]MES异常炉台事故推送:");
            sb.append("\n");
            sb.append(".事故:").append(alarmData.getDescription());
            sb.append("\n");
            sb.append(".内容:").append(json);
            sb.append("\n");
            sb.append(".炉台号:"+alarmData.getDeviceNo());
            ArrayList<String> groupNameList = new ArrayList<>();
            groupNameList.add("异常炉台事故消息推送");
            ArrayList<String> sceneList = new ArrayList<>();
            sceneList.add("prod");
            String source = "异常炉台事故消息推送";
            dingTalkService.robotSeedMessage(sb.toString(), groupNameList, source, sceneList, SeedMassageEnum.GROUP);
        }else if("预警".equals(nameType)){
            StringBuilder sb = new StringBuilder();
            if(name.contains("埚转")) {
                ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(data.getDeviceCode(), Arrays.asList(1, 4, 5));
                if (null != current_productionSchedule && !StringUtils.isEmpty(current_productionSchedule.getLotNo())) {
                    String lotNo = current_productionSchedule.getLotNo();
                    map1.put("炉次号", lotNo);
                    AgainTaskDetail successByLotNo = againTaskDetailService.findSuccessByLotNo(lotNo);
                    if (null == successByLotNo) {
                        map1.put("加次", "0");
                    } else {
                        Integer againNum = successByLotNo.getAgainNum();
                        map1.put("加次", againNum.toString());
                        Integer byLotNoAndAddTime = brokenBractBaseService.findByLotNoAndAddTime(lotNo, againNum);
                        if (null != byLotNoAndAddTime) {
                            map1.put("引放次数", byLotNoAndAddTime.toString());
                        } else {
                            map1.put("引放次数", "1");
                        }
                    }
                }
//                json = JSONObject.toJSONString(map1);
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String date = formatter.format(alarmData.getCreateTime());
                sb.append("["+date+"]MES异常炉台预警推送:");
                sb.append(".内容:").append(alarmData.getDescription());
                sb.append("\n");
                sb.append(".炉台号:"+alarmData.getDeviceNo());
                sb.append("\n");
                sb.append(".加次:"+JSONObject.toJSONString(map1.get("加次")));
                sb.append("\n");
                sb.append(".引放次数:").append(JSONObject.toJSONString(map1.get("引放次数")));
                sb.append("\n");
                sb.append(".埚转:").append(JSONObject.toJSONString(map1.get("埚转")));
                sb.append("\n");
                sb.append(".工步:").append(JSONObject.toJSONString(map1.get("工步")));
                sb.append("\n");
                sb.append(".晶体长度(mm):").append(JSONObject.toJSONString(map1.get("晶体长度(mm)")));
                sb.append("\n");
                sb.append(".剩料重量:").append(JSONObject.toJSONString(map1.get("剩料重量")));
            }
            ArrayList<String> groupNameList = new ArrayList<>();
            groupNameList.add("异常炉台预警消息推送");
            ArrayList<String> sceneList = new ArrayList<>();
            sceneList.add("prod");
            String source = "异常炉台预警消息推送";
            dingTalkService.robotSeedMessage(sb.toString(), groupNameList, source, sceneList, SeedMassageEnum.GROUP);
        }

    }

    @Override
    public void shoulderSeedingRate(ToMESData dto) {

        Equipment equipment = equipmentService.findByCode(dto.getDeviceCode());

        if (StringUtils.isBlank(dto.getDeviceCode())) {
            throw new RuntimeException("MES未配置此炉台信息");
        }

        ShoulderSeedingRate rate = new ShoulderSeedingRate();
        rate.setIntoSeeding(1);

        String lotNo = null;
        ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(dto.getDeviceCode(), Arrays.asList(1, 4, 5));
        if (current_productionSchedule != null) {
            lotNo = current_productionSchedule.getLotNo();
        }

        //系列
        String series = equipment.getSeries();
        //模块
        String area = equipment.getArea();

        rate.setArea(area);
        rate.setSeries(series);
        //系列
        //片区
        FactoryModel factoryModel = factoryModelService.findByCode(equipment.getArea());
        if(factoryModel != null){
            rate.setWorkshop(factoryModel.getName()); // 车间
        }

        factoryModel = factoryModelService.findByCode(equipment.getSeries());
        if(factoryModel != null){
            rate.setDistrict(factoryModel.getName().replace("片区","")); // 片区
        }
        rate.setDeviceNo(dto.getDeviceCode());
        rate.setLotNo(lotNo);

        rate.setCreateTime(new Date());

        int breakTake = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "是否引段")).intValue();
        rate.setBreakTake(breakTake);

        BigDecimal residueWeightDec = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶剩料重量"));
        Double residueWeight = null;
        if (residueWeightDec != null) {
            residueWeight = residueWeightDec.doubleValue();
        }
        rate.setResidueWeight(residueWeight);

        BigDecimal seedingLength = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶长度"));
        rate.setSeedingLength(seedingLength);


        Date breakTakeTime = null;
        try {
            String breakTakeTimeStr = EAPUtils.getEventValue(dto, "引晶断时间");
            if (StringUtils.isNotBlank(breakTakeTimeStr) && !breakTakeTimeStr.equals("null"))
                breakTakeTime = DateUtil.toDate(breakTakeTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Date yjInfoShoulderingTime = null;
        try {
            String yjInfoShoulderingTimeStr = EAPUtils.getEventValue(dto, "引晶-进入放肩时间");
            yjInfoShoulderingTime = DateUtil.toDate(yjInfoShoulderingTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Date infoSeedingSuccessTime = null;
        try {
            String infoSeedingSuccessTimeStr = EAPUtils.getEventValue(dto, "引晶成功时间");
            infoSeedingSuccessTime = DateUtil.toDate(infoSeedingSuccessTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }


        Date infoSeedingTime = null;
        try {
            String infoSeedingTimeStr = EAPUtils.getEventValue(dto, "进入引晶时间");
            infoSeedingTime = DateUtil.toDate(infoSeedingTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }


        Date infoShoulderingTime = null;
        try {
            String infoShoulderingTimeStr = EAPUtils.getEventValue(dto, "进入放肩时间");
            infoShoulderingTime = DateUtil.toDate(infoShoulderingTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }


        //进入引晶时间
        rate.setInfoSeedingTime(infoSeedingTime);

        //计算班次
        String classNo = ReportDateUtil.getClassNo(new Date());
        rate.setClassNo(classNo);

        //计算引晶时长
        BigDecimal seedingTime = null;
        if (rate.getBreakTake() == 1) {//
            rate.setInfoShoulderingTime(infoShoulderingTime);
            if (yjInfoShoulderingTime != null && infoSeedingTime != null) {
                Long time = yjInfoShoulderingTime.getTime() - infoSeedingTime.getTime();
                double value = (time / 1000d) / 60 / 60;
                seedingTime = new BigDecimal(value).setScale(2, BigDecimal.ROUND_DOWN);
            }
        } else {
            rate.setInfoShoulderingTime(breakTakeTime);
            if (breakTakeTime != null && infoSeedingTime != null) {
                Long time = breakTakeTime.getTime() - infoSeedingTime.getTime();
                double value = (time / 1000d) / 60 / 60;
                seedingTime = new BigDecimal(value).setScale(2, BigDecimal.ROUND_DOWN);
            }
        }
        rate.setSeedingTime(seedingTime);

        //扩肩成活率
        BigDecimal shoulderSurvivalRate = new BigDecimal(rate.getBreakTake() / rate.getIntoSeeding() * 100);
        rate.setSeedingRate(shoulderSurvivalRate);

        //引晶功率
        BigDecimal crystalPower = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶功率"));
        rate.setCrystalPower(crystalPower);


        shoulderSeedingRateService.saveOrUpdate(rate);
    }


    @Override
    public void expansionBase(ToMESData dto) {
        Equipment equipment = equipmentService.findByCode(dto.getDeviceCode());
        if (null == equipment) {
            throw new RuntimeException("设备不存在");
        }

        if (StringUtils.isBlank(dto.getDeviceCode())) {
            throw new RuntimeException("MES未配置此炉台信息");
        }

        //系列
        String series = equipment.getSeries();

        ExpansionBase expansionBase = new ExpansionBase();

        String lotNo = null;
        ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(dto.getDeviceCode(), Arrays.asList(1, 4, 5));
        if (current_productionSchedule != null) {
            lotNo = current_productionSchedule.getLotNo();
            expansionBase.setLotNo(lotNo);
            ProductModel productModel = current_productionSchedule.getProductModel();
            if (productModel != null) {
                expansionBase.setSpecs(productModel.getSpecs());//规格
            }
        }

        //未点开炉无生产工单(排程),去设备加工能力中取生产规格
        if (StringUtils.isBlank(expansionBase.getSpecs())) {
            MachineCapacity machineCapacity = machineCapacityService.findByEquipmentId(equipment.getId());
            if (machineCapacity != null) {
                MachineCapacityInfo machineCapacityInfo = machineCapacity.getCapacityInfo();
                if (machineCapacityInfo != null) {
                    expansionBase.setSpecs(machineCapacityInfo.getSpecification());//1.7规格
                }
            }
        }

        Date startTime = null;
        try {
            String startTimeStr = EAPUtils.getEventValue(dto, "开炉时间");
            startTime = DateUtil.toDate(startTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        expansionBase.setStartTime(startTime);

        //扩断时间
        Date expansionTime = new Date();
//        try {
//        	String expansionTimeStr = EAPUtils.getEventValue(dto, "扩断时间");
//        	expansionTime = DateUtil.toDate(expansionTimeStr, "yyyy-MM-dd HH:mm:ss");
//        } catch (ParseException e) {
//        	e.printStackTrace();
//        }


//        EquipmentExtendState state = equipmentExtendStateService.getByCode(dto.getDeviceCode());
//        if (state != null) {
//            expansionBase.setClassGroup(state.getTeamGroup());
//        }
        String classGroup = ReportDateUtil.getClassNo(expansionTime);
        expansionBase.setClassGroup(classGroup);
        expansionBase.setSeries(series);
        expansionBase.setDeviceNo(dto.getDeviceCode());


        expansionBase.setExpansionTime(expansionTime);//扩断时间

        if (expansionTime != null) {
            String dateTime = DateUtil.formatDate(expansionTime, "yyyy-MM-dd");
            expansionBase.setDateTime(dateTime);
        }

        if (expansionBase.getStartTime() != null) {
            long runTimeLen = expansionBase.getExpansionTime().getTime() - expansionBase.getStartTime().getTime();
            double value = (runTimeLen / 1000d) / 60 / 60;
            BigDecimal runTime = new BigDecimal(value).setScale(2, BigDecimal.ROUND_DOWN);
            expansionBase.setRunTime(runTime);
        }

        String expansionType = EAPUtils.getEventValue(dto, "扩断类别");
        expansionBase.setExpansionType(expansionType);

        String expansionProcesses = EAPUtils.getEventValue(dto, "扩断工序");
        expansionBase.setExpansionProcesses(expansionProcesses);

        Integer repeatedInfoInteger = NumberUtil.String2Integer(EAPUtils.getEventValue(dto, "报表复投次数"), null);
        expansionBase.setRepeatedInfo(repeatedInfoInteger.toString());

        BigDecimal crystalPower = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶功率"));
        expansionBase.setCrystalPower(crystalPower);


        Date fjTime = null;
        try {
            String fjTimeStr = EAPUtils.getEventValue(dto, "进入放肩时间");
            fjTime = DateUtil.toDate(fjTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Date yjTime = null;
        try {
            String yjTimeStr = EAPUtils.getEventValue(dto, "进入引晶时间");
            yjTime = DateUtil.toDate(yjTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        if (fjTime != null && yjTime != null) {
            long crystalTimeLen = fjTime.getTime() - yjTime.getTime();
            double value = (crystalTimeLen / 1000d) / 60 / 60;
            BigDecimal crystalTime = new BigDecimal(value).setScale(2, BigDecimal.ROUND_DOWN);
            expansionBase.setCrystalTime(crystalTime);
        }


        CrystalPullingInfo info = crystalPullingInfoService.getByDeviceNo(dto.getDeviceCode());
        if (info != null) {
            BigDecimal materialCount = info.getMaterialCount();
            expansionBase.setFeedingQuantity(materialCount);
        }

        //计算班次
        String classNo = null;
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
        try {
            if (expansionTime != null) {
                Date nowTime = df.parse(df.format(expansionTime));
                Date dayTimeStart = df.parse("09:00:00");
                Date dayTimeEnd = df.parse("21:00:00");

                if (timeCalendar(nowTime, dayTimeStart, dayTimeEnd)) {
                    classNo = "白班";
                } else {
                    classNo = "夜班";
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        expansionBase.setClassNo(classNo);
        expansionBase.setLiquidLevelPixel(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径1000mm液面像素值", 1)));
        expansionBase.setLiquidCursor(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "8032", 1)));
        String dryPotType = EAPUtils.getStartCollectionEventValue(dto, "1196");
        expansionBase.setDryPotType(StringUtils.isNotBlank(dryPotType) && dryPotType.length() > 1 ? dryPotType.substring(dryPotType.length() - 2) : dryPotType);

        //扩断长度取值
        BigDecimal expansionLength = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "扩断长度"));
        expansionBase.setExpansionLength(expansionLength);

        //最新修改时间
        expansionBase.setUpdateTime(new Date());

        expansionBaseService.saveOrUpdate(expansionBase);
    }

    @Override
    public void brokenBractBase(ToMESData dto) throws Exception {
        Equipment equipment = equipmentService.findByCode(dto.getDeviceCode());
        if (null == equipment) {
            throw new RuntimeException("设备不存在");
        }

        if (StringUtils.isBlank(dto.getDeviceCode())) {
            throw new RuntimeException("MES未配置此炉台信息");
        }
        BrokenBractBase brokenBractBase = new BrokenBractBase();
        Date nowDate = new Date();
        brokenBractBase.setMonth(ReportDateUtil.getMonthTime(nowDate));
        brokenBractBase.setWeek(ReportDateUtil.getWeekTime(nowDate));
        brokenBractBase.setDay(ReportDateUtil.getDayTime(nowDate));
        Date startTime = null;
        try {
            startTime = dto.getStartTime();
            String startTimeStr = DateUtil.formatDate(startTime, "yyyy-MM-dd HH:mm:ss");
            brokenBractBase.setDateTime(startTimeStr); // 1.1 日期
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("处理startTime发生异常：" + e.getMessage());
        }

        String classGroup = ReportDateUtil.getClassNo(new Date());
        brokenBractBase.setClassGroup(classGroup); //1.2 班组

        //计算班次
        String classNo = null;
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
        try {
            if (startTime != null) {
                Date nowTime = df.parse(df.format(startTime));
                Date dayTimeStart = df.parse("08:30:00");
                Date dayTimeEnd = df.parse("20:30:00");

                if (timeCalendar(nowTime, dayTimeStart, dayTimeEnd)) {
                    classNo = "白班";
                } else {
                    classNo = "夜班";
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
            throw new Exception("计算班次发生异常：" + e.getMessage());
        }
        brokenBractBase.setClassNo(classNo);// 1.3班次

        //系列
        //片区
        FactoryModel factoryModel = factoryModelService.findByCode(equipment.getSeries());
        if(factoryModel != null){
            brokenBractBase.setSeries(factoryModel.getName().replace("片区","")); // 1.4系列
        }

        brokenBractBase.setDeviceNo(dto.getDeviceCode()); // 1.5 炉台号
        String lotNo = null;
        ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(dto.getDeviceCode(), Arrays.asList(1, 4, 5));
        if (current_productionSchedule != null) {
            lotNo = current_productionSchedule.getLotNo();
            brokenBractBase.setLotNo(lotNo);// 1.6炉次号
            ProductModel productModel = current_productionSchedule.getProductModel();
            if (productModel != null) {
                brokenBractBase.setSpecs(productModel.getSpecs());//1.7规格
            }
            if (null != current_productionSchedule.getProductionOrder()) {
                brokenBractBase.setWorkOrder(current_productionSchedule.getProductionOrder().getAufnr());//工单
            }
        }

        //未点开炉无生产工单(排程),去设备加工能力中取生产规格
        if (StringUtils.isBlank(brokenBractBase.getSpecs())) {
            MachineCapacity machineCapacity = machineCapacityService.findByEquipmentId(equipment.getId());
            if (machineCapacity != null) {
                MachineCapacityInfo machineCapacityInfo = machineCapacity.getCapacityInfo();
                if (machineCapacityInfo != null) {
                    brokenBractBase.setSpecs(machineCapacityInfo.getSpecification());//1.7规格
                }
            }
        }

        Date klTime = null;
        try {
            String klTimeStr = EAPUtils.getEventValue(dto, "开炉时间");
            if (null != klTimeStr && !"null".equals(klTimeStr)) {
                klTime = DateUtil.toDate(klTimeStr, "yyyy-MM-dd HH:mm:ss");
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }

        BigDecimal runTime = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "1038", 2));
        brokenBractBase.setRunTime(runTime); // 1.8 运行时间

        String brokenBractTypeStr = EAPUtils.getEventValue(dto, "断苞类别");
        brokenBractBase.setBrokenBractType(brokenBractTypeStr); // 1.9 断苞类别

        String LineBreakType = EAPUtils.getEventValue(dto, "断线类别");
        brokenBractBase.setLineBreakType(LineBreakType); // 断线类别

        if (LineBreakType != null) {
            brokenBractBase.setBrokenBractProcesses(LineBreakType.replace("断线", "")); // 1.9 断苞工序
        }

        String crystalLengthStr = (dto.getStartCollectionData().get("1274Max") != null ? dto.getStartCollectionData().get("1274Max").toString() : null);
        BigDecimal crystalLength = NumberUtil.Object2BigDecimal(crystalLengthStr);
        brokenBractBase.setCrystalLength(crystalLength); // 1.10 晶体长度

        String repeatedInfoStr = EAPUtils.getEventValue(dto, "报表复投次数", 0);
        brokenBractBase.setRepeatedInfo(repeatedInfoStr); // 1.11 复投次数

        BigDecimal crystalPower = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶功率"));
        brokenBractBase.setCrystalPower(crystalPower); // 1.12 引晶功率

        Date fjTime = null;  // 进入放肩时间
        try {
            String fjTimeStr = EAPUtils.getEventValue(dto, "进入放肩时间");
            if (null != fjTimeStr && !"null".equals(fjTimeStr))
                fjTime = DateUtil.toDate(fjTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Date yjTime = null;  // 进入引晶时间
        try {
            String yjTimeStr = EAPUtils.getEventValue(dto, "进入引晶时间");
            if (null != yjTimeStr && !"null".equals(yjTimeStr))
                yjTime = DateUtil.toDate(yjTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Date djTime = null;
        try {
            String djTimeStr = EAPUtils.getEventValue(dto, "进入等径时间");
            if (djTimeStr != null && !"null".equals(djTimeStr))
                djTime = DateUtil.toDate(djTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Date zjTime = null;  // 进入转肩时间
        try {
            String zjTimeStr = EAPUtils.getEventValue(dto, "进入转肩时间");
            if (null != zjTimeStr && !"null".equals(zjTimeStr))
                zjTime = DateUtil.toDate(zjTimeStr, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        if (fjTime != null && zjTime != null) {
            long shoulderTimeLen = zjTime.getTime() - fjTime.getTime();
            double value = (shoulderTimeLen / 1000d) / 60 / 60;
            BigDecimal crystalTime = new BigDecimal(value).setScale(2, BigDecimal.ROUND_DOWN);
            brokenBractBase.setShoulderTime(crystalTime); // 放肩时长
        }

        if (fjTime != null && yjTime != null) {
            long crystalTimeLen = fjTime.getTime() - yjTime.getTime();
            double value = (crystalTimeLen / 1000d) / 60 / 60;
            BigDecimal crystalTime = new BigDecimal(value).setScale(2, BigDecimal.ROUND_DOWN);
            brokenBractBase.setCrystalTime(crystalTime); // 1.13 引晶时间
        }

        CrystalPullingInfo info = crystalPullingInfoService.getByDeviceNo(dto.getDeviceCode());
        if (info != null) {
            BigDecimal materialCount = info.getMaterialCount();
            brokenBractBase.setFeedingQuantity(materialCount); // 1.14 投料量
        }
        //剩料重量
        BigDecimal residueWeight = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "3286"));
        brokenBractBase.setResidueWeight(residueWeight);

        //获取字典信息
        List<DictionaryItem> dictionaryItemList = dictionaryItemService.findByThemeValueAndItemName("brokenBractBaseArea", "step", true); // theme：断苞基础表长度区间 name：brokenBractBaseArea
        if (0 < dictionaryItemList.size()) {
            BigDecimal step = new BigDecimal(dictionaryItemList.get(0).getValue());
            if (crystalLength.compareTo(new BigDecimal(0.0d)) == 1) { // crystalLength > 0
                double divideNumber = crystalLength.divide(step).setScale(1, BigDecimal.ROUND_DOWN).doubleValue();
                Integer lowNumber = new BigDecimal(Math.floor(divideNumber)).multiply(step).setScale(0, BigDecimal.ROUND_DOWN).intValue();
                Integer highNumber = new BigDecimal(Math.ceil(divideNumber)).multiply(step).setScale(0, BigDecimal.ROUND_DOWN).intValue();
                if (lowNumber.intValue() == highNumber.intValue()) {
                    highNumber = lowNumber + step.setScale(0, BigDecimal.ROUND_DOWN).intValue();
                }
                brokenBractBase.setCrystalLengthArea(lowNumber.toString() + "-" + highNumber.toString()); // 1.15 长度区间
            }
        }
        brokenBractBase.setLiquidLevelPixel(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径1000mm液面像素值", 1)));
        brokenBractBase.setLiquidCursor(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "8032", 1)));
        String dryPotType = EAPUtils.getStartCollectionEventValue(dto, "1196");
        brokenBractBase.setDryPotType(StringUtils.isNotBlank(dryPotType) && dryPotType.length() > 1 ? dryPotType.substring(dryPotType.length() - 2) : dryPotType);

        //查询炉次已完成的罐料任务
        AgainTaskDetail againTaskDetail = againTaskDetailService.findSuccessByLotNo(lotNo);
        if (null != againTaskDetail) {
            AgainTask againTask = againTaskService.get(againTaskDetail.getAgainTaskId());
            if (null != againTask) {
                brokenBractBase.setAddTime(againTask.getAgainNum());
            }
        }

        //查询已完成的装料任务
        LoadingTask loadingTaskDb = loadingTaskService.findSuccessByLotNo(lotNo);
        if (null != loadingTaskDb) {
            if (null == brokenBractBase.getAddTime()) {
                brokenBractBase.setAddTime(0);
            }
            //查询装料坩埚
            List<BlastFurnaceDetail> blastFurnaceDetailList = blastFurnaceDetailService.getBlastFurnaceDetailList(loadingTaskDb.getId());
            BlastFurnaceDetail blastFurnaceDetail =
                    blastFurnaceDetailList.stream().filter(item ->
                                    StringUtils.contains(item.getMaterialName(), "坩埚"))
                            .max(Comparator.comparing(BlastFurnaceDetail::getId)).orElse(null);
            if (null != blastFurnaceDetail) {
                brokenBractBase.setCrucibleCode(blastFurnaceDetail.getBatchNo());
            }
        }

        Double speed = NumberUtil.Object2Double(EAPUtils.getEventValue(dto, "等径拉速"), null);
        Double speedSetting = NumberUtil.Object2Double(EAPUtils.getEventValue(dto, "等径设定拉速"), null);
        //拉速异常阈值
        Double setting = 10d;
        DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable("IsometricSpeedAbnormal", "异常阈值", true);
        if (null != dictionaryItem) {
            setting = NumberUtil.Object2Double(dictionaryItem.getValue(), 10d);
        }
        if (null != speed && null != speedSetting) {
            if (Math.abs(speed - speedSetting) > setting) {
                brokenBractBase.setIsometricSpeedAbnormal("是");
            } else {
                brokenBractBase.setIsometricSpeedAbnormal("暂定");
            }
        }

        //Integer shoulderNum = NumberUtil.Object2Integer(EAPUtils.getEventValue(dto, "第N次引晶"), null);
        brokenBractBase.setShoulderNum(1);
        BigDecimal crystalSurfacTemperature = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶起始液面温度"), null);
        brokenBractBase.setCrystalSurfacTemperature(crystalSurfacTemperature);
        BigDecimal leadCrystalLength = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶长度"), null);
        brokenBractBase.setLeadCrystalLength(leadCrystalLength);
        BigDecimal shoulderLength = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "放肩长度"), null);
        brokenBractBase.setShoulderLength(shoulderLength);
        BigDecimal crystalOneHundredSpeed = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶100MM拉速"), null);
        brokenBractBase.setCrystalOneHundredSpeed(crystalOneHundredSpeed);
        String IsShoulderManual = EAPUtils.getEventValue(dto, "是否手动放肩");
        brokenBractBase.setIsShoulderManual(IsShoulderManual);
        brokenBractBase.setIsometricStartTime(djTime);
        brokenBractBase.setShoulderStartTime(fjTime);
        brokenBractBase.setCrystalEndTemperature(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶最终液面温度"), null));
        brokenBractBase.setCrystalBeginPixel(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶起始液面像素值"), null));
        brokenBractBase.setCrystalLiquidDistancePrecise(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶液口距精测值"), null));
        brokenBractBase.setCrystalLiquidDistanceCoarse(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "引晶液口距粗测值"), null));
        BigDecimal isometricPower = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "等径起始功率"), null);
        brokenBractBase.setIsometricPower(isometricPower);
        BigDecimal isometricPowerMin = NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "等径最小功率"), null);
        brokenBractBase.setIsometricPowerMin(isometricPowerMin);
        if(isometricPowerMin != null && isometricPower != null){
            brokenBractBase.setIsometricPowerDecline(isometricPower.subtract(isometricPowerMin));
        }
        brokenBractBase.setIsometricPowerSetting(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "1263"), null));
        brokenBractBase.setShoulderDiameter(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "放肩最终直径"), null));
        brokenBractBase.setIsometricSpeedAverage(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "等径拉速"), null));
        brokenBractBase.setLiquidDistanceTarget(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "3092"), null));
        brokenBractBase.setLiquidDistanceActual(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "4073"), null));
        brokenBractBase.setSlenderNeckLength(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "3279"), null));
        brokenBractBase.setIsometricPressure(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "等径时炉压"), null));
        brokenBractBase.setIsometricPumpOpening(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "等径时干泵开度"), null));
        brokenBractBase.setIsometricArgonFlow(NumberUtil.Object2BigDecimal(EAPUtils.getEventValue(dto, "等径时氩气流量"), null));


        brokenBractBase.setToMesDataBody(JSONObject.toJSONString(dto).toString()); // 1.14 uap发送给mes报文内容
        String step = EAPUtils.getProcessStep(EAPUtils.getEventValue(dto, "虚拟工步"));
        String tcFjTimeStr = NumberUtil.object2String(EAPUtils.getEventValue(dto, "退出放肩时间"),null);

        // 放肩切手动不切转肩时，时间改为退出放肩时间
        if("放肩断线".equals(brokenBractBase.getLineBreakType()) && !"取肩".equals(step) && !StringUtils.isEmpty(tcFjTimeStr) && !"null".equals(tcFjTimeStr)){
            brokenBractBase.setDateTime(tcFjTimeStr); // 1.1 日期
        }
        brokenBractBaseService.saveOrUpdate(brokenBractBase);

        String LineBreakTypeV2 = "";
        Integer LineBreakTypeCount = NumberUtil.Object2Integer(EAPUtils.getEventValue(dto, "连续断线"),null);
        if(null != LineBreakTypeCount && LineBreakTypeCount == 1){
            LineBreakTypeV2 = "连续大于3次断线";
        }

        if ("连续大于3次断线".equals(LineBreakTypeV2)){
            BrokenBractBaseContinuous brokenBractBaseContinuous = modelMapper.map(brokenBractBase, BrokenBractBaseContinuous.class);
            brokenBractBaseContinuous.setBrokenBractType(LineBreakTypeV2); // 1.9 断苞类别
            brokenBractBaseContinuousService.saveOrUpdate(brokenBractBaseContinuous);
            String content = "设备:"+dto.getDeviceCode()+"连续大于3次断线推送";
            String groupName = "连续大于3次断线推送";
            String source = "连续大于3次断线推送";
            robotSeedMessage(content, groupName, source);
        }
        if ("放肩断线".equals(LineBreakType)) {
            StringBuilder content = getStringBuilder(LineBreakType, brokenBractBase);
            String groupName = "放肩断线";
            String source = "放肩断线";
            robotSeedMessage(content.toString(), groupName, source);
        } else if ("等径断线".equals(LineBreakType)) {
            StringBuilder content = getStringBuilder(LineBreakType, brokenBractBase);
            String groupName = "等径断线";
            String source = "等径断线";
            robotSeedMessage(content.toString(), groupName, source);
        }
    }

    private static StringBuilder getStringBuilder(String LineBreakType, BrokenBractBase brokenBractBaseContinuous) {
        StringBuilder content = new StringBuilder();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        content.append("["+formatter.format(new Date())+"]MES断线实时推送");
        content.append("\n");
        content.append(".断线类型:"+ LineBreakType);
        content.append("\n");
        content.append(".片区:"+ brokenBractBaseContinuous.getDeviceNo().substring(0,2));
        content.append("\n");
        content.append(".炉台号:"+ brokenBractBaseContinuous.getDeviceNo());
        content.append("\n");
        content.append(".加次:"+ brokenBractBaseContinuous.getAddTime());
        content.append("\n");
        content.append(".断线长度:"+brokenBractBaseContinuous.getCrystalLength());
        content.append("\n");
        content.append(".引晶功率:"+ brokenBractBaseContinuous.getCrystalPower());
        content.append("\n");
        content.append(".引晶开始液温:"+brokenBractBaseContinuous.getCrystalSurfacTemperature());
        content.append("\n");
        content.append(".引晶结束液温:"+ brokenBractBaseContinuous.getCrystalEndTemperature());
        content.append("\n");
        content.append(".引晶100mm拉速:"+ brokenBractBaseContinuous.getCrystalOneHundredSpeed());
        content.append("\n");
        content.append(".剩料量:"+brokenBractBaseContinuous.getResidueWeight());
        content.append("\n");
        content.append(".放肩高度:"+brokenBractBaseContinuous.getShoulderLength());
        content.append("\n");
        content.append(".放肩用时:"+brokenBractBaseContinuous.getShoulderTime());
        content.append("\n");
        content.append(".放肩直径:"+brokenBractBaseContinuous.getShoulderDiameter());
        content.append("\n");
        content.append(".等径功率:"+ brokenBractBaseContinuous.getIsometricPower());
        content.append("\n");
        content.append(".等径设定功率:"+brokenBractBaseContinuous.getIsometricPowerSetting());
        content.append("\n");
        return content;
    }

    @Override
    public void successCrystalBase(ToMESData dto) {

        //设备信息
        Equipment equipment = equipmentService.findByCode(dto.getDeviceCode());
        if (null == equipment) {
            throw new RuntimeException("设备不存在");
        }

        SuccessCrystalBase successCrystalBase = successCrystalBaseService.getByDeviceNoAndSpliceStartTime(equipment.getCode(), DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入熔接时间")));
        if (successCrystalBase == null) {
            successCrystalBase = new SuccessCrystalBase();
        }
        //炉次
        successCrystalBase.setLotNo(EAPUtils.getStartCollectionEventValue(dto, "1196"));
        successCrystalBase.setMoudle(equipment.getArea());
        successCrystalBase.setSeries(equipment.getSeries());
        successCrystalBase.setDeviceNo(equipment.getCode());
        successCrystalBase.setParameterName(EAPUtils.getStartCollectionEventValue(dto, "1417"));

        String againNum = "";
        BigDecimal bigDecimalAgainNum = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "报表复投次数"));
        if (null != bigDecimalAgainNum) {
            againNum = bigDecimalAgainNum.setScale(0, BigDecimal.ROUND_DOWN).toString();
        }
        successCrystalBase.setAgainNum(againNum);
        //熔接开始时间
        Date spliceStartTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入熔接时间"));
        successCrystalBase.setSpliceStartTime(spliceStartTime);
        successCrystalBase.setSpliceStartTemperature(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "熔接起始液面温度", 1)));
        successCrystalBase.setSpliceOverheatingTemperature(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "熔接过热液面温度", 1)));
        //过热完成时间
        Date spliceOverheatingTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "过热完成时间"));
        successCrystalBase.setSpliceOverheatingTime(spliceOverheatingTime);
        //过热时间
        if (null != spliceStartTime && null != spliceOverheatingTime) {
            BigDecimal time = BigDecimal.valueOf(hourBetween(spliceStartTime, spliceOverheatingTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
            successCrystalBase.setSpliceTime(time);
        }
        successCrystalBase.setSpliceBrokenTime(DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "熔接断时间")));
        //引晶开始时间
        Date crystalStartTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入引晶时间"));
        successCrystalBase.setCrystalStartTime(crystalStartTime);
        successCrystalBase.setCrystalPower(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶主加热功率", 1)));
        successCrystalBase.setCrystalLocation(EAPUtils.getStartCollectionEventValue(dto, "引晶埚位"));
        successCrystalBase.setCrystalResidueWeight(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶剩料重量", 1)));
        successCrystalBase.setCrystalStartTemperature(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶起始液面温度", 1)));
        successCrystalBase.setCrystalStartPixel(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶起始液面像素值", 1)));
        //引晶长度
        BigDecimal crystalLength = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶长度", 1));
        successCrystalBase.setCrystalLength(crystalLength);
        successCrystalBase.setCrystalEndTemperature(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶最终液面温度", 1)));
        //引晶50mm长度时间
        Date crystalFiftyTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "引晶50mm长度时间"));
        successCrystalBase.setCrystalFiftyTime(crystalFiftyTime);

        //进入引断时间
        Date crystalBrokenTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入引断时间"));
        //放肩开始时间
        Date shoulderingStartTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入放肩时间"));
        //引晶时间
        BigDecimal CrystalTime = null;
        if (null == crystalBrokenTime) {
            if (null != shoulderingStartTime && null != crystalStartTime) {
                CrystalTime = BigDecimal.valueOf(hourBetween(crystalStartTime, shoulderingStartTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
            }
        } else {
            if (null != crystalStartTime) {
                CrystalTime = BigDecimal.valueOf(hourBetween(crystalStartTime, crystalBrokenTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
            }
        }
        successCrystalBase.setCrystalTime(CrystalTime);
        if (null != CrystalTime && null != crystalLength && !(CrystalTime.compareTo(BigDecimal.ZERO) == 0)) {
            successCrystalBase.setCrystalSpeed(crystalLength.divide(CrystalTime, 2, BigDecimal.ROUND_HALF_UP));
        }

        BigDecimal crystal15Length = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "15min晶体长度", 1));
        if (crystal15Length != null) {
            successCrystalBase.setCrystalStartAverageSpeed(crystal15Length.divide(new BigDecimal(0.25), 2, BigDecimal.ROUND_HALF_UP));
        }

        if (null != crystalFiftyTime && null != crystalStartTime) {
            BigDecimal time = BigDecimal.valueOf(hourBetween(crystalStartTime, crystalFiftyTime)).setScale(1, BigDecimal.ROUND_HALF_UP);
            if (!(time.compareTo(BigDecimal.ZERO) == 0)) {
                successCrystalBase.setCrystalEndAverageSpeed(new BigDecimal(50).divide(time.multiply(new BigDecimal(60)), 2, BigDecimal.ROUND_HALF_UP));
            }
        }

        successCrystalBase.setCrystalEndSpeed(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶最终拉速", 2)));
        successCrystalBase.setCrystalBrokenTime(crystalBrokenTime);
        successCrystalBase.setCrystalWithManual(EAPUtils.getStartCollectionEventValue(dto, "手动引晶").equals("false") ? 0 : 1);
        successCrystalBase.setShoulderingStartTime(shoulderingStartTime);
        successCrystalBase.setShoulderingSixtyLength(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩直径60mm对应长度", 1)));
        successCrystalBase.setShoulderingSixtyDiameter(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩长度60mm对应直径", 1)));
        //放肩扩断时间
        Date shoulderingBrokenTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "放肩扩断时间"));
        //转肩开始时间
        Date shouderCircuitStartTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入转肩时间"));
        //放肩时间
        BigDecimal shoulderingTime = null;
        if (null != shouderCircuitStartTime) {
            if (null != shoulderingStartTime) {
                shoulderingTime = BigDecimal.valueOf(hourBetween(shoulderingStartTime, shouderCircuitStartTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
            }
        } else {
            if (null != shoulderingBrokenTime && null != shoulderingStartTime) {
                shoulderingTime = BigDecimal.valueOf(hourBetween(shoulderingStartTime, shoulderingBrokenTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
            }
        }
        successCrystalBase.setShoulderingBrokenTime(shoulderingBrokenTime);
        successCrystalBase.setShoulderingDiameter(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩扩断直径", 1)));
        successCrystalBase.setShoulderingFinishPixel(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩完成液面像素值", 1)));
        successCrystalBase.setShoulderingEndDiameter(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩最终直径", 1)));
        //放肩长度
        BigDecimal shoulderingLength = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩长度", 1));
        successCrystalBase.setShoulderingLength(shoulderingLength);
        //放肩长度小于十毫引断
        if (null != shoulderingLength) {
            if (shoulderingLength.compareTo(new BigDecimal(10)) != -1) {
                successCrystalBase.setCrystalWithBroken(0);
            } else {
                successCrystalBase.setCrystalWithBroken(1);
            }
        }
        //计算大角
        BigDecimal lengthBigAngle = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩直径200mm对应长度"));
        BigDecimal diameterBigAngle = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩长度180mm对应直径"));
        successCrystalBase.setShoulderingBigAngle(getBigAngleOrSmallAngle(shoulderingLength, successCrystalBase.getShoulderingEndDiameter(), 2));
        //计算小角
        BigDecimal lengthSmallAngle = new BigDecimal(60);
        BigDecimal diameterSmallAngle = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩长度60mm对应直径"));
        successCrystalBase.setShoulderingSmallAngle(getBigAngleOrSmallAngle(lengthSmallAngle, diameterSmallAngle, 1));
//        successCrystalBase.setShoulderingBigAngle(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto,"大角",2)));
//        successCrystalBase.setShoulderingSmallAngle(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto,"小角",2)));
        successCrystalBase.setShoulderingWeight(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "放肩重量", 1)));
        successCrystalBase.setShoulderingTime(shoulderingTime);

        successCrystalBase.setShouderCircuitStartTime(shouderCircuitStartTime);
        successCrystalBase.setShouderCircuitTopSpeed(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "转肩最大拉速", 2)));
        successCrystalBase.setShouderCircuitStartDiameter(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "转肩启动直径", 1)));
        successCrystalBase.setShouderCircuitWeight(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "转肩晶体重量", 1)));
        //转肩扩断时间
        Date shouderCircuitBrokenTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "转肩扩断时间"));
        //等径开始时间
        Date isometricStartTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入等径时间"));
        //等径时间
        BigDecimal shouderCircuitTime = null;
        if (null == shouderCircuitBrokenTime) {
            if (null != isometricStartTime && null != shouderCircuitStartTime) {
                shouderCircuitTime = BigDecimal.valueOf(hourBetween(shouderCircuitStartTime, isometricStartTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
            }
        } else {
            if (null != shouderCircuitStartTime) {
                shouderCircuitTime = BigDecimal.valueOf(hourBetween(shouderCircuitStartTime, shouderCircuitBrokenTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
            }
        }
        successCrystalBase.setShouderCircuitBrokenTime(shouderCircuitBrokenTime);
        successCrystalBase.setShouderCircuitTime(shouderCircuitTime);
        successCrystalBase.setIsometricStartTime(isometricStartTime);
        successCrystalBase.setIsometricFiftyDistance(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径50mm液口距", 1)));
        successCrystalBase.setIsometricStartPower(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径起始功率", 1)));
        successCrystalBase.setIsometricThousandPower(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径1000mm功率", 1)));
        successCrystalBase.setIsometricStartPixel(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径起始液面像素值", 1)));
        successCrystalBase.setIsometricFiveHundredPixel(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径500mm液面像素值", 1)));
        successCrystalBase.setIsometricStartDiameter(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径起始直径", 1)));
        //等径100mm时间
        Date isometricOneHundredTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "等径100mm时间"));
        successCrystalBase.setIsometricOneHundredTime(isometricOneHundredTime);
        if (null != isometricStartTime && null != isometricOneHundredTime) {
            BigDecimal hour = BigDecimal.valueOf(hourBetween(isometricStartTime, isometricOneHundredTime)).setScale(1, BigDecimal.ROUND_HALF_UP);
            if (!(hour.compareTo(BigDecimal.ZERO) == 0)) {
                successCrystalBase.setIsometricOneHundredSpeed(new BigDecimal(100).divide(hour, 2, BigDecimal.ROUND_HALF_UP));
            }
        }
        //等径500mm时间
        Date isometricFiveHundredTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "等径500mm时间"));
        successCrystalBase.setIsometricFiveHundredTime(isometricFiveHundredTime);
        if (null != isometricStartTime && null != isometricFiveHundredTime) {
            BigDecimal hour = BigDecimal.valueOf(hourBetween(isometricStartTime, isometricFiveHundredTime)).setScale(1, BigDecimal.ROUND_HALF_UP);
            if (!(hour.compareTo(BigDecimal.ZERO) == 0)) {
                successCrystalBase.setIsometricFiveHundredSpeed(new BigDecimal(500).divide(hour, 2, BigDecimal.ROUND_HALF_UP));
            }
        }

        //等径长度
        BigDecimal isometricLength = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "1274Max", 1));
        successCrystalBase.setIsometricLength(isometricLength);
        //等径长度小于50mm扩断
        if (null != isometricLength && shoulderingLength != null) {
            if ((isometricLength.compareTo(new BigDecimal(50)) == -1) && (shoulderingLength.compareTo(new BigDecimal(10)) != -1)) {
                successCrystalBase.setShoulderingWithBroken(1);
            } else {
                successCrystalBase.setShoulderingWithBroken(0);
            }
        }

        Integer isometricFiveHundredWithBroken = null;
        BigDecimal isometricWithOnce = new BigDecimal(500);
        //按照等径长度判断
        if (null != isometricLength) {
            isometricFiveHundredWithBroken = ((isometricLength.compareTo(new BigDecimal(50)) == 1) && (isometricLength.compareTo(isometricWithOnce) == -1)) ? 1 : 0;
        }
        successCrystalBase.setIsometricFiveHundredWithBroken(isometricFiveHundredWithBroken);

        //若当前炉台当前复投次数  拉出的第一根单晶  且长度大于500mm为一次成晶
        //获取字典信息（一次成晶）
        List<DictionaryItem> dictionaryItemList = dictionaryItemService.findByThemeValueAndItemName("CrystallineJudge", "一次成晶", true);
        if (0 < dictionaryItemList.size()) {
            isometricWithOnce = new BigDecimal(dictionaryItemList.get(0).getValue());
        }
        List<SuccessCrystalBase> list = successCrystalBaseService.findByDeviceNoAndAgainNum(equipment.getCode(), EAPUtils.getStartCollectionEventValue(dto, "报表复投次数"));
        if (list != null && list.size() <= 0 && null != isometricLength && isometricLength.compareTo(isometricWithOnce) == 1) {
            successCrystalBase.setIsometricWithOnce(1);
        } else {
            successCrystalBase.setIsometricWithOnce(0);
        }

        //获取字典信息（一次整棒）
        BigDecimal fullWithOnce = new BigDecimal(3300);
        List<DictionaryItem> dictionaryItemList1 = dictionaryItemService.findByThemeValueAndItemName("CrystallineJudge", "一次整棒", true);
        if (0 < dictionaryItemList1.size()) {
            fullWithOnce = new BigDecimal(dictionaryItemList1.get(0).getValue());
        }
        if (list != null && list.size() <= 0 && null != isometricLength && isometricLength.compareTo(fullWithOnce) == 1) {
            successCrystalBase.setFullWithOnce(1);
        } else {
            successCrystalBase.setFullWithOnce(0);
        }

        //等径用时
        BigDecimal isometricTime = null;
        //等径结束时间
        Date isometricEndTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "结束等径时间"));
        if (null != isometricEndTime && null != isometricStartTime) {
            isometricTime = BigDecimal.valueOf(hourBetween(isometricStartTime, isometricEndTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
            if (isometricLength != null && isometricTime != null && !(isometricTime.compareTo(BigDecimal.ZERO) == 0)) {
                successCrystalBase.setIsometricAverageSpeed(isometricLength.divide(isometricTime.multiply(new BigDecimal(60)), 2, BigDecimal.ROUND_HALF_UP));
            }
        }

        successCrystalBase.setIsometricWeight(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "等径晶体重量", 1)));
        successCrystalBase.setIsometricEndTime(isometricEndTime);
        successCrystalBase.setIsometricTime(isometricTime);
        successCrystalBase.setSectionResidueWeight(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "取段剩料重量", 1)));
        //进入收尾时间
        Date finishingStartTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入收尾时间"));
        successCrystalBase.setFinishingStartTime(finishingStartTime);
        successCrystalBase.setFinishingLength(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "尾长度", 1)));
        //进入收尾时间
        Date finishingEndTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "结束收尾时间"));
        successCrystalBase.setFinishingEndTime(finishingEndTime);

        //收尾用时
        BigDecimal finishingTime = null;
        if (null != finishingEndTime && null != finishingStartTime) {
            finishingTime = BigDecimal.valueOf(hourBetween(finishingStartTime, finishingEndTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
        }
        successCrystalBase.setFinishingTime(finishingTime);
        successCrystalBase.setFinishingWeight(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "收尾重量", 1)));
        //进入取段稳温时间
        Date sectionStartTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "进入取段稳温时间"));
        successCrystalBase.setSectionStartTime(sectionStartTime);
        //结束取段稳温时间
        Date sectionEndTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "结束取段稳温时间"));
        successCrystalBase.setSectionEndTime(sectionEndTime);
        //收尾用时
        BigDecimal sectionTime = null;
        if (null != sectionStartTime && null != sectionEndTime) {
            sectionTime = BigDecimal.valueOf(hourBetween(sectionStartTime, sectionEndTime)).setScale(2, BigDecimal.ROUND_HALF_UP);
        }
        successCrystalBase.setSectionTime(sectionTime);
        successCrystalBase.setSectionWeight(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "取段稳温晶体重量", 1)));
        successCrystalBase.setCreateTime(new Date());

        successCrystalBaseService.saveOrUpdate(successCrystalBase);
    }

    /**
     * 获取相差小时数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static Double hourBetween(Date startDate, Date endDate) {
        Double hour = 0d;
        if (startDate.after(endDate)) {
            return hour;
        }
        long startTime = startDate.getTime();
        long endTime = endDate.getTime();

        hour = ((endTime - startTime) / 1000d) / 60 / 60;
        return hour;
    }

    /**
     * 获取大小角
     * <p>
     * 小角度定义：取肩长 c60mm 时，作出通过顶点的三角，求出三角型 a 顶点的角度，此 a 顶
     * 点的角度定义为小角度。 (公式：小角=(ATAN(直径值/2/晶体长度)/π*180)*2)
     * <p>
     * 大角度定义：取肩长 d60mm 时，以中心线为顶点，取到转肩直径的放肩高度 d120mm 时，
     * 过顶点通过转肩直径 200mm 时作三角形，与顶点形成的角定义为大角度。(公式：大角=(ATAN(直径值/2/(晶体长度-60))/π*180)*2)
     *
     * @param lenth    晶体长度(大角计算:放肩长度 ;小角计算:60mm)
     * @param diameter 直径值(大角计算:放肩最终直径;小角计算:放肩长度60mm对应直径)
     * @param type     1获取小角 2 获取大角
     * @return
     */
    public BigDecimal getBigAngleOrSmallAngle(BigDecimal lenth, BigDecimal diameter, int type) {
        BigDecimal result = null;
        try {
            if (lenth == null || diameter == null || lenth.compareTo(BigDecimal.ZERO) == 0) {
                return result;
            }
            double lenthDouble = lenth.doubleValue();
            double diameterDouble = diameter.doubleValue();
            switch (type) {
                case 1:
                    result = new BigDecimal(Double.toString(Math.atan(diameterDouble / 2 / lenthDouble) / Math.PI * 180));
                    result = result.multiply(new BigDecimal(2)).setScale(2, BigDecimal.ROUND_HALF_UP);
                    break;
                case 2:
                    result = new BigDecimal(Double.toString(Math.atan(diameterDouble / 2 / (lenthDouble - 60)) / Math.PI * 180));
                    result = result.multiply(new BigDecimal(2)).setScale(2, BigDecimal.ROUND_HALF_UP);
                    break;
            }
        } catch (Exception e) {

        }
        return result;
    }

    @Override
    public void crystalBrokenBase(ToMESData dto) {
        Equipment equipment = equipmentService.findByCode(dto.getDeviceCode());
        if (equipment == null) {
            throw new RuntimeException("MES未配置此炉台信息");
        }

        CrystalBrokenBase crystalBrokenBase = new CrystalBrokenBase();
        Date breakTakeTime = DateUtil.toDate(EAPUtils.getStartCollectionEventValue(dto, "引晶断时间"));
        if (breakTakeTime == null) {
            breakTakeTime = new Date();
        }
        crystalBrokenBase.setDateTime(breakTakeTime);
//        EquipmentExtendState state = equipmentExtendStateService.getByCode(dto.getDeviceCode());
//        if (state != null) {
//            crystalBrokenBase.setClassGroup(state.getTeamGroup()); //1.2 班组
//        }
        String classGroup = ReportDateUtil.getClassNo(breakTakeTime);
        crystalBrokenBase.setClassGroup(classGroup); //1.2 班组

        //计算班次
        String classNo = null;
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
        try {
            if (breakTakeTime != null) {
                Date nowTime = df.parse(df.format(breakTakeTime));
                Date dayTimeStart = df.parse("09:00:00");
                Date dayTimeEnd = df.parse("21:00:00");
                if (timeCalendar(nowTime, dayTimeStart, dayTimeEnd)) {
                    classNo = "白班";
                } else {
                    classNo = "夜班";
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        crystalBrokenBase.setClassNo(classNo);// 1.3班次
        crystalBrokenBase.setSeries(equipment.getSeries()); // 1.4系列
        crystalBrokenBase.setDeviceNo(dto.getDeviceCode()); // 1.5 炉台号
        String lotNo = null;
        ProductionSchedule current_productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(dto.getDeviceCode(), Arrays.asList(1, 4, 5));
        if (current_productionSchedule != null) {
            lotNo = current_productionSchedule.getLotNo();
            crystalBrokenBase.setLotNo(lotNo);// 1.6炉次号
        }

        String againNum = "";
        BigDecimal bigDecimalAgainNum = NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "报表复投次数"));
        if (null != bigDecimalAgainNum) {
            againNum = bigDecimalAgainNum.setScale(0, BigDecimal.ROUND_DOWN).toString();
        }
        crystalBrokenBase.setAgainNum(againNum);
        crystalBrokenBase.setCrystalLength(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶长度", 2)));
        crystalBrokenBase.setCrystalResidueWeight(NumberUtil.Object2BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "引晶剩料重量", 2)));
        crystalBrokenBase.setCreateBy("eap");
        crystalBrokenBase.setCreateTime(new Date());

        crystalBrokenBaseService.saveOrUpdate(crystalBrokenBase);
    }


    @Override
    public void materialPrepareSignal(ToMESData dto) {

        Date current = new Date();

        if (dto.getTaskCode().equals("ShutdownFurnace")) {//停炉处理
            DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable("MMCSPrepare", "chargeDelayed", true);//拆清装料延时时间(h)

//        	ProductionSchedule productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(dto.getDeviceCode(), EnumValuesServer.ProductionScheduleStatus.未开始.getKey());
//        	ProductModel productMode = productionSchedule.getProductModel();

            if (dictionaryItem == null)
                return;


            int stepMinute = (int) (Double.valueOf(dictionaryItem.getValue()) * 60);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(current);
            calendar.add(Calendar.MINUTE, stepMinute);
            Date estimate = calendar.getTime();


            MmcsDistributionPlan mcs = new MmcsDistributionPlan();
            mcs.setCreateTime(current);
            mcs.setDisconnectOrNot(EAPUtils.getStartCollectionEventValue(dto, "是否断取"));
            mcs.setEquipmentCode(dto.getDeviceCode());
            mcs.setEstimate(estimate);
            mcs.setResidualWeight(EAPUtils.getStartCollectionEventValue(dto, "3286"));
            mcs.setSended(false);
            mcs.setType("0");
            mmcsDistributionPlanService.saveOrUpdate(mcs);

        } else if (dto.getTaskCode().equals("VacuumPumping")) {
            DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable("MMCSPrepare", "initialDelayed", true);//初始复投延时时间(h)


            if (dictionaryItem == null)
                return;


            int stepMinute = (int) (Double.valueOf(dictionaryItem.getValue()) * 60);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(current);
            calendar.add(Calendar.MINUTE, stepMinute);
            Date estimate = calendar.getTime();


            MmcsDistributionPlan mcs = new MmcsDistributionPlan();
            mcs.setCreateTime(current);
            mcs.setDisconnectOrNot(EAPUtils.getStartCollectionEventValue(dto, "是否断取"));
            mcs.setEquipmentCode(dto.getDeviceCode());
            mcs.setEstimate(estimate);
            mcs.setResidualWeight(EAPUtils.getStartCollectionEventValue(dto, "3286"));
            mcs.setSended(false);
            mcs.setType("1");
            mmcsDistributionPlanService.saveOrUpdate(mcs);


        } else if (dto.getTaskCode().equals("取段投料发送")) {
            DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable("MMCSPrepare", "takeSectionDelayed", true);//取段复投延时时间(h)


            if (dictionaryItem == null)
                return;


            int stepMinute = (int) (Double.valueOf(dictionaryItem.getValue()) * 60);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(current);
            calendar.add(Calendar.MINUTE, stepMinute);
            Date estimate = calendar.getTime();


            MmcsDistributionPlan mcs = new MmcsDistributionPlan();
            mcs.setCreateTime(current);
            mcs.setDisconnectOrNot(EAPUtils.getStartCollectionEventValue(dto, "是否断取"));
            mcs.setEquipmentCode(dto.getDeviceCode());
            mcs.setEstimate(estimate);
            mcs.setResidualWeight(EAPUtils.getStartCollectionEventValue(dto, "3286"));
            mcs.setSended(false);
            mcs.setType("2");
            mmcsDistributionPlanService.saveOrUpdate(mcs);
        }
    }

    @Override
    public List<MaterialTaskDto> materialOperationList(MaterialOperationDto dto) {
        List<MaterialTaskDto> taskListDto = new ArrayList<>();
        Integer type = dto.getType();
        String lotNo = dto.getLotNo();
        List<LoadingTask> loadingTasks;
        List<AgainTask> againTasks = new ArrayList<>();
        List<AgainTaskDetail> againTaskDetails;

        //0:取消叫料
        if (type == 0) {
            Collection<SearchFilter> searchFilters = new HashSet<>();
            searchFilters.add(new SearchFilter("lotNo", SearchFilter.Operator.EQ, lotNo));
            searchFilters.add(new SearchFilter("rawStatus", SearchFilter.Operator.IN, new String[]{"0"}));
            Specification<LoadingTask> specification = DynamicSpecifications.bySearchFilter(LoadingTask.class, searchFilters);
            loadingTasks = loadingTaskService.findByExampleSort(specification, null);
            Specification<AgainTask> specification1 = DynamicSpecifications.bySearchFilter(AgainTask.class, searchFilters);
            againTasks = againTaskService.findByExampleSort(specification1, null);
            Specification<AgainTaskDetail> specification2 = DynamicSpecifications.bySearchFilter(AgainTaskDetail.class, searchFilters);
            againTaskDetails = againTaskDetailService.findByExampleSort(specification2, null);
            //1:生产退料
        } else if (type == 1) {
            Collection<SearchFilter> searchFilters = new HashSet<>();
            searchFilters.add(new SearchFilter("lotNo", SearchFilter.Operator.EQ, lotNo));
            searchFilters.add(new SearchFilter("rawStatus", SearchFilter.Operator.IN, new String[]{"3", "4"}));
            Specification<LoadingTask> specification = DynamicSpecifications.bySearchFilter(LoadingTask.class, searchFilters);
            loadingTasks = loadingTaskService.findByExampleSort(specification, null);
            Specification<AgainTaskDetail> specification2 = DynamicSpecifications.bySearchFilter(AgainTaskDetail.class, searchFilters);
            againTaskDetails = againTaskDetailService.findByExampleSort(specification2, null);
            //2重新叫料
        } else {
            Collection<SearchFilter> searchFilters = new HashSet<>();
            searchFilters.add(new SearchFilter("lotNo", SearchFilter.Operator.EQ, lotNo));
            searchFilters.add(new SearchFilter("rawStatus", SearchFilter.Operator.IN, new String[]{"2", "5"}));
            Specification<LoadingTask> specification = DynamicSpecifications.bySearchFilter(LoadingTask.class, searchFilters);
            loadingTasks = loadingTaskService.findByExampleSort(specification, null);
            Specification<AgainTask> specification1 = DynamicSpecifications.bySearchFilter(AgainTask.class, searchFilters);
            againTasks = againTaskService.findByExampleSort(specification1, null);
            Specification<AgainTaskDetail> specification2 = DynamicSpecifications.bySearchFilter(AgainTaskDetail.class, searchFilters);
            againTaskDetails = againTaskDetailService.findByExampleSort(specification2, null);
        }
        if (null != loadingTasks && loadingTasks.size() > 0) {
            for (LoadingTask loadingTask : loadingTasks) {
                MaterialTaskDto materialTaskDto = new MaterialTaskDto();
                materialTaskDto.setId(loadingTask.getId());
                materialTaskDto.setType("配料任务");
                materialTaskDto.setWeight(loadingTask.getRealWeight());
                taskListDto.add(materialTaskDto);
            }
        }
        if (null != againTasks && againTasks.size() > 0) {
            for (AgainTask againTask : againTasks) {
                MaterialTaskDto materialTaskDto = new MaterialTaskDto();
                materialTaskDto.setId(againTask.getId());
                materialTaskDto.setType("复投任务");
                Integer againNum = againTask.getAgainNum();
                DictionaryItem item = dictionaryItemService.findByThemeValueAndValueAndEnable("RecipeUsage", againNum.toString(), true);
                materialTaskDto.setAgainNum(item.getName());
                materialTaskDto.setBucketSum(againTask.getAgainPailNum());
                materialTaskDto.setWeight(againTask.getAgainWeigth());
                taskListDto.add(materialTaskDto);
            }
        }
        if (null != againTaskDetails && againTaskDetails.size() > 0) {
            for (AgainTaskDetail againTaskDetail : againTaskDetails) {
                MaterialTaskDto materialTaskDto = new MaterialTaskDto();
                materialTaskDto.setId(againTaskDetail.getId());
                materialTaskDto.setType("灌料任务");
                Integer againNum = againTaskDetail.getAgainNum();
                DictionaryItem item = dictionaryItemService.findByThemeValueAndValueAndEnable("RecipeUsage", againNum.toString(), true);
                materialTaskDto.setAgainNum(item.getName());
                materialTaskDto.setBucketNum(againTaskDetail.getPailNum());
                materialTaskDto.setBucketSum(againTaskDetail.getAgainPailNum());
                materialTaskDto.setWeight(againTaskDetail.getAgainRealWeight());
                taskListDto.add(materialTaskDto);
            }
        }
        return taskListDto;
    }

    @Override
    public void materialOperationAction(MaterialOperationActionDto dto) {
        Integer type = dto.getActionType();
        Long id = dto.getId();
        String cause = dto.getCause();
        //取消叫料
        if (type == 0 || type == 1 || type == 2 || type == 3 || type == 4) {
            if (StringUtils.isBlank(cause)) {
                throw new BusinessException("未填写原因！");
            }
        }
        //生产退料
        //操作类型(0:取消叫料配料,1:取消叫料复投,2:取消叫料灌料,3:生产退料配料,4:生产退料灌料,5:重新叫料配料,6:重新叫料复投,7:重新叫料灌料)
        switch (type) {
            case 0:
                loadingTaskService.cancelTask(id, cause);
                break;
            case 1:
                againTaskService.cancelTask(id, cause);
                break;
            case 2:
                againTaskDetailService.cancelTask(id, cause);
                break;
            case 3:
                loadingTaskService.backTask(id, cause);
                break;
            case 4:
                againTaskDetailService.backTask(id, cause, 3);
                break;
            case 5:
                loadingTaskService.repeatOpenTask(id);
                break;
            case 6:
                againTaskService.repeatOpenTask(id);
                break;
            case 7:
                againTaskDetailService.repeatOpenTask(id);
                break;
            default:
                break;
        }


    }

    @Override
    public Result updateLastLongCrstalWeight(LongCrystalVO longCrystalVO) {
        return longCrystalService.updateLastLongCrstalWeight(longCrystalVO);
    }

    @Override
    public void surplusReceive(ToMESData dto) {

        //剩余重量
        BigDecimal residualWeight = StringUtils.isBlank(EAPUtils.getStartCollectionEventValue(dto, "3286"))
                ? BigDecimal.ZERO : new BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "3286"));

        //单晶重量
        BigDecimal weight = StringUtils.isBlank(EAPUtils.getStartCollectionEventValue(dto, "3287"))
                ? BigDecimal.ZERO : new BigDecimal(EAPUtils.getStartCollectionEventValue(dto, "3287"));

        String surplusType = dto.getTaskCode();
        String deviceNo = dto.getDeviceCode();

        //查询工单号,炉次号,加料次数
        String lotNo = null;
        String workOrderNo = null;
        //片区(工作中心编码)
        String field4 = null;
        ProductionSchedule productionSchedule = null;
        //1 转换类型
        //埚底/焖炉 允许多条录入开关-0: 开 1: 关 ;默认关闭1
        String bottomOrFurnaceSwitch = "1";
        //埚底,允许焖炉转换的开关 0开 1关
        String typeChangeSwitch ="1";
        if (SurplusStepEnum.StepNameEnum.埚底焖炉.getValue().equals(surplusType)) {
            //1.1根据字典配置区分是埚底/焖炉
            DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable("bottomOrFurnace", "埚底焖炉", true);
            if (null == dictionaryItem || StringUtils.isBlank(dictionaryItem.getValue())) {
                throw new BusinessException("请维护关键字[bottomOrFurnace]字典!");
            }
            bottomOrFurnaceSwitch = dictionaryItem.getExtendValue1();
            typeChangeSwitch = dictionaryItem.getExtendValue2();
            if ("0".equals(typeChangeSwitch)) {
                if (residualWeight.compareTo(new BigDecimal(dictionaryItem.getValue())) > 0) {
                    surplusType = SurplusStepEnum.StepNameEnum.焖炉.getValue();
                } else {
                    surplusType = SurplusStepEnum.StepNameEnum.埚底.getValue();
                }
            }
            //埚底/焖炉 查找最近一次的已关闭的排程工单
            productionSchedule = productionScheduleService.findCloseByEquipmentCode(deviceNo, 2);
            if (productionSchedule == null) {
                throw new BusinessException("收集类型为:" + surplusType + ":该炉台无关闭的排程工单!");
            }
        } else {
            productionSchedule = productionScheduleService.findTopByDeviceNoAndStatusOrderByIdDesc(deviceNo, Arrays.asList(1, 4, 5));
        }
        //2 余料落库
        if (null != productionSchedule) {
            lotNo = productionSchedule.getLotNo();
            if (null != productionSchedule.getProductionOrder()) {
                workOrderNo = productionSchedule.getProductionOrder().getAufnr();
                field4 = productionSchedule.getProductionOrder().getField4();
            }
        }
        Integer againNum = null;
        AgainTaskDetail againTaskDetail = againTaskDetailService.findTopByEquipmentCodeOrderByIdDesc(deviceNo);
        if (null != againTaskDetail) {
            AgainTask againTask = againTaskService.get(againTaskDetail.getAgainTaskId());
            if (null != againTask) {
                againNum = againTask.getAgainNum();
            }
        }
        if (StringUtils.isBlank(lotNo) || null == againNum || StringUtils.isBlank(workOrderNo)) {
            throw new BusinessException("设备" + deviceNo + ":炉次" + lotNo + "加料次数" + againNum + "工单" + workOrderNo + "未查询到!");
        }
        DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable("ResidualType", surplusType, true);
        if (null == dictionaryItem || StringUtils.isBlank(dictionaryItem.getValue())) {
            throw new BusinessException("请维护关键字[ResidualType]字典!");
        }
        String residualTypeValue = dictionaryItem.getValue();
        String directionalSign =  productionSchedule.getProductionOrder().getZorientationind();
        String directionalSignValue = "B";
        if (StringUtils.isBlank(directionalSign)) {
            directionalSign = "非定向";
        }
        DictionaryItem directionalSignValueResult = dictionaryItemService.findByThemeValueAndNameAndEnable("creatCrystalNo", directionalSign, true);
        if (null != directionalSignValueResult) {
            if (directionalSignValueResult.getValue().length() > 1) {
                log.info("字典关键字[creatCrystalNo]词条:实际值 [" + directionalSign + "配置值异常,必须一位字符!");
            }
            directionalSignValue = directionalSignValueResult.getValue();
        } else {
            log.info("字典关键字[creatCrystalNo]词条:实际值 [" + directionalSign + "未配置!");
        }
        String surplusCode =directionalSignValue+ lotNo + againNum + residualTypeValue + workOrderNo;
        String surplusCodeNo = surplusCode + recycledBatchGenerator.buildSurplusBatchNo(surplusCode);
        SurplusInfo surplusInfo = new SurplusInfo();
        surplusInfo.setDeviceNo(deviceNo)
                .setSurplusCode(surplusCodeNo)
                .setWeight(weight)
                .setSurplusType(surplusType)
                .setLotNo(lotNo)
                .setWorkOrderNo(workOrderNo)
                .setArea(field4)
                .setFeedTimes(againNum)
                .setStatus(SurplusInfoStatusEnum.待入库.getKey())
                .setDel(SurplusInfoStatusEnum.未删除.getKey())
                .setDataType(SurplusInfoStatusEnum.EAP自动采集.getKey())
                .setCreateBy(SurplusInfoStatusEnum.EAP自动采集.getValue())
                .setCreateTime(new Date());
        SurplusInfo oldSurplusInfo = surplusInfoService.queryLimitInfoBySurplusCode(surplusCodeNo);
        if (null != oldSurplusInfo) {
            throw new BusinessException("该编号: [" + surplusCodeNo + "] 余料数据已存在");
        }
        //卡控一个订单只能有一个坩埚或焖炉类型的数据
        if ("1".equals(bottomOrFurnaceSwitch) && (SurplusStepEnum.StepNameEnum.埚底.getValue().equals(surplusType) || SurplusStepEnum.StepNameEnum.焖炉.getValue().equals(surplusType))) {
            List<String> strings = Arrays.asList(SurplusStepEnum.StepNameEnum.埚底.getValue(), SurplusStepEnum.StepNameEnum.焖炉.getValue());
            SurplusInfo checkSurplusInfo = surplusInfoService.queryLimitInfoByWorkOrderNoAndSurplusType(workOrderNo, strings);
            if (null != checkSurplusInfo) {
                log.info("EAP自动下发:对应订单号:" + workOrderNo + "已存在类型:" + checkSurplusInfo.getSurplusType() + "的数据!");
                throw new BusinessException("订单号:" + workOrderNo + "已存在类型:" + checkSurplusInfo.getSurplusType() + "的数据!");
            }
        }
        surplusInfoService.saveOrUpdate(surplusInfo);
    }

    @Override
    @Transactional
    public void manualShutdown(String equipmentCode, String abnormalCause) {
        Equipment equipment = equipmentService.findByCode(equipmentCode);
        if (equipment == null) {
            throw new BusinessException(equipmentCode + "设备编号不存在");
        }

        EquipmentDeviceStatus deviceStatus = equipmentDeviceStatusService.findByDeviceNo(equipmentCode);
        if (null != deviceStatus && null != deviceStatus.getStatus() && deviceStatus.getStatus() == 2) {
            throw new BusinessException(equipmentCode + ":该炉台已停炉，无需手动操作停炉");
        }
        checkStopStatus(equipmentCode);
        ProductionSchedule productionSchedule = productionScheduleService.findTopByDeviceIdAndStatusOrderByIdDesc(equipment.getId(), EnumValuesServer.ProductionScheduleStatus.进行中.getKey());
        if (productionSchedule == null) {
            throw new BusinessException(equipmentCode + "设备，没有进行中的任务");
        }
        LongCrystal longCrystal = longCrystalService.findTopByLotIdAndCrystalStatus(productionSchedule.getLotId(), EnumValuesFactory.CrystalStatus.待确认.getKey());
        if (longCrystal != null) {
            throw new BusinessException(productionSchedule.getLotNo() + "炉次存在未确认的单晶，无法停炉，请确认单晶后重试");
        }

        //卡控:炉次终止时必须有一根完结尾
        LongCrystal longCrystal1 = longCrystalService.findBLotNoAndClosureIdentification(productionSchedule.getLotNo(), "W");
//        LongCrystal longCrystal1 = longCrystalService.findBLotNoAndClosureIdentification(productionSchedule.getLotNo(), "W");

        if (null == longCrystal1) {
            throw new RuntimeException("当前炉次【" + productionSchedule.getLotNo() + "】无完结尾单晶产出，不能点击炉次终止，请联系计划员/MES运维处理!");
        }
        PlanProcessingOrder planProcessingOrder = planProcessingOrderService.get(productionSchedule.getLotId());
        planProcessingOrder.setStatus(EnumValuesFactory.PlanProcessingOrderStatus.加工完成.getKey());
        planProcessingOrder.setRemarks("MES人工手动停炉，炉次结束");
        planProcessingOrderService.saveOrUpdate(planProcessingOrder);

//        equipment.setEnableState(true);
        // 手动停炉也不写设备状态值，与eap发信号保持一致
//        equipment.setStatus(2)
        equipment.setReason("炉次终止");

        //清空设备待机时间信息
        equipment.setStartOrEndWaitTime(null);

        equipmentService.saveOrUpdate(equipment);

        //处理炉次任务，改成完成
        productionSchedule.setStatus(EnumValuesServer.ProductionScheduleStatus.已完成.getKey());
        productionSchedule.setStatusInfo("MES人工手动停炉");
        productionSchedule.setCloseTime(new Date());
        productionScheduleService.saveOrUpdate(productionSchedule);

        // 上报石墨件消耗
        reportGraphiteToAccessory(productionSchedule);

        //修改设备加工能力的已开炉次数量
        MachineCapacity machineCapacity = machineCapacityService.findByEquipmentId(planProcessingOrder.getEquipmentId());
//        boolean isCapacityInfoNext = false;
        if (machineCapacity != null) {
            //防止eap错误，关闭自动增长已开炉次
            machineCapacity.setUsedCount((machineCapacity.getUsedCount() == null ? 0 : machineCapacity.getUsedCount()) + 1);
            if (machineCapacity.getCapacityInfoNext() != null) {
                MachineCapacity machineCapacity1 = JSON.parseObject(machineCapacity.getCapacityInfoNext(), MachineCapacity.class);
                machineCapacity.setCapacityInfo(machineCapacity1.getCapacityInfo());
                machineCapacity.setProductType(machineCapacity1.getProductType());
                machineCapacity.setRdlogo(machineCapacity1.getRdlogo());
                machineCapacity.setRdonlylogo(machineCapacity1.getRdonlylogo());
                machineCapacity.setCapacityInfoNext(null);
                machineCapacity.setUpdateTime(new Date());
            }
        }
        machineCapacityService.saveOrUpdate(machineCapacity);

        CrystalPullingInfo info = crystalPullingInfoService.getByDeviceNo(equipment.getCode());
        clearCrystalPullingInfoForManual(info, equipment.getCode(), abnormalCause);
        redisUtils.hmSet("all_device_shutdown_time", equipmentCode, DateUtil.formatDate(new Date(), DateUtil.longFormat));
    }

    /**
     * 检查停炉状态
     *
     * @param equipmentCode 设备编号
     */
    private void checkStopStatus(String equipmentCode) {
        String shutdownTimeStr
                = NumberUtil.object2String(redisUtils.hmGet("all_device_shutdown_time", equipmentCode), null);
        Date shutdownTime = DateUtil.toDateNull(shutdownTimeStr, DateUtil.longFormat);

        double time = 10;
        //查询字典值
        DictionaryItem dictionaryItem = dictionaryItemService.findByThemeValueAndNameAndEnable("shutdownTime", "time", true);
        if (dictionaryItem != null) {
            time = NumberUtil.Object2Double(dictionaryItem.getValue(), 10d);
        }

        if (shutdownTime != null && (System.currentTimeMillis() - shutdownTime.getTime()) < 1000 * 60 * 60 * time) {
            throw new BusinessException("距离上次停炉时间（" + shutdownTimeStr + "），不足" + time + "小时，请不要频繁进行停炉操作");
        }
    }

    /**
     * 处理预装料信号（等径长度大于**时触发）长度由eap做配置
     *
     * @param sendEapSignalDto
     * @param equipment
     * @param productionSchedule
     * @return
     */
    private Result handleYzl(SendEapSignalDto sendEapSignalDto, Equipment equipment, ProductionSchedule productionSchedule, int source) {
        // 设置输入的复投次数和颗次
        PlanProcessingOrder planProcessingOrder = planProcessingOrderService.get(productionSchedule.getLotId());
        // 发送预装料任务
        if(GlobalSwitch.enable(GlobalSwitchNamespace.NAMESPACE_COMMON_SWITCH, "PrepareInputMaterial", false)){
            BigDecimal weight = null;
            //如果在扩展信息中配置片区则优先使用extendValue
            List<DictionaryItem> dic = dictionaryItemService.findByThemeValueAndEnable("PrepareInputMaterialWeight",true);
            List<DictionaryItem> collect = dic.stream().filter(v -> v.getExtendValue() != null && Arrays.asList(v.getExtendValue().split(",")).contains(equipment.getSeries())).collect(Collectors.toList());
            if(collect != null && collect.size() > 0) weight = NumberUtil.Object2BigDecimal(collect.get(0).getValue());
            else {
                List<DictionaryItem> collect1 = dic.stream().filter(v -> StringUtils.isBlank(v.getExtendValue())).collect(Collectors.toList());
                if(collect1 != null && collect1.size() > 0) weight = NumberUtil.Object2BigDecimal(collect1.get(0).getValue());
            }
            if(null != weight && weight.doubleValue() % 10 == 0){
                // 复投次数需要+1
                return yzlTaskCreate(productionSchedule, equipment, weight, (planProcessingOrder.getAgainNum() + 1));
            } else {
                return ResultGenerator.success("未配置复投重量或者配置重量不为10的倍数，不产生预装料任务",null);
            }
        } else {
            return ResultGenerator.success("全局开关关闭，不产生预装料任务",null);
        }
    }

    /**
     * 发送预装料任务
     *
     * @param productionSchedule
     * @param equipment
     * @param weight 复投总重
     * @return
     */
    private Result yzlTaskCreate(ProductionSchedule productionSchedule, Equipment equipment, BigDecimal weight, Integer againNum) {
        try {
            AgainTaskPrepareDto againTaskDto = new AgainTaskPrepareDto();
            againTaskDto.setEquipmentCode(productionSchedule.getDeviceNo());
            againTaskDto.setLotNo(productionSchedule.getLotNo());
            againTaskDto.setSeries(equipment.getSeries());
            againTaskDto.setArea(equipment.getArea());
            againTaskDto.setProductCode(productionSchedule.getProductModel().getCode());
            againTaskDto.setProductGrade(productionSchedule.getProductModel().getProductGrade());
            againTaskDto.setSpecs(productionSchedule.getCapacityInfo().getSpecification());
            againTaskDto.setDopant(productionSchedule.getCapacityInfo().getDopant());
            againTaskDto.setProductType(productionSchedule.getProductType());
            againTaskDto.setSapNo(productionSchedule.getProductionOrder().getAufnr());
            againTaskDto.setFurnaceType(productionSchedule.getFurnaceType());
            againTaskDto.setAgainNum(againNum);
            againTaskDto.setAgainWeigth(weight);
            eapDockingService.yzlTaskCreate(againTaskDto);
            return ResultGenerator.success();
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResultGenerator.fail("复投任务发送失败：" + e.getMessage());
        }
    }
}

