package schedule

import (
	wltab "LAPP_LF_MOM_BACKEND/dao/base"
	dal "LAPP_LF_MOM_BACKEND/dao/me"
	wotab "LAPP_LF_MOM_BACKEND/dao/om"
	"LAPP_LF_MOM_BACKEND/db"
	"LAPP_LF_MOM_BACKEND/grmi"
	"LAPP_LF_MOM_BACKEND/infra/logger"
	"LAPP_LF_MOM_BACKEND/models/base"
	common "LAPP_LF_MOM_BACKEND/models/base"
	model "LAPP_LF_MOM_BACKEND/models/me"
	"LAPP_LF_MOM_BACKEND/models/om"
	"LAPP_LF_MOM_BACKEND/utils"
	"errors"
	"fmt"
	uuid "github.com/iris-contrib/go.uuid"
	"reflect"
	"strconv"
	"time"
)

// 调度器
type SchedulerData struct {
	TaskId             string    `json:"taskId"`
	StartDate          string    `json:"startDate"`
	EndDate            string    `json:"endDate"`
	PlannedTaskArray   []TaskSrv `json:"plannedTaskArray"`
	UnPlannedTaskArray []TaskSrv `json:"unPlannedTaskArray"`
}

// 调度器
type SchedulerSrv struct {
	TaskId             string
	PlantNr            int // 工厂号
	StartDate          time.Time
	EndDate            time.Time
	Tmsrv              TimeModelSrv // 时间模型
	WorklineDict       map[string]*WorklineSrv
	SetupDict          map[string]SetupSrv
	ProjDict           map[string]model.Project // 激活的项目字典
	LockedTaskArray    []TaskSrv                // 已锁定的任务队列，包括：已下达、中断、生产中和已完成 >=24
	PlannedTaskArray   []TaskSrv                // 已计划的任务队列   =20
	UnPlannedTaskArray []TaskSrv                // 未计划的任务队列   =10
	Operation          string                   // move(左右),up（向上）,down（向下）
	ManualPlannedTask  TaskSrv                  // 手动的任务队列
}

// 加载激活的项目
func (schedsrv *SchedulerSrv) LoadProjectData() (err error) {
	var (
		i          int
		projtablst []model.Project
	)
	// 初始化
	schedsrv.ProjDict = make(map[string]model.Project)

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewProjectDAO(session, common.PlantNr, "scheduler")
	if projtablst, err = dao.SelectActiveProjects(); err != nil {
		err = errors.New(fmt.Sprintf("获取工厂%d的激活的项目失败%v !", schedsrv.PlantNr, err))
		return
	}
	// 加载项目
	for i = 0; i < len(projtablst); i++ {
		schedsrv.ProjDict[projtablst[i].ProjectId] = projtablst[i]
	}
	return
}

// 加载产线及其相关数据
func (schedsrv *SchedulerSrv) LoadWorklineData(worklineid string) (err error) {
	var (
		i        int
		wltablst []base.WorkLine
	)
	// 初始化
	schedsrv.WorklineDict = make(map[string]*WorklineSrv)
	// 加载产线主数据
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wltab.NewWorkLineDAO(session, common.PlantNr, "scheduler")
	if wltablst, err = dao.SelectByType(worklineid); err != nil {
		err = errors.New(fmt.Sprintf("加载类型%s的产线数据失败%v!", worklineid, err))
		return
	}
	// 生成产线对象
	for i = 0; i < len(wltablst); i++ {
		wlsrv := WorklineSrv{}
		wlsrv.Init(wltablst[i])
		schedsrv.WorklineDict[wlsrv.WorklineId] = &wlsrv
	}
	return
}

// 加载时间模型
func (schedsrv *SchedulerSrv) LoadTimemodelData(worklineid string) (err error) {
	var (
		wotablst []om.WorkOrder
	)
	//查询边界开始日期
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wotab.NewWorkOrderDAO(session, common.PlantNr, "scheduler")
	if wotablst, err = dao.SelectLockedTasks(schedsrv.StartDate, schedsrv.EndDate, worklineid); err != nil {
		err = errors.New("加载已开始任务失败！")
		return
	}
	if len(wotablst) > 1 {
		PlanStartDate := wotablst[0].PlanStartDate.Restore()
		if PlanStartDate.Unix() < schedsrv.StartDate.Unix() {
			schedsrv.StartDate = PlanStartDate
		}
	}
	// 初始化
	schedsrv.Tmsrv = TimeModelSrv{PlantNr: schedsrv.PlantNr, StartDate: schedsrv.StartDate, EndDate: schedsrv.EndDate}
	// 加载指定日期范畴内的时间模型相关主数据
	err = schedsrv.Tmsrv.LoadBasicData(schedsrv.StartDate, schedsrv.EndDate)
	if err != nil {
		fmt.Println(err)
		return
	}
	// 生成日模型的时间线列表
	err = schedsrv.Tmsrv.GenDayModelLineArray()
	if err != nil {
		fmt.Println(err)
		return
	}
	return
}

// 加载产线班次的实际出勤数据，指定时段，没有按班次指定的默认值填充
func (schedsrv *SchedulerSrv) LoadWorkLineWorkShiftData() (err error) {
	// 遍历产线
	for _, wlsrv := range schedsrv.WorklineDict {
		// 加载产线指定时段内的实际出勤数据
		if err = wlsrv.LoadWorkShiftData(schedsrv.StartDate, schedsrv.EndDate); err != nil {
			err = errors.New(fmt.Sprintf("加载产线%s的排班数据出错%v!", wlsrv.WorklineId, err))
			return
		}
	}

	return
}

// 加载产品换型矩阵
func (schedsrv *SchedulerSrv) LoadSetupData() (err error) {
	var (
		i           int
		setupsrv    SetupSrv
		setuptablst []model.SetupMatrix
	)

	// 初始化
	schedsrv.SetupDict = make(map[string]SetupSrv)
	// 加载换型数据
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSetupMatrixDAO(session, common.PlantNr, "scheduler")
	if setuptablst, err = dao.SelectAll(); err != nil {
		err = errors.New(fmt.Sprintf("加载工厂%d的换型主数据出错%v!", schedsrv.PlantNr, err))
		return
	}
	for i = 0; i < len(setuptablst); i++ {
		setupsrv = SetupSrv{}
		setupsrv.Init(setuptablst[i])
		schedsrv.SetupDict[setupsrv.GetKey()] = setupsrv
	}

	return
}

// 生成产线的调度曲线
func (schedsrv *SchedulerSrv) GenerateWorkLineTimeCurve() (err error) {
	// 遍历产线
	for _, wlsrv := range schedsrv.WorklineDict {
		// 基于产线的时间模型和排班获取产线在指定时间区间内的时间线集合
		err := wlsrv.GetWorkDayList(&schedsrv.Tmsrv)
		if err != nil {
			fmt.Println(err)
		}
		wlsrv.GenerateTimeCurve(&schedsrv.Tmsrv)
	}

	return
}

