package com.bs.mes.schedule;

import cn.hutool.core.date.DateUtil;
import com.ximai.mes.pro.domain.task.WorkorderScheduleParams;
import com.ximai.mes.pro.schedule.*;
import com.ximai.mes.pro.schedule.impl.ThXMScheduleAlgorithmAdapter;
import com.ximai.mes.pro.schedule.strategy.EquipmentSelectionStrategyThXMImpl;
import com.ximai.mes.pro.schedule.strategy.EvaluateEquipmentSerial;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;


public class ThXMScheduleAlgorithmAdapterTest {

    @Test
    public void schedule(){
        LocalDateTime scheduleStartDate = LocalDateTime.now().with(LocalTime.MIN).withYear(2022).withMonth(5).withDayOfMonth(5);
        ThXMScheduleAlgorithmAdapter baoshenScheduleAlgorithmAdapter = new ThXMScheduleAlgorithmAdapter();
        AlgorithmDataSource algorithmDataSource = new AlgorithmDataSource1();
        baoshenScheduleAlgorithmAdapter.setAlgorithmDataSource(algorithmDataSource);
        baoshenScheduleAlgorithmAdapter.setEquipmentSelectionStrategy(new EquipmentSelectionStrategyThXMImpl());
        baoshenScheduleAlgorithmAdapter.setAlgorithmResultProcess(new AlgorithmResultProcess() {
            @Override
            public void execute(LocalDateTime scheduleStartDate, List<Job> jobs) {
                jobPrint(jobs, scheduleStartDate);
                Assert.assertEquals(jobs.size(),2);
                Assert.assertEquals(Duration.ofHours(8), jobs.get(0).getTasks().get(0).getScheduledStartedTime());
                //120+47+准备工时1分钟（取整分钟）
                Assert.assertEquals(Duration.ofHours(8).plus(Duration.ofHours(2)).plus(Duration.ofMinutes(48)),
                        jobs.get(0).getTasks().get(0).getScheduledEndedTime());
                Assert.assertEquals(jobs.get(0).getTasks().get(0).getScheduledEndedTime(), jobs.get(0).getTasks().get(1).getScheduledStartedTime());
                //+17分钟
                Assert.assertEquals(jobs.get(0).getTasks().get(1).getScheduledStartedTime().plus(Duration.ofMinutes(17)),
                        jobs.get(0).getTasks().get(1).getScheduledEndedTime());
                Assert.assertEquals(Duration.ofHours(8), jobs.get(1).getTasks().get(0).getScheduledStartedTime());
                Assert.assertEquals(Duration.ofMinutes(400)//第一道工序加工时间
                        .plus(Duration.ofHours(8))//开始时间
                        .plus(Duration.ofMinutes(90))//跨越了一个休息时间1小时30分
                        .plus(Duration.ofMinutes(1))//准备工时1分钟
                        ,
                        jobs.get(1).getTasks().get(0).getScheduledEndedTime());
            }
        });
        baoshenScheduleAlgorithmAdapter.schedule(scheduleStartDate, null);
    }

    @Test
    public void schedule2(){
        LocalDateTime scheduleStartDate = LocalDateTime.now().with(LocalTime.MIN).withYear(2022).withMonth(5).withDayOfMonth(5);
        ThXMScheduleAlgorithmAdapter baoshenScheduleAlgorithmAdapter = new ThXMScheduleAlgorithmAdapter();
        AlgorithmDataSource algorithmDataSource = new AlgorithmDataSource2();
        baoshenScheduleAlgorithmAdapter = new ThXMScheduleAlgorithmAdapter();
        baoshenScheduleAlgorithmAdapter.setAlgorithmDataSource(algorithmDataSource);
        EquipmentSelectionStrategyThXMImpl selectionStrategyThXM = new EquipmentSelectionStrategyThXMImpl();
        selectionStrategyThXM.addEvaluateEquipment(new EvaluateEquipmentSerial());
        baoshenScheduleAlgorithmAdapter.setEquipmentSelectionStrategy(selectionStrategyThXM);
        baoshenScheduleAlgorithmAdapter.setAlgorithmResultProcess(new AlgorithmResultProcess() {
            @Override
            public void execute(LocalDateTime scheduleStartDate, List<Job> jobs) {
                jobPrint(jobs, scheduleStartDate);
                Assert.assertEquals(jobs.size(),1);
                Assert.assertEquals(4,jobs.get(0).getTasks().size());
                List<Task> job1TaskList = jobs.get(0).getTasks();
                //task1
                Assert.assertEquals(Duration.ofHours(8), job1TaskList.get(0).getScheduledStartedTime());
                Assert.assertEquals(job1TaskList.get(0).getScheduledEndedTime(),
                        job1TaskList.get(0).getScheduledStartedTime().plus(Duration.ofHours(2)));
                //task2，柔版印刷1设备优先级高，产能不足
                Assert.assertEquals(ScheduleStatus.InadequateTime, job1TaskList.get(1).getScheduleStatus());

            }
        });
        baoshenScheduleAlgorithmAdapter.schedule(scheduleStartDate, null);
    }