// 加载调度任务
func (schedsrv *SchedulerSrv) LoadSchedTaskData(worklineid string) (err error) {
	var (
		i        int
		tasksrv  TaskSrv
		wotablst []om.WorkOrder
	)
	// 初始化
	schedsrv.LockedTaskArray = []TaskSrv{}
	schedsrv.PlannedTaskArray = []TaskSrv{}
	schedsrv.UnPlannedTaskArray = []TaskSrv{}
	// 加载调度任务数据
	// 1. 按实际开始时间先后加载状态 >= 24已开始的任务

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wotab.NewWorkOrderDAO(session, common.PlantNr, "scheduler")
	if wotablst, err = dao.SelectStartedTasks(schedsrv.StartDate, schedsrv.EndDate, worklineid); err != nil {
		err = errors.New("加载已开始任务失败！")
		return
	}
	for i = 0; i < len(wotablst); i++ {
		tasksrv = TaskSrv{}
		tasksrv.Init(wotablst[i])
		schedsrv.LockedTaskArray = append(schedsrv.LockedTaskArray, tasksrv)
	}
	// 2. 按计划开始时间先后加载状态 = 20  的已计划任务
	if wotablst, err = dao.SelectPlannedTasks(schedsrv.StartDate, schedsrv.EndDate, worklineid); err != nil {
		err = errors.New("加载已开始任务失败！")
		return
	}
	for i = 0; i < len(wotablst); i++ {
		tasksrv = TaskSrv{}
		tasksrv.Init(wotablst[i])
		schedsrv.PlannedTaskArray = append(schedsrv.PlannedTaskArray, tasksrv)
	}
	// 3. 按指定规则加载未计划的任务
	if wotablst, err = dao.SelectUnPlannedTasks(worklineid); err != nil {
		err = errors.New("加载已开始任务失败！")
		return
	}
	for i = 0; i < len(wotablst); i++ {
		tasksrv = TaskSrv{}
		tasksrv.Init(wotablst[i])
		schedsrv.UnPlannedTaskArray = append(schedsrv.UnPlannedTaskArray, tasksrv)
	}

	return
}

// 撤回计划，将未下达的任务全部更新为未计划
func (schedsrv *SchedulerSrv) RetrieveSchedule() {
	var schedTask TaskSrv

	// 将已计划的任务队列中的任务撤消到待调度任务队列
	for _, schedTask = range schedsrv.PlannedTaskArray {
		schedTask.Status = common.WO_STATUS_UNPLANNED
		schedsrv.UnPlannedTaskArray = append(schedsrv.UnPlannedTaskArray, schedTask)
	}
	schedsrv.PlannedTaskArray = []TaskSrv{}
	schedsrv.SortUnplannedTask()
}

// 获取产线上产品与产品之间的换型时间
func (schedsrv *SchedulerSrv) GetSetupDuration(stpsrv SetupSrv) time.Duration {
	var (
		key string
		ok  bool
	)

	if stpsrv.FromArtId == "" {
		key = "*"
	} else {
		key = stpsrv.FromArtId
	}
	if stpsrv.ToArtId == "" {
		key = key + "*"
	} else {
		key = key + stpsrv.ToArtId
	}
	if stpsrv.WorklineId == "" {
		key = key + "*"
	} else {
		key = key + stpsrv.WorklineId
	}

	if stpsrv, ok = schedsrv.SetupDict[key]; !ok {
		return 0
	} else {
		return stpsrv.SetupDura
	}
}

// 调度任务到产线，对不同状态的任务分别处理，调度后将任务添加到产线的调度任务列表
func (schedsrv *SchedulerSrv) ScheduleTask(worklineid string, task *TaskSrv) (err error) {
	var (
		iLen               int
		startTime, endTime time.Time
		setupsrv           SetupSrv
		tlinesrv           TimeLineSrv
		ok, needSetup      bool
	)

	// 获取产线对象
	wlsrv, ok := schedsrv.WorklineDict[worklineid]
	if !ok {
		err = errors.New(fmt.Sprintf("产线%s不存在！", worklineid))
		return
	}
	// 获取产线上一个任务的计划结束时间作为当前计划任务的开始时间，如果没有，则以计划时间曲线的开始时间和当前时间的最大值为准
	iLen = len(wlsrv.SchedTaskArray)
	needSetup = false
	if iLen <= 0 {
		startTime = utils.MaxTime(time.Now(), wlsrv.TimeCurve.StartTime)
	} else {
		idx := iLen - 1
		startTime = wlsrv.SchedTaskArray[idx].SchedEndTime
		// 获取换型时间
		setupsrv = SetupSrv{}
		setupsrv.WorklineId = wlsrv.WorklineId
		setupsrv.FromArtId = wlsrv.SchedTaskArray[idx].ArtId
		setupsrv.ToArtId = task.ArtId
		setupsrv.SetupDura = schedsrv.GetSetupDuration(setupsrv)
		needSetup = true
	}
	// 如果指定任务的最早开始时间，则依次为准
	if task.EarliestStartTime.Unix() > startTime.Unix() {
		startTime = task.EarliestStartTime
	}
	// 如果需要换型，则任务实际开始时间等于换型结束时间
	if needSetup && (setupsrv.SetupDura > 0) {
		task.SetupStartTime = startTime
		task.SetupEndTime = startTime.Add(setupsrv.SetupDura)
		startTime = task.SetupEndTime
	}
	// 基于工单不同的状态做不同的调度处理
	tlinesrv = TimeLineSrv{}
	switch task.Status {
	case common.WO_STATUS_UNPLANNED:
		//最后开始时间要和当前时间做对比
		timeNow := time.Now().Local()
		if timeNow.Unix() >= startTime.Unix() {
			startTime = timeNow
		}
		startTime, endTime = wlsrv.TimeCurve.CalEndTime(startTime, task.Duration)
		tlinesrv.StartTime = startTime
		tlinesrv.EndTime = endTime
		task.SchedStartTime = startTime
		task.SchedEndTime = endTime
	case common.WO_STATUS_PLANNED:
		// 固定计划开始和结束时间
		tlinesrv.StartTime = task.SchedStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_LOCKED:
		// 固定计划开始和结束时间
		tlinesrv.StartTime = task.SchedStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_RELEASED:
		// 固定计划开始和结束时间
		tlinesrv.StartTime = task.SchedStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_INTERRUPT:
		// 固定计划开始和结束时间
		tlinesrv.StartTime = task.SchedStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_RUNNING:
		// 固定时间开始和计划结束时间
		tlinesrv.StartTime = task.ActStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_FINISHED:
		tlinesrv.StartTime = task.ActStartTime
		tlinesrv.EndTime = task.ActEndTime
	default:

	}
	// 将调度任务添加到产线的调度队列中
	task.SchedResId = wlsrv.WorklineId
	wlsrv.SchedTaskArray = append(wlsrv.SchedTaskArray, *task)
	if wlsrv.SchedTimeCurve.TimeLineList == nil {
		return
	}
	err = wlsrv.SchedTimeCurve.SubTimeLine(tlinesrv)
	if err != nil {

		return
	}
	wlsrv.SchedTimeCurve.GetLineArray()
	return
}

// 调度任务到产线，对不同状态的任务分别处理，调度后将任务添加到产线的调度任务列表
func (schedsrv *SchedulerSrv) ManualrScheduleTask(worklineid string, task *TaskSrv) (err error) {
	var (
		iLen               int
		startTime, endTime time.Time
		setupsrv           SetupSrv
		tlinesrv           TimeLineSrv
		ok, needSetup      bool
	)

	// 获取产线对象
	wlsrv, ok := schedsrv.WorklineDict[worklineid]
	if !ok {
		err = errors.New(fmt.Sprintf("产线%s不存在！", worklineid))
		return
	}
	// 获取产线上一个任务的计划结束时间作为当前计划任务的开始时间，如果没有，则以计划时间曲线的开始时间和当前时间的最大值为准
	iLen = len(wlsrv.SchedTaskArray)
	needSetup = false
	if iLen <= 0 {
		startTime = utils.MaxTime(time.Now(), wlsrv.TimeCurve.StartTime)
	} else {
		idx := iLen - 1
		startTime = wlsrv.SchedTaskArray[idx].SchedEndTime
		// 获取换型时间
		setupsrv = SetupSrv{}
		setupsrv.WorklineId = wlsrv.WorklineId
		setupsrv.FromArtId = wlsrv.SchedTaskArray[idx].ArtId
		setupsrv.ToArtId = task.ArtId
		setupsrv.SetupDura = schedsrv.GetSetupDuration(setupsrv)
		needSetup = true
	}
	// 如果指定任务的最早开始时间，则依次为准
	if task.EarliestStartTime.Unix() > startTime.Unix() {
		startTime = task.EarliestStartTime
	}
	// 如果需要换型，则任务实际开始时间等于换型结束时间
	if needSetup && (setupsrv.SetupDura > 0) {
		task.SetupStartTime = startTime
		task.SetupEndTime = startTime.Add(setupsrv.SetupDura)
		startTime = task.SetupEndTime
	}
	// 基于工单不同的状态做不同的调度处理
	tlinesrv = TimeLineSrv{}
	switch task.Status {
	case common.WO_STATUS_UNPLANNED:
		//最后开始时间要和当前时间做对比
		timeNow := time.Now().Local()
		if timeNow.Unix() >= startTime.Unix() {
			startTime = timeNow
		}
		//判断开始时间
		if task.SchedStartTime.Unix() > startTime.Unix() {
			startTime = task.SchedStartTime
		}
		startTime, endTime = wlsrv.TimeCurve.CalEndTime(startTime, task.Duration)
		tlinesrv.StartTime = startTime
		tlinesrv.EndTime = endTime
		task.SchedStartTime = startTime
		task.SchedEndTime = endTime
	case common.WO_STATUS_PLANNED:
		//最后开始时间要和当前时间做对比
		timeNow := time.Now().Local()
		if timeNow.Unix() >= startTime.Unix() {
			startTime = timeNow
		}
		//判断开始时间
		if task.SchedStartTime.Unix() > startTime.Unix() {
			startTime = task.SchedStartTime
		}
		// 固定计划开始和结束时间
		startTime, endTime = wlsrv.TimeCurve.CalEndTime(startTime, task.Duration)
		tlinesrv.StartTime = startTime
		tlinesrv.EndTime = endTime
		task.SchedStartTime = startTime
		task.SchedEndTime = endTime
	case common.WO_STATUS_LOCKED:
		// 固定计划开始和结束时间
		tlinesrv.StartTime = task.SchedStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_RELEASED:
		// 固定计划开始和结束时间
		tlinesrv.StartTime = task.SchedStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_INTERRUPT:
		// 固定计划开始和结束时间
		tlinesrv.StartTime = task.SchedStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_RUNNING:
		// 固定时间开始和计划结束时间
		tlinesrv.StartTime = task.ActStartTime
		tlinesrv.EndTime = task.SchedEndTime
	case common.WO_STATUS_FINISHED:
		tlinesrv.StartTime = task.ActStartTime
		tlinesrv.EndTime = task.ActEndTime
	default:

	}
	// 将调度任务添加到产线的调度队列中
	task.SchedResId = wlsrv.WorklineId
	wlsrv.SchedTaskArray = append(wlsrv.SchedTaskArray, *task)
	// 更新产线的时间线
	err = wlsrv.SchedTimeCurve.SubTimeLine(tlinesrv)
	if err != nil {
		return
	}
	wlsrv.SchedTimeCurve.GetLineArray()
	return
}