    @Test
    public void schedule3(){
        LocalDateTime scheduleStartDate = LocalDateTime.now().with(LocalTime.MIN).withYear(2022).withMonth(5).withDayOfMonth(5);
        ThXMScheduleAlgorithmAdapter baoshenScheduleAlgorithmAdapter = new ThXMScheduleAlgorithmAdapter();
        AlgorithmDataSource algorithmDataSource = new AlgorithmDataSource3();
        baoshenScheduleAlgorithmAdapter = new ThXMScheduleAlgorithmAdapter();
        baoshenScheduleAlgorithmAdapter.setAlgorithmDataSource(algorithmDataSource);
        EquipmentSelectionStrategyThXMImpl selectionStrategyThXM = new EquipmentSelectionStrategyThXMImpl();
        selectionStrategyThXM.addEvaluateEquipment(new EvaluateEquipmentSerial());
        baoshenScheduleAlgorithmAdapter.setEquipmentSelectionStrategy(selectionStrategyThXM);
        // 柔版印刷2设备优先级高，正常排产
        baoshenScheduleAlgorithmAdapter.setAlgorithmResultProcess(new AlgorithmResultProcess() {
            @Override
            public void execute(LocalDateTime scheduleStartDate, List<Job> jobs) {
                jobPrint(jobs, scheduleStartDate);
                Assert.assertEquals(jobs.size(),1);
                Assert.assertEquals(4,jobs.get(0).getTasks().size());
                List<Task> job1TaskList = jobs.get(0).getTasks();
                //task1
                Assert.assertEquals(Duration.ofHours(8), job1TaskList.get(0).getScheduledStartedTime());
                Assert.assertEquals(job1TaskList.get(0).getScheduledEndedTime(),
                        job1TaskList.get(0).getScheduledStartedTime().plus(Duration.ofHours(2)));

                //task2
                Assert.assertEquals(Duration.ofHours(5+8).plusMinutes(30), //设备2从下午有日历
                        job1TaskList.get(1).getScheduledStartedTime());
                Assert. assertEquals(job1TaskList.get(1).getScheduledStartedTime()
                                .plus(Duration.ofHours(4)//第一班次4小时
                                .plus(Duration.ofHours(14).plusMinutes(30))//休息间隔，17:30 - 8:00
                                .plus(Duration.ofMinutes(108))
                                ),

                        job1TaskList.get(1).getScheduledEndedTime());

                //task3
                Assert.assertEquals(job1TaskList.get(1).getScheduledEndedTime(),
                        job1TaskList.get(2).getScheduledStartedTime());
                Assert.assertEquals(job1TaskList.get(2).getScheduledStartedTime().plus(Duration.ofHours(2)),
                        jobs.get(0).getTasks().get(2).getScheduledEndedTime());
                //task4
                Assert.assertEquals(job1TaskList.get(2).getScheduledEndedTime(),
                        job1TaskList.get(3).getScheduledStartedTime());
                Assert.assertEquals(job1TaskList.get(3).getScheduledStartedTime().plus(Duration.ofHours(2)
                                .plus(Duration.ofMinutes(90))),//中午休息1个半小时
                        job1TaskList.get(3).getScheduledEndedTime());

            }
        });
        baoshenScheduleAlgorithmAdapter.schedule(scheduleStartDate, null);
    }