// 修复产线的调度计划，生产中的结束时间、未开始的任务的计划开始和结束时间的重新计算
func (schedsrv *SchedulerSrv) RepairSchedule(worklineid string) (err error) {
	var (
		setupsrv      SetupSrv
		tlinesrv      TimeLineSrv
		i             int
		startTime     time.Time
		pevstartTime     time.Time
		ok, needSetup bool
	)

	// 获取产线对象
	wlsrv, ok := schedsrv.WorklineDict[worklineid]
	if !ok {
		err = errors.New(fmt.Sprintf("产线%s不存在！", worklineid))
		return
	}

	// 初始化产线的调度时间线
	wlsrv.SchedTimeCurve = wlsrv.TimeCurve
	// 遍历工单逐个依次调度
	for i = 0; i < len(wlsrv.SchedTaskArray); i++ {
		// 获取任务的计划起始时间点
		needSetup = false
		if wlsrv.SchedTaskArray[i].Status <= common.WO_STATUS_PLANNED {
			startTime = utils.MaxTime(time.Now(), wlsrv.TimeCurve.StartTime)
			if i > 0{
				pevstartTime = wlsrv.SchedTaskArray[i-1].SchedEndTime
				if pevstartTime.Unix() > startTime.Unix(){
					startTime = pevstartTime
				}
			}
			// 获取换型时间
			setupsrv = SetupSrv{}
			setupsrv.WorklineId = wlsrv.WorklineId
			setupsrv.FromArtId = wlsrv.SchedTaskArray[i-1].ArtId
			setupsrv.ToArtId = wlsrv.SchedTaskArray[i].ArtId
			setupsrv.SetupDura = schedsrv.GetSetupDuration(setupsrv)
			startTime = utils.MaxTime(startTime, wlsrv.SchedTaskArray[i].EarliestStartTime)
			needSetup = true
		} else  {
			if i == 0{
				startTime = wlsrv.SchedTaskArray[i].SchedStartTime
			}else{
				startTime = wlsrv.SchedTaskArray[i-1].SchedEndTime
			}
		}

		// 如果需要换型，计算任务的换型开始和结束时间
		// 对于已经开始的工单不再计算换型时间
		if wlsrv.SchedTaskArray[i].Status < common.WO_STATUS_RUNNING && needSetup {
			wlsrv.SchedTaskArray[i].SetupStartTime = startTime
			wlsrv.SchedTaskArray[i].SetupEndTime = startTime.Add(setupsrv.SetupDura)
			wlsrv.SchedTaskArray[i].SchedStartTime = wlsrv.SchedTaskArray[i].SetupEndTime
		} else {
			wlsrv.SchedTaskArray[i].SetupStartTime = startTime
			wlsrv.SchedTaskArray[i].SetupEndTime = startTime
			wlsrv.SchedTaskArray[i].SchedStartTime = startTime
		}
		// 基于任务的不同状态计算开始结束时间
		tlinesrv = TimeLineSrv{}
		switch wlsrv.SchedTaskArray[i].Status {
		case common.WO_STATUS_UNPLANNED:
			wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].SchedEndTime = wlsrv.TimeCurve.CalEndTime(wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].Duration)
			tlinesrv.StartTime = wlsrv.SchedTaskArray[i].SchedStartTime
			tlinesrv.EndTime = wlsrv.SchedTaskArray[i].SchedEndTime
		case common.WO_STATUS_PLANNED:
			wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].SchedEndTime = wlsrv.TimeCurve.CalEndTime(wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].Duration)
			tlinesrv.StartTime = wlsrv.SchedTaskArray[i].SchedStartTime
			tlinesrv.EndTime = wlsrv.SchedTaskArray[i].SchedEndTime
		case common.WO_STATUS_LOCKED:
			wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].SchedEndTime = wlsrv.TimeCurve.CalEndTime(wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].Duration)
			tlinesrv.StartTime = wlsrv.SchedTaskArray[i].SchedStartTime
			tlinesrv.EndTime = wlsrv.SchedTaskArray[i].SchedEndTime
		case common.WO_STATUS_RELEASED:
			wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].SchedEndTime = wlsrv.TimeCurve.CalEndTime(wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].Duration)
			tlinesrv.StartTime = wlsrv.SchedTaskArray[i].SchedStartTime
			tlinesrv.EndTime = wlsrv.SchedTaskArray[i].SchedEndTime
		case common.WO_STATUS_RUNNING:
			nowTime := time.Now()
			if wlsrv.SchedTaskArray[i].SchedEndTime.Unix() > nowTime.Unix(){
				percent := (wlsrv.SchedTaskArray[i].PlanQty - wlsrv.SchedTaskArray[i].FinishedQty) / wlsrv.SchedTaskArray[i].PlanQty
				seconds := wlsrv.SchedTaskArray[i].Duration.Seconds() * percent
				_, wlsrv.SchedTaskArray[i].SchedEndTime = wlsrv.TimeCurve.CalEndTime(time.Now(), time.Duration(seconds)*time.Second)
				tlinesrv.StartTime = wlsrv.SchedTaskArray[i].ActStartTime
				tlinesrv.EndTime = wlsrv.SchedTaskArray[i].SchedEndTime
			}else{
				wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].SchedEndTime = wlsrv.TimeCurve.CalEndTime(wlsrv.SchedTaskArray[i].SchedStartTime, wlsrv.SchedTaskArray[i].Duration)
				tlinesrv.StartTime = wlsrv.SchedTaskArray[i].SchedStartTime
				tlinesrv.EndTime = wlsrv.SchedTaskArray[i].SchedEndTime
			}
		case common.WO_STATUS_FINISHED:
			tlinesrv.StartTime = wlsrv.SchedTaskArray[i].ActStartTime
			tlinesrv.EndTime = wlsrv.SchedTaskArray[i].ActEndTime
		default:
		}

		// 将任务从调度曲线中扣除并更新产线的时间曲线
		wlsrv.SchedTimeCurve.SubTimeLine(tlinesrv)
		wlsrv.SchedTimeCurve.GetLineArray()
	}

	return
}