    //测试产能不足、延期状态数据
    @Test
    public void schedule5(){
        LocalDateTime scheduleStartDate = LocalDateTime.now().with(LocalTime.MIN).withYear(2022).withMonth(5).withDayOfMonth(5);
        ThXMScheduleAlgorithmAdapter baoshenScheduleAlgorithmAdapter = new ThXMScheduleAlgorithmAdapter();
        AlgorithmDataSource algorithmDataSource = new AlgorithmDataSource5();
        baoshenScheduleAlgorithmAdapter.setAlgorithmDataSource(algorithmDataSource);
        baoshenScheduleAlgorithmAdapter.setEquipmentSelectionStrategy(new EquipmentSelectionStrategyThXMImpl());
        baoshenScheduleAlgorithmAdapter.setAlgorithmResultProcess(new AlgorithmResultProcess() {
            @Override
            public void execute(LocalDateTime scheduleStartDate, List<Job> jobs) {
                jobPrint(jobs, scheduleStartDate);
                Assert.assertEquals(jobs.size(),2);
                Assert.assertEquals(jobs.get(0).getScheduleStatus(), ScheduleStatus.Delay);
                Assert.assertEquals(jobs.get(1).getScheduleStatus(), ScheduleStatus.InadequateTime);

                Assert.assertEquals(Duration.ofHours(8), jobs.get(0).getTasks().get(0).getScheduledStartedTime());
                Assert.assertEquals(jobs.get(0).getTasks().get(0).getScheduledStartedTime().plus(Duration.ofHours(2)),
                        jobs.get(0).getTasks().get(0).getScheduledEndedTime());
                //task2
                Assert.assertEquals(Duration.ofHours(32),//5号不存在日历
                        jobs.get(0).getTasks().get(1).getScheduledStartedTime());
                Assert.assertEquals(jobs.get(0).getTasks().get(1).getScheduledStartedTime().plus(Duration.ofHours(2)),
                        jobs.get(0).getTasks().get(1).getScheduledEndedTime());
                //test3
                Assert.assertEquals(jobs.get(0).getTasks().get(1).getScheduledEndedTime(),
                        jobs.get(0).getTasks().get(2).getScheduledStartedTime());
                Assert.assertEquals(jobs.get(0).getTasks().get(2).getScheduledStartedTime().plus(Duration.ofHours(1)),
                        jobs.get(0).getTasks().get(2).getScheduledEndedTime());

                //job2
                List<Task> job2Task = jobs.get(1).getTasks();
                Assert.assertEquals(Duration.ofHours(8), job2Task.get(0).getScheduledStartedTime());
                Assert.assertEquals(job2Task.get(0).getScheduledStartedTime().plus(Duration.ofHours(2)),
                        job2Task.get(0).getScheduledEndedTime());

                Assert.assertEquals(job2Task.get(0).getScheduledEndedTime().plus(Duration.ofHours(22)), job2Task.get(1).getScheduledStartedTime());
                Assert.assertEquals(job2Task.get(1).getScheduledStartedTime().plus(Duration.ofHours(6)).plus(Duration.ofMinutes(90)),
                        job2Task.get(1).getScheduledEndedTime());
                Assert.assertEquals(job2Task.get(2).getScheduleStatus(), ScheduleStatus.Success);
                Assert.assertEquals(job2Task.get(3).getScheduleStatus(), ScheduleStatus.InadequateTime);



            }
        });
        baoshenScheduleAlgorithmAdapter.schedule(scheduleStartDate, null);
    }

    //外协工序测试
    @Test
    public void scheduleOutsourcing(){
        LocalDateTime scheduleStartDate = LocalDateTime.now().with(LocalTime.MIN).withYear(2022).withMonth(5).withDayOfMonth(5);
        ThXMScheduleAlgorithmAdapter baoshenScheduleAlgorithmAdapter = new ThXMScheduleAlgorithmAdapter();
        AlgorithmDataSource algorithmDataSource = new AlgorithmDataSourceOutsourcing();
        baoshenScheduleAlgorithmAdapter.setAlgorithmDataSource(algorithmDataSource);
        baoshenScheduleAlgorithmAdapter.setEquipmentSelectionStrategy(new EquipmentSelectionStrategyThXMImpl());
        baoshenScheduleAlgorithmAdapter.setAlgorithmResultProcess(new AlgorithmResultProcess() {
            @Override
            public void execute(LocalDateTime scheduleStartDate, List<Job> jobs) {
                jobPrint(jobs, scheduleStartDate);
                Assert.assertEquals(jobs.size(),2);
                Assert.assertEquals(jobs.get(0).getScheduleStatus(), ScheduleStatus.Delay);
                Assert.assertEquals(jobs.get(1).getScheduleStatus(), ScheduleStatus.Delay);
                //job1
                List<Task> jobTask = jobs.get(0).getTasks();

                //task1
                Assert.assertEquals(Duration.ofHours(0), jobs.get(0).getTasks().get(0).getScheduledStartedTime());
                Assert.assertEquals(jobs.get(0).getTasks().get(0).getScheduledStartedTime().plus(Duration.ofDays(3)),
                        jobs.get(0).getTasks().get(0).getScheduledEndedTime());
                //task2
                Assert.assertEquals(Duration.ofDays(3).plusHours(8),
                        jobs.get(0).getTasks().get(1).getScheduledStartedTime());
                Assert.assertEquals(jobs.get(0).getTasks().get(1).getScheduledStartedTime().plus(Duration.ofHours(2)),
                        jobs.get(0).getTasks().get(1).getScheduledEndedTime());
                //test3
                Assert.assertEquals(jobs.get(0).getTasks().get(1).getScheduledEndedTime(),
                        jobs.get(0).getTasks().get(2).getScheduledStartedTime());
                Assert.assertEquals(jobs.get(0).getTasks().get(2).getScheduledStartedTime().plus(Duration.ofHours(1)),
                        jobs.get(0).getTasks().get(2).getScheduledEndedTime());

                //job2
                List<Task> job2Task = jobs.get(1).getTasks();
                Assert.assertEquals(Duration.ofHours(8), job2Task.get(0).getScheduledStartedTime());
                Assert.assertEquals(job2Task.get(0).getScheduledStartedTime().plus(Duration.ofHours(2)),
                        job2Task.get(0).getScheduledEndedTime());

                Assert.assertEquals(job2Task.get(0).getScheduledEndedTime(), job2Task.get(1).getScheduledStartedTime());
                Assert.assertEquals(job2Task.get(1).getScheduledStartedTime().plus(Duration.ofDays(3)),
                        job2Task.get(1).getScheduledEndedTime());

                Assert.assertEquals(job2Task.get(1).getScheduledEndedTime(), job2Task.get(2).getScheduledStartedTime());
                Assert.assertEquals(job2Task.get(2).getScheduledStartedTime().plus(Duration.ofHours(1)),
                        job2Task.get(2).getScheduledEndedTime());

                Assert.assertEquals(job2Task.get(2).getScheduledEndedTime(), job2Task.get(3).getScheduledStartedTime());
                Assert.assertEquals(job2Task.get(3).getScheduledStartedTime()
                                .plus(Duration.ofHours(2)
                                .plus(Duration.ofMinutes(90))),
                        job2Task.get(3).getScheduledEndedTime());


            }
        });
        baoshenScheduleAlgorithmAdapter.schedule(scheduleStartDate, null);
    }

    //时间验证
    @Test
    public void scheduleTimeValidate(){
        LocalDateTime scheduleStartDate = LocalDateTime.now().with(LocalTime.MIN).withYear(2022).withMonth(5).withDayOfMonth(5);
        ThXMScheduleAlgorithmAdapter baoshenScheduleAlgorithmAdapter = new ThXMScheduleAlgorithmAdapter();
        AlgorithmDataSource algorithmDataSource = new AlgorithmDataTimeSpace();
        baoshenScheduleAlgorithmAdapter.setAlgorithmDataSource(algorithmDataSource);
        baoshenScheduleAlgorithmAdapter.setEquipmentSelectionStrategy(new EquipmentSelectionStrategyThXMImpl());
        baoshenScheduleAlgorithmAdapter.setAlgorithmResultProcess(new AlgorithmResultProcess() {
            @Override
            public void execute(LocalDateTime scheduleStartDate, List<Job> jobs) {
                jobPrint(jobs, scheduleStartDate);
                Assert.assertEquals(jobs.size(),1);
                Assert.assertEquals(ScheduleStatus.Delay, jobs.get(0).getScheduleStatus());

                //job1
                List<Task> jobTask = jobs.get(0).getTasks();
                //task1
                //日历占用，从10点25分开始
                Assert.assertEquals(Duration.ofHours(10).plusMinutes(25), jobs.get(0).getTasks().get(0).getScheduledStartedTime());
                //总生产6小时，跨时间段，10：25-12：00=95分 13:30-17:30=240分 隔天08:00-25=25分 总6小时=7200*3/60=360分
                //第1工序单件加工时间3秒
                //第一时间段95/360*7200=1899，剩余时间5301*3/60=266(分钟取整)
                //第二时间段240/(266)*5301=4782，剩余时间519*3/60=26(分钟取整)
                //第三时间段耗时=直接取26分钟
                Assert.assertEquals(jobs.get(0).getTasks().get(0).getScheduledStartedTime().plusHours(13)
                                .plusMinutes(35).plusHours(8)
                                .plusMinutes(25),//跨时间段计算多出一分钟
                        jobs.get(0).getTasks().get(0).getScheduledEndedTime());
            }
        });
        baoshenScheduleAlgorithmAdapter.schedule(scheduleStartDate, null);
    }

    /*
     * 数据源1，验证工序时间是否连续排产，
     * 不同工单相同设备不相互占用空闲时间
     */
    public class AlgorithmDataSource1 implements AlgorithmDataSource{
        @Override
        public List<Resource> getResource(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams) {
            return buildEquipment(
                    new String[]{"包装1","排版1"},
                    new String[]{"包装","排版"},
                    new Long[]{1l,2l});
        }

        @Override
        public List<Job> getJob(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams, List<Resource> resources) {
            List<Job> rst = new ArrayList<Job>();
            Job job = buildJob(1l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(10000),
                    new String[]{"包装", "排版"},
                    new Long[]{1l,2l},
                    new Duration[]{Duration.ofSeconds(1), Duration.ofMillis(100)},
                    new Duration[]{Duration.ofSeconds(1), Duration.ZERO});

            Job job2 = buildJob(2l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(8000),
                    new String[]{"包装"},
                    new Long[]{1l},
                    new Duration[]{Duration.ofSeconds(3)},
                    new Duration[]{Duration.ofSeconds(1)});

            rst.add(job);
            rst.add(job2);
            return rst;
        }