// 保存计划排程结果,只保存更新状态 < 80的订单
func (schedsrv *SchedulerSrv) SaveSchedule() (err error) {
	var (
		i, lastidx, prevstatus int
		eventsrv               EventSrv
	)

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wotab.NewWorkOrderDAO(session, common.PlantNr, "scheduler")
	wost := wotab.NewWorkOrderStatusDAO(session, common.PlantNr, "scheduler")

	// 已锁定、已下达、已开始的订单
	for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
		// 获取工单上次的状态
		lastidx = len(schedsrv.LockedTaskArray[i].WorkOrderStatus.WorkOrderStatusRecLst) - 1
		if lastidx < 0 {
			prevstatus = 0
		} else {
			prevstatus = schedsrv.LockedTaskArray[i].WorkOrderStatus.WorkOrderStatusRecLst[lastidx].Status
		}

		switch schedsrv.LockedTaskArray[i].Status {
		case common.WO_STATUS_LOCKED: // 已锁定
			schedsrv.LockedTaskArray[i].WorkOrderStatus.Status = common.WO_STATUS_RELEASED
			if schedsrv.LockedTaskArray[i].FixStartTime {
				schedsrv.LockedTaskArray[i].Wotab.FixStartTimeToggle = true
			} else {
				schedsrv.LockedTaskArray[i].Wotab.FixStartTimeToggle = false
			}
			schedsrv.LockedTaskArray[i].Wotab.PlanResourceId = schedsrv.LockedTaskArray[i].SchedResId
			schedsrv.LockedTaskArray[i].Wotab.PlanStartTime = grmi.DateTime(schedsrv.LockedTaskArray[i].SchedStartTime)

		case common.WO_STATUS_RELEASED: // 已下达
			schedsrv.LockedTaskArray[i].WorkOrderStatus.Status = common.WO_STATUS_RELEASED
			if schedsrv.LockedTaskArray[i].FixStartTime {
				schedsrv.LockedTaskArray[i].Wotab.FixStartTimeToggle = true
			} else {
				schedsrv.LockedTaskArray[i].Wotab.FixStartTimeToggle = false
			}
			schedsrv.LockedTaskArray[i].Wotab.PlanResourceId = schedsrv.LockedTaskArray[i].SchedResId
			schedsrv.LockedTaskArray[i].Wotab.PlanStartTime = grmi.DateTime(schedsrv.LockedTaskArray[i].SchedStartTime)
		case common.WO_STATUS_INTERRUPT: // 已中断
		case common.WO_STATUS_RUNNING:
		// 生产中
		default:
		}
		schedsrv.LockedTaskArray[i].Wotab.PlanEndTime = grmi.DateTime(schedsrv.LockedTaskArray[i].SchedEndTime)
		// 设置更新时间和人员，更新订单计划时间
		SetTableLastModify(&schedsrv.LockedTaskArray[i].Wotab, schedsrv.LockedTaskArray[i].Wotab, common.MODIFY_MODE_UPDATE, "sched_service")
		dao.UpdateOne(&schedsrv.LockedTaskArray[i].Wotab)
		// 如果与变更前的状态不一致，则插入状态变更记录
		if schedsrv.LockedTaskArray[i].Status != prevstatus {
			// 更新工单状态
			SetTableLastModify(&schedsrv.LockedTaskArray[i].WorkOrderStatus, schedsrv.LockedTaskArray[i].WorkOrderStatus, common.MODIFY_MODE_UPDATE, "sched_service")
			wost.UpdateOne(&schedsrv.LockedTaskArray[i].WorkOrderStatus)
			// 创建工单状态变更记录
			eventsrv = EventSrv{}
			eventsrv.TriggerService = "sched_service"
			eventsrv.TriggerEvent = common.OUTPUT_EVENT_WO_LOCK
			eventsrv.TriggerObjectId = schedsrv.LockedTaskArray[i].TaskId
			eventsrv.OutputEvent = common.OUTPUT_EVENT_WO_LOCK
			eventsrv.OutputObjectId = schedsrv.LockedTaskArray[i].TaskId
			if err = schedsrv.LockedTaskArray[i].CreateStatusRecord(prevstatus, eventsrv); err != nil {
				err = errors.New(fmt.Sprintf("为工单%s创建状态记录出错%v!", schedsrv.LockedTaskArray[i].TaskId, err))
				return
			}
		}
	}

	// 已计划订单，更新状态和计划时间
	for i = 0; i < len(schedsrv.PlannedTaskArray); i++ {
		// 获取工单上次的状态
		lastidx = len(schedsrv.PlannedTaskArray[i].WorkOrderStatus.WorkOrderStatusRecLst) - 1
		if lastidx < 0 {
			prevstatus = 0
		} else {
			prevstatus = schedsrv.PlannedTaskArray[i].WorkOrderStatus.WorkOrderStatusRecLst[lastidx].Status
		}

		schedsrv.PlannedTaskArray[i].WorkOrderStatus.Status = common.WO_STATUS_PLANNED
		if schedsrv.PlannedTaskArray[i].FixStartTime {
			schedsrv.PlannedTaskArray[i].Wotab.FixStartTimeToggle = true
		} else {
			schedsrv.PlannedTaskArray[i].Wotab.FixStartTimeToggle = false
		}
		schedsrv.PlannedTaskArray[i].Wotab.PlanResourceId = schedsrv.LockedTaskArray[i].SchedResId
		schedsrv.PlannedTaskArray[i].Wotab.PlanStartTime = grmi.DateTime(schedsrv.LockedTaskArray[i].SchedStartTime)
		schedsrv.PlannedTaskArray[i].Wotab.PlanEndTime = grmi.DateTime(schedsrv.LockedTaskArray[i].SchedEndTime)
		// 设置更新时间和人员
		SetTableLastModify(&schedsrv.PlannedTaskArray[i].Wotab, schedsrv.PlannedTaskArray[i].Wotab, common.MODIFY_MODE_UPDATE, "sched_service")
		// 更新订单和状态
		dao.UpdateOne(&schedsrv.PlannedTaskArray[i].Wotab)
		// 如果与变更前的状态不一致，则插入状态变更记录
		if schedsrv.PlannedTaskArray[i].Status != prevstatus {
			SetTableLastModify(&schedsrv.PlannedTaskArray[i].WorkOrderStatus, schedsrv.PlannedTaskArray[i].WorkOrderStatus, common.MODIFY_MODE_UPDATE, "sched_service")
			wost.UpdateOne(&schedsrv.PlannedTaskArray[i].WorkOrderStatus)
			// 创建工单状态变更记录
			eventsrv = EventSrv{}
			eventsrv.TriggerService = "sched_service"
			eventsrv.TriggerEvent = common.OUTPUT_EVENT_WO_PLAN
			eventsrv.TriggerObjectId = schedsrv.LockedTaskArray[i].TaskId
			eventsrv.OutputEvent = common.OUTPUT_EVENT_WO_PLAN
			eventsrv.OutputObjectId = schedsrv.LockedTaskArray[i].TaskId
			if err = schedsrv.PlannedTaskArray[i].CreateStatusRecord(prevstatus, eventsrv); err != nil {
				err = errors.New(fmt.Sprintf("为工单%s创建状态记录出错%v!", schedsrv.PlannedTaskArray[i].TaskId, err))
				return
			}
		}
	}

	// 未计划订单，更新状态
	for i = 0; i < len(schedsrv.UnPlannedTaskArray); i++ {
		// 获取工单上次的状态
		lastidx = len(schedsrv.UnPlannedTaskArray[i].WorkOrderStatus.WorkOrderStatusRecLst) - 1
		if lastidx < 0 {
			prevstatus = 0
		} else {
			prevstatus = schedsrv.UnPlannedTaskArray[i].WorkOrderStatus.WorkOrderStatusRecLst[lastidx].Status
		}

		// 如果与变更前的状态不一致，则插入状态变更记录
		if schedsrv.UnPlannedTaskArray[i].Status != prevstatus {
			SetTableLastModify(&schedsrv.UnPlannedTaskArray[i].WorkOrderStatus, schedsrv.UnPlannedTaskArray[i].WorkOrderStatus, common.MODIFY_MODE_UPDATE, "sched_service")
			wost.UpdateOne(&schedsrv.UnPlannedTaskArray[i].WorkOrderStatus)
			// 创建工单状态变更记录
			eventsrv = EventSrv{}
			eventsrv.TriggerService = "scheduler"
			eventsrv.TriggerEvent = common.OUTPUT_EVENT_WO_NEW
			eventsrv.TriggerObjectId = schedsrv.LockedTaskArray[i].TaskId
			eventsrv.OutputEvent = common.OUTPUT_EVENT_WO_NEW
			eventsrv.OutputObjectId = schedsrv.LockedTaskArray[i].TaskId
			if err = schedsrv.UnPlannedTaskArray[i].CreateStatusRecord(prevstatus, eventsrv); err != nil {
				err = errors.New(fmt.Sprintf("为工单%s创建状态记录出错%v!", schedsrv.UnPlannedTaskArray[i].TaskId, err))
				return
			}
		}
	}

	return
}