        @Override
        public List<Calendar> getCalendar(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams,List<Resource> resources) {
            List<Calendar> rst = new ArrayList<Calendar>();
            rst.add(buildWorkingCalendar(resources.get(0), schedulingStartedDate,
                    "22-05-05 08:00","22-05-05 12:00"));
            rst.add(buildWorkingCalendar(resources.get(0), schedulingStartedDate,
                    "22-05-05 13:30","22-05-05 17:00"));
            rst.add(buildWorkingCalendar(resources.get(0), schedulingStartedDate,
                    "22-05-06 08:00","22-05-06 12:00"));
            rst.add(buildWorkingCalendar(resources.get(0), schedulingStartedDate,
                    "22-05-06 13:30","22-05-06 17:00"));
            //设备2
            rst.add(buildWorkingCalendar(resources.get(1), schedulingStartedDate,
                    "22-05-05 08:00","22-05-05 12:00"));
            rst.add(buildWorkingCalendar(resources.get(1), schedulingStartedDate,
                    "22-05-05 13:30","22-05-05 17:00"));
            rst.add(buildWorkingCalendar(resources.get(1), schedulingStartedDate,
                    "22-05-06 08:00","22-05-06 12:00"));
            rst.add(buildWorkingCalendar(resources.get(1), schedulingStartedDate,
                    "22-05-06 13:30","22-05-06 17:00"));
            return rst;
        }
    }



    /*
     * 数据源2，单任务多设备可选情况，选择评分最高设备，评分一样时按ID排序后选择一台
     * 工单1工艺：排版(DTA)-柔版印刷-冷切-包装(8-12 ~ 13.30-17.30)
     * 工单1时长，需求日期=05-06，排版2H-柔版印刷6H-冷切1H-包装2H
     * 柔版印刷工序2台设备支持加工，设备1产能不足，设备2产能足够
     * 测试2种情况
     * 设备1序号靠前，产能不足直接中止排产
     */
    public class AlgorithmDataSource2 implements AlgorithmDataSource {
        @Override
        public List<Resource> getResource(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams) {
            return buildEquipment(
                    new String[]{"排版(DTA)1","冷切1","包装1","柔版印刷1-1","柔版印刷2-2"},
                    new String[]{"排版(DTA)","冷切","包装","柔版印刷","柔版印刷"},
                    new Long[]{1l,2l,3l,4l,4l});
        }

        @Override
        public List<Job> getJob(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams, List<Resource> resources) {
            List<Job> rst = new ArrayList<Job>();
            Job job = buildJob(1l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(7200),
                    new String[]{"排版(DTA)", "柔版印刷", "冷切", "包装"},
                    new Long[]{1l,4l,2l,3l},
                    new Duration[]{Duration.ofSeconds(1), Duration.ofMillis(2900), Duration.ofSeconds(1), Duration.ofSeconds(1)},
                    new Duration[]{Duration.ZERO, Duration.ZERO, Duration.ZERO, Duration.ZERO});

            rst.add(job);
            return rst;
        }

        @Override
        public List<Calendar> getCalendar(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams,List<Resource> resources) {
            List<Calendar> rst = new ArrayList<Calendar>();
            for(int i=0;i<3;i++){
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 08:00","22-05-05 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 13:30","22-05-05 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 08:00","22-05-06 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 13:30","22-05-06 17:30"));
            }
            rst.add(buildWorkingCalendar(resources.get(4), schedulingStartedDate,
                    "22-05-05 13:30","22-05-05 17:30"));
            rst.add(buildWorkingCalendar(resources.get(4), schedulingStartedDate,
                    "22-05-06 08:00","22-05-06 12:30"));
            rst.add(buildWorkingCalendar(resources.get(4), schedulingStartedDate,
                    "22-05-06 13:30","22-05-06 17:30"));
            return rst;
        }
    }


    /*
     * 数据源3，单任务多设备可选情况，选择评分最高设备，评分一样时按ID排序后选择一台
     * 工单1工艺：排版(DTA)-柔版印刷-冷切-包装(8-12 ~ 13.30-17.30)
     * 工单1时长，需求日期=05-06，排版2H-柔版印刷6H-冷切1H-包装2H
     * 柔版印刷工序2台设备支持加工，设备1产能不足，设备2产能足够
     *
     * 设备2序号靠前，产能足够继续排产
     */
    public class AlgorithmDataSource3 implements AlgorithmDataSource {
        @Override
        public List<Resource> getResource(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams) {
            return buildEquipment(
                    new String[]{"排版(DTA)1","冷切1","包装1","柔版印刷1-2","柔版印刷2-1"},
                    new String[]{"排版(DTA)","冷切","包装","柔版印刷","柔版印刷"},
                    new Long[]{1l,2l,3l,4l,4l});
        }

        @Override
        public List<Job> getJob(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams, List<Resource> resources) {
            List<Job> rst = new ArrayList<Job>();
            Job job = buildJob(1l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(7200),
                    new String[]{"排版(DTA)", "柔版印刷", "冷切", "包装"},
                    new Long[]{1l,4l,2l,3l},
                    new Duration[]{Duration.ofSeconds(1), Duration.ofMillis(2900), Duration.ofSeconds(1), Duration.ofSeconds(1)},
                    new Duration[]{Duration.ZERO, Duration.ZERO, Duration.ZERO, Duration.ZERO});

            rst.add(job);
            return rst;
        }

        @Override
        public List<Calendar> getCalendar(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams,List<Resource> resources) {
            List<Calendar> rst = new ArrayList<Calendar>();
            for(int i=0;i<3;i++){
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 08:00","22-05-05 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 13:30","22-05-05 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 08:00","22-05-06 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 13:30","22-05-06 17:30"));
            }
            rst.add(buildWorkingCalendar(resources.get(4), schedulingStartedDate,
                    "22-05-05 13:30","22-05-05 17:30"));
            rst.add(buildWorkingCalendar(resources.get(4), schedulingStartedDate,
                    "22-05-06 08:00","22-05-06 12:30"));
            rst.add(buildWorkingCalendar(resources.get(4), schedulingStartedDate,
                    "22-05-06 13:30","22-05-06 17:30"));
            return rst;
        }
    }

    /*
     * 数据源5，测试产能不足、延期状态数据
     * 工单工艺：排版(DTA)-柔版印刷-冷切-包装(8-12 ~ 13.30-17.30)
     * 工单1时长，需求日期=05-05，排版2H-柔版印刷2H-冷切1H-包装2H（延期状态数据）
     * 工单2时长，需求日期=05-06，排版2H-柔版印刷12H-冷切1H-包装2H（产能不足，柔板5号没排产能）
     */
    public class AlgorithmDataSource5 implements AlgorithmDataSource {
        @Override
        public List<Resource> getResource(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams) {
            return buildEquipment(
                    new String[]{"排版(DTA)1","冷切1","包装1","柔版印刷1","柔版印刷2"},
                    new String[]{"排版(DTA)","冷切","包装","柔版印刷","柔版印刷"},
                    new Long[]{1l,2l,3l,4l,4l});
        }

        @Override
        public List<Job> getJob(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams, List<Resource> resources) {
            List<Job> rst = new ArrayList<Job>();
            LocalDateTime demandDate = LocalDateTime.now().withYear(2022).withMonth(5).withDayOfMonth(5).with(LocalTime.MIN);

            Job job = buildJob(1l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(7200),
                    new String[]{"排版(DTA)", "柔版印刷", "冷切", "包装"},
                    new Long[]{1l,4l,2l,3l},
                    new Duration[]{Duration.ofSeconds(1), Duration.ofSeconds(1), Duration.ofMillis(500), Duration.ofSeconds(1)},
                    new Duration[]{Duration.ZERO, Duration.ZERO, Duration.ZERO, Duration.ZERO});

            Job job2 = buildJob(2l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(7200),
                    new String[]{"排版(DTA)", "柔版印刷", "冷切", "包装"},
                    new Long[]{1l,4l,2l,3l},
                    new Duration[]{Duration.ofSeconds(1), Duration.ofSeconds(3), Duration.ofMillis(500), Duration.ofSeconds(1)},
                    new Duration[]{Duration.ZERO, Duration.ZERO, Duration.ZERO, Duration.ZERO});

            rst.add(job);
            rst.add(job2);
            return rst;
        }

        @Override
        public List<Calendar> getCalendar(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams,List<Resource> resources) {
            List<Calendar> rst = new ArrayList<Calendar>();
            for(int i=0;i<3;i++){
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 08:00","22-05-05 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 13:30","22-05-05 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 08:00","22-05-06 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 13:30","22-05-06 17:30"));
            }
            rst.add(buildWorkingCalendar(resources.get(3), schedulingStartedDate,
                    "22-05-06 08:00","22-05-06 12:00"));
            rst.add(buildWorkingCalendar(resources.get(3), schedulingStartedDate,
                    "22-05-06 13:30","22-05-06 17:30"));
            rst.add(buildWorkingCalendar(resources.get(3), schedulingStartedDate,
                    "22-05-07 08:00","22-05-07 12:00"));
            rst.add(buildWorkingCalendar(resources.get(3), schedulingStartedDate,
                    "22-05-07 13:30","22-05-07 17:30"));
            return rst;
        }
    }


    //外协工序测试数据源
    public class AlgorithmDataSourceOutsourcing implements AlgorithmDataSource {
        @Override
        public List<Resource> getResource(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams) {
            return buildEquipment(
                    new String[]{"排版(DTA)1","冷切1","包装1","柔版印刷1","柔版印刷2"},
                    new String[]{"排版(DTA)","冷切","包装","柔版印刷","柔版印刷"},
                    new Long[]{1l,2l,3l,4l,4l});
        }