// 下达产线工单，基于序列订单的WIP数量控制，不含已下线的，生成序列订单
func (schedsrv *SchedulerSrv) ReleaseSchedule(worklineid string) (err error) {
	uid, err := uuid.NewV1()
	if err != nil {
		return err
	}
	releaseId := uid.String()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("recovery error:", err)
		}
	}()
	innerLog, _ := logger.NewLogger("scheduler", "scheduler")
	var (
		ok                             bool
		dura                           time.Duration
		i, wipqty, totalrelqty, relqty int
		PlanEndDate, PlanEndTime       time.Time
	)

	// 获取产线对象
	wlsrv, ok := schedsrv.WorklineDict[worklineid]
	if !ok {
		err = errors.New(fmt.Sprintf("产线%s不存在！", worklineid))
		return err
	}
	innerLog.Debug("ReleaseSchedule start", "release id:"+releaseId, "workline id:"+worklineid)
	// 获取调度到产线上状态为>=24已锁定计划的批次订单并依次下达
	// 对于状态为24的生产工单置位26，状态默认为20已计划
	// 基于产线不同的派工方式依次对序列订单派工
	switch wlsrv.RelCtrlWay {
	case common.LINE_REL_BY_QTY:
		// 获取在制数量 >=26 && <80
		if wipqty, err = schedsrv.GetWipQty(worklineid); err != nil {
			return err
		}
		innerLog.Debug("ReleaseSchedule release by qty current wait qty:"+strconv.Itoa(wipqty), "release id:"+releaseId, "workline id:"+worklineid)
		// 比较确认需要下达的序列订单总计数量
		totalrelqty = wlsrv.RelCtrlQty - wipqty
		innerLog.Debug("ReleaseSchedule release by qty totalrelqty:"+strconv.Itoa(totalrelqty), "release id :"+releaseId)
		err = schedsrv.LoadLockedTaskArray(worklineid, releaseId)
		if err != nil {
			return err
		}

		innerLog.Debug("ReleaseSchedule LoadLockedTaskArray :" + strconv.Itoa(wipqty))
		// 按照已下达的批次订单的排序号，依次获取未下达的序列订单，并按照指定数量依次下达
		for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
			if totalrelqty == 0 {
				innerLog.Debug("ReleaseSchedule total relqty is 0 workOrderId :"+schedsrv.LockedTaskArray[i].Wotab.WorkOrderId, "release id :"+releaseId)
				return nil
			}
			innerLog.Debug("ReleaseSchedule workOrderId :"+schedsrv.LockedTaskArray[i].Wotab.WorkOrderId, "release id :"+releaseId)
			if schedsrv.LockedTaskArray[i].WorkOrderQty.PlanQty-schedsrv.LockedTaskArray[i].WorkOrderQty.ReleasedQty <= 0 {
				continue
			}
			if int(schedsrv.LockedTaskArray[i].WorkOrderQty.PlanQty-schedsrv.LockedTaskArray[i].WorkOrderQty.ReleasedQty) >= totalrelqty {
				innerLog.Debug("ReleaseSchedule beyond relqty:"+strconv.Itoa(relqty)+" totalrelqty: "+strconv.Itoa(totalrelqty), "release id :"+releaseId)
				relqty = totalrelqty
				if err = schedsrv.LockedTaskArray[i].ReleaseSerialOrder(relqty, releaseId); err != nil {
					err = errors.New(fmt.Sprintf("下达工单%s出错%v!", schedsrv.LockedTaskArray[i].TaskId, err))
					return
				}
				break
			} else {
				relqty = int(schedsrv.LockedTaskArray[i].WorkOrderQty.PlanQty - schedsrv.LockedTaskArray[i].WorkOrderQty.ReleasedQty)
				innerLog.Debug("ReleaseSchedule below relqty:"+strconv.Itoa(relqty), "release id :"+releaseId)
				if err = schedsrv.LockedTaskArray[i].ReleaseSerialOrder(relqty, releaseId); err != nil {
					err = errors.New(fmt.Sprintf("下达工单%s出错%v!", schedsrv.LockedTaskArray[i].TaskId, err))
					return
				}
				totalrelqty = totalrelqty - relqty
				continue
			}
		}

	case common.LINE_REL_BY_DURATION:
		err = schedsrv.LoadLockedTaskArray(worklineid, releaseId)
		if err != nil {
			return err
		}
		// 按照已下达的批次订单的排序号，依次获取未下达的序列订单
		// 比较序列订单计划结束时间和要求交期的差值，满足则下达
		// 为每一个下达的序列订单生成订单消息，用于接口服务处理
		for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
			PlanEndDate = schedsrv.LockedTaskArray[i].Wotab.PlanEndDate.Restore()
			PlanEndTime = schedsrv.LockedTaskArray[i].Wotab.PlanEndDate.Restore()
			dura = PlanEndDate.Sub(PlanEndTime)
			if dura > wlsrv.RelCtrlDura {
				continue
			}
			//err = schedsrv.LockedTaskArray[i].LoadSerialOrders()
			relqty = int(schedsrv.LockedTaskArray[i].WorkOrderQty.PlanQty - schedsrv.LockedTaskArray[i].WorkOrderQty.ReleasedQty)
			if err = schedsrv.LockedTaskArray[i].ReleaseSerialOrder(relqty, releaseId); err != nil {
				err = errors.New(fmt.Sprintf("下达工单%s出错%v!", schedsrv.LockedTaskArray[i].TaskId, err))
				return
			}
			// 比较每一个序列订单的计划结束事件
			//for j = 0; j < len(schedsrv.LockedTaskArray[i].SerialTaskArray); j++ {
			//	PlanEndDate = schedsrv.LockedTaskArray[i].Wotab.PlanEndDate.Restore()
			//	PlanEndTime = schedsrv.LockedTaskArray[i].Wotab.PlanEndDate.Restore()
			//	dura = PlanEndDate.Sub(PlanEndTime)
			//	if dura > wlsrv.RelCtrlDura {
			//		continue
			//	}
			//	// 下达序列订单
			//	if err = schedsrv.LockedTaskArray[i].SerialTaskArray[j].Release(); err != nil {
			//		err = errors.New(fmt.Sprintf("下达工单%s的序列工单%s出错%v!", schedsrv.LockedTaskArray[i].TaskId, schedsrv.LockedTaskArray[i].SerialTaskArray[j].TaskId, err))
			//		return
			//	}
			//}
		}

	default:
	}
	return
}

// 获取产线的在制数量 >= 26 && < 80
func (schedsrv *SchedulerSrv) GetWipQty(worklineid string) (wipqty int, err error) {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wotab.NewSerialOrderDAO(session, common.PlantNr, "scheduler")
	if wipqty, err = dao.GetLineWIPQty(worklineid); err != nil {
		err = errors.New(fmt.Sprintf("获取产线%sWIP数量失败%v！", worklineid, err))
		return
	}
	return
}

// 为指定批次订单生成序列订单，订单状态为 24 已锁定
func (schedsrv *SchedulerSrv) CreateTaskSerialOrder(schedtask *TaskSrv) (err error) {
	var (
		i, createqty int
		projtab      model.Project
		stasksrv     SerialTaskSrv
		serordid     string
		ok           bool
	)

	// 判断工单状态是否满足
	if schedtask.WorkOrderStatus.Status != common.WO_STATUS_LOCKED {
		err = errors.New(fmt.Sprintf("调度工单%s的状态%d不满足生成序列订单的条件！", schedtask.TaskId, schedtask.WorkOrderStatus.Status))
		return
	}

	// 获取工单任务对应的项目号
	if projtab, ok = schedsrv.ProjDict[schedtask.ProjectId]; !ok {
		err = errors.New(fmt.Sprintf("调度工单%s的项目%s不存在！", schedtask.TaskId, schedtask.ProjectId))
		return
	}

	// 从项目号获取序列订单号对应的流水号并依次创建生产订单的序列订单

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wltab.NewSnrDAO(session, common.PlantNr, "scheduler")
	createqty = int(schedtask.WorkOrderQty.PlanQty - schedtask.WorkOrderQty.ReleasedQty)
	for i = 0; i < createqty; i++ {
		if serordid, err = dao.GetNextSnr(projtab.SerialOrderSnr); err != nil {
			err = errors.New(fmt.Sprintf("项目%s使用序列工单流水码%s生成序列工单号失败%v！", projtab.ProjectId, projtab.SerialOrderSnr, err))
			return
		}
		stasksrv = SerialTaskSrv{}
		stasksrv.TaskId = serordid
		stasksrv.ArtId = schedtask.ArtId
		stasksrv.TaskType = common.TASK_TYPE_SERIALORDER
		stasksrv.Status = common.WO_STATUS_PLANNED
		stasksrv.PlantNr = schedtask.PlantNr
		stasksrv.ProjectId = schedtask.ProjectId
		stasksrv.ParentTaskId = schedtask.TaskId
		stasksrv.PlanQty = 1
		stasksrv.SchedResId = schedtask.SchedResId
		stasksrv.EarliestStartTime = schedtask.EarliestStartTime
		stasksrv.LatestEndTime = schedtask.LatestEndTime
		eachitemsec := schedtask.Duration.Seconds() / float64(createqty)
		offsetdura := time.Duration(eachitemsec*float64(i)) * time.Second
		stasksrv.SchedStartTime = schedtask.SchedStartTime.Add(offsetdura)
		offsetdura = time.Duration(eachitemsec*float64(i+1)) * time.Second
		stasksrv.SchedEndTime = schedtask.SchedStartTime.Add(offsetdura)
		if err = stasksrv.Create(); err != nil {
			err = errors.New(fmt.Sprintf("工单%s的序列订单%s创建失败%v！", schedtask.TaskId, serordid, err))
			return
		}
	}

	return
}

// 处理工单状态事件
func (schedsrv *SchedulerSrv) ParseWorkOrderEvent(event string) (err error) {
	var (
		i                 int
		tasksrv           TaskSrv
		wostatusrectablst []om.WorkOrderStatusRecLst
	)

	// 加载指定事件的开口消息
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wotab.NewWorkOrderStatusRecLstDAO(session, common.PlantNr, "scheduler")
	if wostatusrectablst, err = dao.SelectByEvent(event); err != nil {
		err = errors.New(fmt.Sprintf("获取工单事件%s失败%v!", event, err))
		return
	}
	// 按照时间先后依次处理
	for i = 0; i < len(wostatusrectablst); i++ {
		tasksrv = TaskSrv{PlantNr: schedsrv.PlantNr, TaskId: wostatusrectablst[i].OutputObjectId}
		if err = tasksrv.Get(); err != nil {
			err = errors.New(fmt.Sprintf("获取工单%s出错%v!", wostatusrectablst[i].OutputObjectId, err))
			return
		}

		switch event {
		case common.OUTPUT_EVENT_WO_PLAN:
		case common.OUTPUT_EVENT_WO_LOCK:
			// 生成批次工单的序列订单
			if err = schedsrv.CreateTaskSerialOrder(&tasksrv); err != nil {
				err = errors.New(fmt.Sprintf("为工单%s生成序列订单出错%v!", tasksrv.TaskId, err))
				return
			}

		case common.OUTPUT_EVENT_WO_REL:
		case common.OUTPUT_EVENT_WO_RUN:
		case common.OUTPUT_EVENT_WO_CLOSE:
		case common.OUTPUT_EVENT_WO_CANCEL:
		default:
		}

		// 关闭事件消息
		wostatusrectablst[i].OutputStatus = common.OUTPUT_TRIGGER_HANDLED
		SetTableLastModify(&wostatusrectablst[i], wostatusrectablst[i], common.MODIFY_MODE_UPDATE, "msg_parser")

		if err = dao.UpdateOne(&wostatusrectablst[i]); err != nil {
			err = errors.New(fmt.Sprintf("关闭工单消息记录行%v出错%v!", wostatusrectablst[i], err))
			return
		}
	}

	return
}

// 处理序列工单状态事件
func (schedsrv *SchedulerSrv) ParseSerialOrderEvent(event string) (err error) {
	var (
		i                   int
		sertasksrv          SerialTaskSrv
		serostatusrectablst []om.SerialOrderStatusRecLst
	)

	// 加载指定事件的开口消息

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wotab.NewSerialOrderStatusRecLstDAO(session, common.PlantNr, "scheduler")

	if serostatusrectablst, err = dao.SelectByEvent(event); err != nil {
		err = errors.New(fmt.Sprintf("获取序列工单事件%s失败%v!", event, err))
		return
	}
	// 按照时间先后依次处理
	for i = 0; i < len(serostatusrectablst); i++ {
		sertasksrv = SerialTaskSrv{PlantNr: schedsrv.PlantNr, TaskId: serostatusrectablst[i].OutputObjectId}
		if err = sertasksrv.Get(); err != nil {
			err = errors.New(fmt.Sprintf("获取工单%s出错%v!", serostatusrectablst[i].OutputObjectId, err))
			return
		}

		switch event {
		case common.OUTPUT_EVENT_SER_NEW:
		case common.OUTPUT_EVENT_SER_REL:
		case common.OUTPUT_EVENT_SER_RUN:
		case common.OUTPUT_EVENT_SER_CLOSE:
		default:
		}

		// 关闭事件消息
		serostatusrectablst[i].OutputStatus = common.OUTPUT_TRIGGER_HANDLED
		SetTableLastModify(&serostatusrectablst[i], serostatusrectablst[i], common.MODIFY_MODE_UPDATE, "msg_parser")
		if err = dao.UpdateOne(&serostatusrectablst[i]); err != nil {
			err = errors.New(fmt.Sprintf("关闭序列工单消息记录行%v出错%v!", serostatusrectablst[i], err))
			return
		}
	}

	return
}