        @Override
        public List<Job> getJob(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams, List<Resource> resources) {
            List<Job> rst = new ArrayList<Job>();
            LocalDateTime demandDate = LocalDateTime.now().withYear(2022).withMonth(5).withDayOfMonth(5).with(LocalTime.MIN);

            Job job = buildJob(1l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(7200),
                    new String[]{"排版(DTA)-1", "柔版印刷", "冷切", "包装"},
                    new Long[]{1l,4l,2l,3l},
                    new Duration[]{Duration.ofSeconds(1), Duration.ofSeconds(1), Duration.ofMillis(500), Duration.ofSeconds(1)},
                    new Duration[]{Duration.ZERO, Duration.ZERO, Duration.ZERO, Duration.ZERO});

            Job job2 = buildJob(2l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(7200),
                    new String[]{"排版(DTA)", "柔版印刷-1", "冷切", "包装"},
                    new Long[]{1l,4l,2l,3l},
                    new Duration[]{Duration.ofSeconds(1), Duration.ofSeconds(3), Duration.ofMillis(500), Duration.ofSeconds(1)},
                    new Duration[]{Duration.ZERO, Duration.ZERO, Duration.ZERO, Duration.ZERO});

            rst.add(job);
            rst.add(job2);
            return rst;
        }

        @Override
        public List<Calendar> getCalendar(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams,List<Resource> resources) {
            List<Calendar> rst = new ArrayList<Calendar>();
            for(int i=0;i<4;i++){
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 08:00","22-05-05 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 13:30","22-05-05 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 08:00","22-05-06 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 13:30","22-05-06 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-07 08:00","22-05-07 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-07 13:30","22-05-07 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-08 08:00","22-05-08 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-08 13:30","22-05-08 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-09 08:00","22-05-09 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-09 13:30","22-05-09 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-10 08:00","22-05-10 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-10 13:30","22-05-10 17:30"));
            }
            return rst;
        }
    }

    //时间间隔测试
    public class AlgorithmDataTimeSpace implements AlgorithmDataSource {
        @Override
        public List<Resource> getResource(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams) {
            return buildEquipment(
                    new String[]{"排版(DTA)-1@25","冷切-1","包装-1","柔版印刷-1@45","柔版印刷-2"},
                    new String[]{"排版(DTA)","冷切","包装","柔版印刷","柔版印刷"},
                    new Long[]{1l,2l,3l,4l,4l});
        }

        @Override
        public List<Job> getJob(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams, List<Resource> resources) {
            List<Job> rst = new ArrayList<Job>();
            LocalDateTime demandDate = LocalDateTime.now().withYear(2022).withMonth(5).withDayOfMonth(5).with(LocalTime.MIN);

            Job job = buildJob(1l, schedulingStartedDate, "2022-05-06 00:00", BigDecimal.valueOf(7200),
                    new String[]{"排版(DTA)", "柔版印刷", "冷切", "包装"},
                    new Long[]{1l,4l,2l,3l},
                    new Duration[]{Duration.ofSeconds(3), Duration.ofSeconds(1), Duration.ofMillis(500), Duration.ofSeconds(1)},
                    new Duration[]{Duration.ZERO, Duration.ZERO, Duration.ZERO, Duration.ZERO});

            rst.add(job);
            return rst;
        }

        @Override
        public List<Calendar> getCalendar(LocalDateTime schedulingStartedDate, List<WorkorderScheduleParams> workorderScheduleParams,List<Resource> resources) {
            List<Calendar> rst = new ArrayList<Calendar>();
            for(int i=0;i<4;i++){
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 08:00","22-05-05 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-05 13:30","22-05-05 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 08:00", "22-05-06 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-06 13:30", "22-05-06 17:30"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-07 08:00", "22-05-07 12:00"));
                rst.add(buildWorkingCalendar(resources.get(i), schedulingStartedDate,
                        "22-05-07 13:30", "22-05-07 17:30"));
            }
            //占用日历
            Calendar.OccupiedCalendar alg_CalendarItem = new Calendar.OccupiedCalendar() {{
                setStartedTime(Duration.between(schedulingStartedDate, schedulingStartedDate.plusHours(8)));
                setEndedTime(Duration.between(schedulingStartedDate, schedulingStartedDate.plusHours(8).plusMinutes(145)));
                setResource(resources.get(0));
            }};
            rst.add(alg_CalendarItem);

            return rst;
        }
    }

    /**
     * 生成设备
     * @param workUnits 作业单元 单元名-序号@固化时间@转间时间
     * @param workCenterName 工作中心名称
     * @param workCenterId 工作中心ID
     * @return
     */
    private List<Resource> buildEquipment(String[] workUnits, String[] workCenterName, Long[] workCenterId){
        List<Resource> rst = new ArrayList<Resource>();
        int i=0;
        for(String workUnitName : workUnits){
            Equipment equipment = new Equipment(i*1l);
            String[] workunitConf = workUnitName.split("[@]");
            if(workunitConf.length==2){
                equipment.setCuringTime(Integer.valueOf(workunitConf[1]));
            }
            if(workunitConf.length==3){
                if(StringUtils.isNoneBlank(workunitConf[1])) {
                    equipment.setCuringTime(Integer.valueOf(workunitConf[1]));
                }
                equipment.setTransferShopTime(Integer.valueOf(workunitConf[2]));
            }
            workUnitName = workunitConf[0];
            String[] namesSplit = workUnitName.split("-");
            equipment.setWorkUnitName(namesSplit[0]);
            if(namesSplit.length==2){
                equipment.setSerial(Integer.valueOf(namesSplit[1]));
            }
            equipment.setWorkCenterName(workCenterName[i]);
            equipment.setWorkCenterId(workCenterId[i]);
            equipment.setIsParallel(false);
            rst.add(equipment);
            i++;
        }
        Equipment equipment = new Equipment((i+1)*1l);
        equipment.setWorkUnitName("外协");
        equipment.setWorkUnitCode("GZBSWWDY");
        equipment.setWorkCenterName("外协");
        equipment.setWorkCenterId((i+1)*1l);
        equipment.setIsParallel(false);
        rst.add(equipment);
        return rst;
    }



    private Job buildJob(Long id, LocalDateTime schedulingStartedDate, String demandDate,  BigDecimal quantity,
                         String[] processes, Long[] workCenterId, Duration[] standardTime, Duration[] setupTime){
        LocalDateTime end = DateUtil.toLocalDateTime(DateUtil.parse(demandDate,"yy-MM-dd HH:mm"));
        Job job = new Job(id+"", Duration.between(schedulingStartedDate, end));
        LinkedList<Task> taskList = new LinkedList<Task>();
        int i=1;
        for(String processName : processes){
            String[] temps = processName.split("-");
            Task task = new Task(id*i, job);
            if(temps.length==2){
                processName = temps[0];
                //为1是外协工序
                if(temps[1].equals("1")){
                    task.setOutsourced(true);
                }
            }
            task.setProcessName(processName);
            task.setWorkCenterId(workCenterId[i-1]);
            task.setStandardWorkingTime(standardTime[i-1]);
            task.setSetupTime(setupTime[i-1]);
            task.setOrderQuantity(quantity);
            i++;
            taskList.add(task);
        }
        job.setTasks(taskList);
        return job;
    }

    /*
     * 转换工作日历
     */
    private Calendar.WorkingCalendar buildWorkingCalendar(Resource resource, LocalDateTime scheduleStartDate, String start, String end){

        return new Calendar.WorkingCalendar() {{
            LocalDateTime startDate = DateUtil.toLocalDateTime(DateUtil.parse(start,"yy-MM-dd HH:mm"));
            LocalDateTime endDate = DateUtil.toLocalDateTime(DateUtil.parse(end,"yy-MM-dd HH:mm"));
            setStartedTime(Duration.between(scheduleStartDate, startDate));
            setEndedTime(Duration.between(scheduleStartDate, endDate));
            setResource(resource);
        }};
    }

    private void jobPrint(List<Job> jobs, LocalDateTime scheduleStartDate){
        jobs.forEach(job->{
            System.out.println(job.getId()+" - "+job.getScheduleStatus());
            job.getTasks().forEach(task->{
                if(task.getScheduleStatus() == ScheduleStatus.Success){
                    if(task.getSplitTask()==null||task.getSplitTask().isEmpty()){
                        LocalDateTime start = scheduleStartDate.plus(task.getScheduledStartedTime());
                        LocalDateTime end = scheduleStartDate.plus(task.getScheduledEndedTime());
                        System.out.println(job.getId()+ " - " + task.getProcessName() +" - "+start + " - " +end+" - " +
                                task.getScheduledEquipment().getWorkUnitName());
                    }else{
                        task.getSplitTask().forEach(subTask->{
                            LocalDateTime start = scheduleStartDate.plus(subTask.getScheduledStartedTime());
                            LocalDateTime end = scheduleStartDate.plus(subTask.getScheduledEndedTime());
                            System.out.println(job.getId()+ " - " + subTask.getProcessName() +" - "+start + " - " +end +" - " +
                                    subTask.getScheduledEquipment().getWorkUnitName());
                        });
                    }
                }else{
                    System.out.println(job.getId() + " - " +task.getProcessName() +" - "+task.getScheduleStatus());
                }
            });
        });
    }


}