// 对调度任务进行排序
func (schedsrv *SchedulerSrv) SortUnplannedTask() {
	var (
		i         int
		sortTasks []interface{}
	)

	// 计算排序任务的排序属性
	for i = 0; i < len(schedsrv.UnPlannedTaskArray); i++ {

	}

	// 准备排序
	sortTasks = []interface{}{}
	for i = 0; i < len(schedsrv.UnPlannedTaskArray); i++ {
		sortTasks = append(sortTasks, schedsrv.UnPlannedTaskArray[i])
	}

	SortBody(sortTasks, func(p, q *interface{}) bool {
		v := reflect.ValueOf(*p)
		i := v.FieldByName("Create_time")
		v = reflect.ValueOf(*q)
		j := v.FieldByName("Create_time")
		return i.String() < j.String()
	})
}

// 调度引擎自动排程
func (schedsrv *SchedulerSrv) AutoSchedule() (err error) {
	var (
		i int
	)
	// 遍历已锁定任务,先占位
	for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
		schedsrv.ScheduleTask(schedsrv.LockedTaskArray[i].SchedResId, &schedsrv.LockedTaskArray[i])
	}
	// 遍历已计划任务，占位
	for i = 0; i < len(schedsrv.PlannedTaskArray); i++ {
		schedsrv.ScheduleTask(schedsrv.PlannedTaskArray[i].SchedResId, &schedsrv.PlannedTaskArray[i])
	}
	// 将未计划任务排序

	// 遍历未计划任务
	for i = 0; i < len(schedsrv.UnPlannedTaskArray); i++ {
		// 此处可以增加选择调度资源逻辑

		// 将未计划任务调度到指定的计划资源上
		schedsrv.ScheduleTask(schedsrv.UnPlannedTaskArray[i].SchedResId, &schedsrv.UnPlannedTaskArray[i])

	}
	return
}

// 调度引擎自动排程
func (schedsrv *SchedulerSrv) ManPowerSchedule() (err error) {
	var (
		i int
	)
	// 遍历已锁定任务,先占位
	for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
		schedsrv.ScheduleTask(schedsrv.LockedTaskArray[i].SchedResId, &schedsrv.LockedTaskArray[i])
	}
	// 遍历已计划任务，占位
	for i = 0; i < len(schedsrv.PlannedTaskArray); i++ {
		schedsrv.ScheduleTask(schedsrv.PlannedTaskArray[i].SchedResId, &schedsrv.PlannedTaskArray[i])
	}
	// 不对未计划任务排序

	return
}

// 调度引擎自动排程
func (schedsrv *SchedulerSrv) ManualrSchedule() (err error) {
	var (
		i int
	)
	// 遍历已锁定任务,先占位
	for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
		schedsrv.ManualrScheduleTask(schedsrv.LockedTaskArray[i].SchedResId, &schedsrv.LockedTaskArray[i])
	}
	// 遍历已计划任务，占位
	for i = 0; i < len(schedsrv.PlannedTaskArray); i++ {
		schedsrv.ManualrScheduleTask(schedsrv.PlannedTaskArray[i].SchedResId, &schedsrv.PlannedTaskArray[i])
	}
	// 不对未计划任务排序

	return
}

// 打印调度引擎排程结果
func (schedsrv *SchedulerSrv) PrintScheduleResult() (err error) {
	var (
		i int
	)
	// 遍历已锁定任务,先占位
	fmt.Println("已锁定任务：")
	for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
		fmt.Println(i, " -- ", fmt.Sprintf("生产订单：%s 计划产线：%s 计划开始时间：%v 计划结束时间：%v ",
			schedsrv.LockedTaskArray[i].TaskId, schedsrv.LockedTaskArray[i].SchedResId,
			schedsrv.LockedTaskArray[i].SchedStartTime, schedsrv.LockedTaskArray[i].SchedEndTime))
	}
	// 遍历已计划任务，占位
	fmt.Println("已计划任务：")
	for i = 0; i < len(schedsrv.PlannedTaskArray); i++ {
		fmt.Println(i, " -- ", fmt.Sprintf("生产订单：%s 计划产线：%s 计划开始时间：%v 计划结束时间：%v ",
			schedsrv.PlannedTaskArray[i].TaskId, schedsrv.PlannedTaskArray[i].SchedResId,
			schedsrv.PlannedTaskArray[i].SchedStartTime, schedsrv.PlannedTaskArray[i].SchedEndTime))
	}
	// 遍历未计划任务
	fmt.Println("未计划任务：")
	for i = 0; i < len(schedsrv.UnPlannedTaskArray); i++ {
		fmt.Println(i, " -- ", fmt.Sprintf("生产订单：%s 计划产线：%s 计划开始时间：%v 计划结束时间：%v ",
			schedsrv.UnPlannedTaskArray[i].TaskId, schedsrv.UnPlannedTaskArray[i].SchedResId,
			schedsrv.UnPlannedTaskArray[i].SchedStartTime, schedsrv.UnPlannedTaskArray[i].SchedEndTime))
	}
	return
}

// 派工根据产线加载调度任务
func (schedsrv *SchedulerSrv) LoadLockedTaskArray(workLineId string, releaseId string) (err error) {
	innerLog, _ := logger.NewLogger("scheduler", "scheduler")
	if schedsrv.LockedTaskArray == nil {
		schedsrv.LockedTaskArray = []TaskSrv{}
	}
	if len(schedsrv.LockedTaskArray) != 0 {
		return nil
	}
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := wotab.NewWorkOrderDAO(session, common.PlantNr, "scheduler")
	workOrderLi, err := dao.SelectLockWorkOrder(workLineId)
	if err != nil {
		innerLog.Error("LoadLockedTaskArray get locked work order failed, error: " + err.Error(), "release id:" + releaseId)
		return err
	}
	innerLog.Debug("LoadLockedTaskArray get locked work order length: " + strconv.Itoa(len(workOrderLi)), "release id:" + releaseId + " workline:" + workLineId)
	for _, workOrder := range workOrderLi {
		taskSrv := TaskSrv{}
		taskSrv.Init(workOrder)
		schedsrv.LockedTaskArray = append(schedsrv.LockedTaskArray, taskSrv)
	}
	return
}
