|
package schedule
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"leit.com/LAPP_GAAS_GFrame/models"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
// 调度器
|
|
type SchedulerSrv struct {
|
|
PlantNr int // 工厂号
|
|
StartDate time.Time
|
|
EndDate time.Time
|
|
Tmsrv TimeModelSrv // 时间模型
|
|
WorklineDict map[string]*WorklineSrv
|
|
SetupDict map[string]SetupSrv
|
|
ProjDict map[string]models.MeProject // 激活的项目字典
|
|
LockedTaskArray []TaskSrv // 已锁定的任务队列,包括:已下达、中断、生产中和已完成
|
|
PlannedTaskArray []TaskSrv // 已计划的任务队列
|
|
UnPlannedTaskArray []TaskSrv // 未计划的任务队列
|
|
}
|
|
|
|
// 加载激活的项目
|
|
func(schedsrv *SchedulerSrv)LoadProjectData()(err error){
|
|
var(
|
|
i int
|
|
projtab models.MeProject
|
|
projtablst []models.MeProject
|
|
)
|
|
// 初始化
|
|
schedsrv.ProjDict = make(map[string]models.MeProject)
|
|
// 读取激活的项目数据
|
|
projtab = models.MeProject{Plantnr: schedsrv.PlantNr}
|
|
if projtablst, err = projtab.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(wltype string)(err error){
|
|
var(
|
|
i int
|
|
wltab models.Workline
|
|
wltablst []models.Workline
|
|
wlsrv WorklineSrv
|
|
)
|
|
// 初始化
|
|
schedsrv.WorklineDict = make(map[string]*WorklineSrv)
|
|
// 加载产线主数据
|
|
wltab = models.Workline{Plantnr: schedsrv.PlantNr}
|
|
if wltablst, err = wltab.SelectByType(wltype); err != nil {
|
|
err = errors.New(fmt.Sprintf("加载类型%s的产线数据失败%v!",wltype, 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(){
|
|
// 初始化
|
|
schedsrv.Tmsrv = TimeModelSrv{PlantNr: schedsrv.PlantNr}
|
|
// 加载指定日期范畴内的时间模型相关主数据
|
|
schedsrv.Tmsrv.LoadBasicData(schedsrv.StartDate, schedsrv.EndDate)
|
|
// 生成日模型的时间线列表
|
|
schedsrv.Tmsrv.GenDayModelLineArray()
|
|
}
|
|
// 加载产线班次的实际出勤数据,指定时段,没有按班次指定的默认值填充
|
|
func(schedsrv *SchedulerSrv)LoadWorkLineWorkShiftData()(err error){
|
|
var(
|
|
wlsrv *WorklineSrv
|
|
)
|
|
// 遍历产线
|
|
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
|
|
setuptab models.MeSetupmatrix
|
|
setuptablst []models.MeSetupmatrix
|
|
)
|
|
|
|
// 初始化
|
|
schedsrv.SetupDict = make(map[string]SetupSrv)
|
|
// 加载换型数据
|
|
setuptab = models.MeSetupmatrix{Plantnr: schedsrv.PlantNr}
|
|
if setuptablst, err = setuptab.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){
|
|
var(
|
|
wlsrv *WorklineSrv
|
|
)
|
|
// 遍历产线
|
|
for _, wlsrv = range schedsrv.WorklineDict {
|
|
// 基于产线的时间模型和排班获取产线在指定时间区间内的时间线集合
|
|
wlsrv.GetWorkDayList(&schedsrv.Tmsrv)
|
|
wlsrv.GenerateTimeCurve(&schedsrv.Tmsrv)
|
|
}
|
|
|
|
return
|
|
}
|
|
// 加载调度任务
|
|
func(schedsrv *SchedulerSrv)LoadSchedTaskData()(err error){
|
|
var(
|
|
i int
|
|
tasksrv TaskSrv
|
|
wotab models.OmWorkorder
|
|
wotablst []models.OmWorkorder
|
|
)
|
|
// 初始化
|
|
schedsrv.LockedTaskArray = []TaskSrv{}
|
|
schedsrv.PlannedTaskArray = []TaskSrv{}
|
|
schedsrv.UnPlannedTaskArray = []TaskSrv{}
|
|
// 加载调度任务数据
|
|
// 1. 按实际开始时间先后加载状态 >= 39已开始的任务
|
|
wotab = models.OmWorkorder{}
|
|
if wotablst, err = wotab.SelectStartedTasks(schedsrv.StartDate, schedsrv.EndDate); 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 && < 39 的已计划任务
|
|
if wotablst, err = wotab.SelectPlannedTasks(schedsrv.StartDate, schedsrv.EndDate); 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 = wotab.SelectUnPlannedTasks(); 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)
|
|
}
|
|
|
|
return
|
|
}
|
|
// 撤回计划,将未下达的任务全部更新为未计划
|
|
func(schedsrv *SchedulerSrv)RetrieveSchedule(){
|
|
var schedTask TaskSrv
|
|
|
|
// 将已计划的任务队列中的任务撤消到待调度任务队列
|
|
for _, schedTask = range schedsrv.PlannedTaskArray{
|
|
schedTask.Status = 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(
|
|
wlsrv *WorklineSrv
|
|
iLen int
|
|
startTime,endTime time.Time
|
|
setupsrv SetupSrv
|
|
tlinesrv TimeLineSrv
|
|
ok,needSetup bool
|
|
)
|
|
|
|
// 获取产线对象
|
|
if wlsrv, ok = schedsrv.WorklineDict[worklineid]; !ok{
|
|
err = errors.New(fmt.Sprintf("产线%s不存在!",worklineid))
|
|
return
|
|
}
|
|
|
|
// 获取产线上一个任务的计划结束时间作为当前计划任务的开始时间,如果没有,则以计划时间曲线的开始时间和当前时间的最大值为准
|
|
iLen = len(wlsrv.SchedTaskArray)
|
|
needSetup = false
|
|
if iLen <= 0 {
|
|
startTime = MaxTime(time.Now(),wlsrv.TimeCurve.StartTime)
|
|
}else{
|
|
startTime = wlsrv.SchedTaskArray[iLen].SchedEndTime
|
|
// 获取换型时间
|
|
setupsrv = SetupSrv{}
|
|
setupsrv.WorklineId = wlsrv.WorklineId
|
|
setupsrv.FromArtId = wlsrv.SchedTaskArray[iLen].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 WO_STATUS_UNPLANNED:
|
|
endTime = wlsrv.TimeCurve.CalEndTime(startTime, task.Duration)
|
|
tlinesrv.StartTime = startTime
|
|
tlinesrv.EndTime = endTime
|
|
task.SchedEndTime = endTime
|
|
case WO_STATUS_PLANNED:
|
|
// 固定计划开始和结束时间
|
|
tlinesrv.StartTime = task.SchedStartTime
|
|
tlinesrv.EndTime = task.SchedEndTime
|
|
case WO_STATUS_RELEASED:
|
|
// 固定计划开始和结束时间
|
|
tlinesrv.StartTime = task.SchedStartTime
|
|
tlinesrv.EndTime = task.SchedEndTime
|
|
case WO_STATUS_RUNNING:
|
|
// 固定时间开始和计划结束时间
|
|
tlinesrv.StartTime = task.ActStartTime
|
|
tlinesrv.EndTime = task.SchedEndTime
|
|
case WO_STATUS_FINISHED:
|
|
tlinesrv.StartTime = task.ActStartTime
|
|
tlinesrv.EndTime = task.ActEndTime
|
|
default:
|
|
|
|
}
|
|
|
|
// 将调度任务添加到产线的调度队列中
|
|
task.SchedResId = wlsrv.WorklineId
|
|
wlsrv.SchedTaskArray = append(wlsrv.SchedTaskArray, task)
|
|
// 更新产线的时间线
|
|
wlsrv.SchedTimeCurve.SubTimeLine(tlinesrv)
|
|
wlsrv.SchedTimeCurve.GetLineArray()
|
|
|
|
return
|
|
}
|
|
// 修复产线的调度计划,生产中的结束时间、未开始的任务的计划开始和结束时间的重新计算
|
|
func(schedsrv *SchedulerSrv)RepairSchedule(worklineid string)(err error){
|
|
var (
|
|
wlsrv *WorklineSrv
|
|
setupsrv SetupSrv
|
|
tlinesrv TimeLineSrv
|
|
i int
|
|
startTime time.Time
|
|
ok, needSetup bool
|
|
)
|
|
|
|
// 获取产线对象
|
|
if wlsrv, ok = schedsrv.WorklineDict[worklineid]; !ok{
|
|
err = errors.New(fmt.Sprintf("产线%s不存在!",worklineid))
|
|
return
|
|
}
|
|
|
|
// 初始化产线的调度时间线
|
|
wlsrv.SchedTimeCurve = wlsrv.TimeCurve
|
|
// 遍历工单逐个依次调度
|
|
for i = 0; i < len(wlsrv.SchedTaskArray); i++ {
|
|
// 获取任务的计划起始时间点
|
|
needSetup = false
|
|
if i == 0 {
|
|
startTime = MaxTime(time.Now(), wlsrv.TimeCurve.StartTime)
|
|
}else{
|
|
startTime = wlsrv.SchedTaskArray[i-1].SchedEndTime
|
|
// 获取换型时间
|
|
setupsrv = SetupSrv{}
|
|
setupsrv.WorklineId = wlsrv.WorklineId
|
|
setupsrv.FromArtId = wlsrv.SchedTaskArray[i-1].ArtId
|
|
setupsrv.ToArtId = wlsrv.SchedTaskArray[i].ArtId
|
|
setupsrv.SetupDura = schedsrv.GetSetupDuration(setupsrv)
|
|
needSetup = true
|
|
}
|
|
startTime = MaxTime(startTime, wlsrv.SchedTaskArray[i].EarliestStartTime)
|
|
|
|
// 如果需要换型,计算任务的换型开始和结束时间
|
|
// 对于已经开始的工单不再计算换型时间
|
|
if wlsrv.SchedTaskArray[i].Status < 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 WO_STATUS_UNPLANNED:
|
|
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 WO_STATUS_PLANNED:
|
|
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 WO_STATUS_RELEASED:
|
|
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 WO_STATUS_RUNNING:
|
|
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
|
|
case 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
|
|
)
|
|
|
|
// 已锁定、已下达、已开始的订单
|
|
for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
|
|
// 获取工单上次的状态
|
|
lastidx = len(schedsrv.LockedTaskArray[i].WoStatusTab.Itemlst) - 1
|
|
if lastidx < 0 {
|
|
prevstatus = 0
|
|
}else{
|
|
prevstatus = schedsrv.LockedTaskArray[i].WoStatusTab.Itemlst[lastidx].Status
|
|
}
|
|
|
|
switch schedsrv.LockedTaskArray[i].Status {
|
|
case WO_STATUS_LOCKED: // 已锁定
|
|
schedsrv.LockedTaskArray[i].WoStatusTab.Status = WO_STATUS_RELEASED
|
|
if schedsrv.LockedTaskArray[i].FixStartTime{
|
|
schedsrv.LockedTaskArray[i].Wotab.Fixstarttime = 1
|
|
}else{
|
|
schedsrv.LockedTaskArray[i].Wotab.Fixstarttime = 0
|
|
}
|
|
schedsrv.LockedTaskArray[i].Wotab.Planresourceid = schedsrv.LockedTaskArray[i].SchedResId
|
|
schedsrv.LockedTaskArray[i].Wotab.Planstarttime = schedsrv.LockedTaskArray[i].SchedStartTime
|
|
|
|
case WO_STATUS_RELEASED: // 已下达
|
|
schedsrv.LockedTaskArray[i].WoStatusTab.Status = WO_STATUS_RELEASED
|
|
if schedsrv.LockedTaskArray[i].FixStartTime{
|
|
schedsrv.LockedTaskArray[i].Wotab.Fixstarttime = 1
|
|
}else{
|
|
schedsrv.LockedTaskArray[i].Wotab.Fixstarttime = 0
|
|
}
|
|
schedsrv.LockedTaskArray[i].Wotab.Planresourceid = schedsrv.LockedTaskArray[i].SchedResId
|
|
schedsrv.LockedTaskArray[i].Wotab.Planstarttime = schedsrv.LockedTaskArray[i].SchedStartTime
|
|
case WO_STATUS_INTERRUPT: // 已中断
|
|
case WO_STATUS_RUNNING: // 生产中
|
|
default:
|
|
}
|
|
schedsrv.LockedTaskArray[i].Wotab.Planendtime = schedsrv.LockedTaskArray[i].SchedEndTime
|
|
// 设置更新时间和人员,更新订单计划时间
|
|
SetTableLastModify(&schedsrv.LockedTaskArray[i].Wotab, schedsrv.LockedTaskArray[i].Wotab, MODIFY_MODE_UPDATE, "sched_service")
|
|
schedsrv.LockedTaskArray[i].Wotab.Update()
|
|
// 如果与变更前的状态不一致,则插入状态变更记录
|
|
if schedsrv.LockedTaskArray[i].Status != prevstatus {
|
|
// 更新工单状态
|
|
SetTableLastModify(&schedsrv.LockedTaskArray[i].WoStatusTab, schedsrv.LockedTaskArray[i].WoStatusTab, MODIFY_MODE_UPDATE, "sched_service")
|
|
schedsrv.LockedTaskArray[i].WoStatusTab.Update()
|
|
// 创建工单状态变更记录
|
|
eventsrv = EventSrv{}
|
|
eventsrv.TriggerService = "sched_service"
|
|
eventsrv.TriggerEvent = OUTPUT_EVENT_WO_LOCK
|
|
eventsrv.TriggerObjectId = schedsrv.LockedTaskArray[i].TaskId
|
|
eventsrv.OutputEvent = 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].WoStatusTab.Itemlst) - 1
|
|
if lastidx < 0 {
|
|
prevstatus = 0
|
|
}else{
|
|
prevstatus = schedsrv.PlannedTaskArray[i].WoStatusTab.Itemlst[lastidx].Status
|
|
}
|
|
|
|
schedsrv.PlannedTaskArray[i].WoStatusTab.Status = WO_STATUS_PLANNED
|
|
if schedsrv.PlannedTaskArray[i].FixStartTime{
|
|
schedsrv.PlannedTaskArray[i].Wotab.Fixstarttime = 1
|
|
}else{
|
|
schedsrv.PlannedTaskArray[i].Wotab.Fixstarttime = 0
|
|
}
|
|
schedsrv.PlannedTaskArray[i].Wotab.Planresourceid = schedsrv.LockedTaskArray[i].SchedResId
|
|
schedsrv.PlannedTaskArray[i].Wotab.Planstarttime = schedsrv.LockedTaskArray[i].SchedStartTime
|
|
schedsrv.PlannedTaskArray[i].Wotab.Planendtime = schedsrv.LockedTaskArray[i].SchedEndTime
|
|
// 设置更新时间和人员
|
|
SetTableLastModify(&schedsrv.PlannedTaskArray[i].Wotab, schedsrv.PlannedTaskArray[i].Wotab, MODIFY_MODE_UPDATE, "sched_service")
|
|
// 更新订单和状态
|
|
schedsrv.PlannedTaskArray[i].Wotab.Update()
|
|
// 如果与变更前的状态不一致,则插入状态变更记录
|
|
if schedsrv.PlannedTaskArray[i].Status != prevstatus {
|
|
SetTableLastModify(&schedsrv.PlannedTaskArray[i].WoStatusTab, schedsrv.PlannedTaskArray[i].WoStatusTab, MODIFY_MODE_UPDATE, "sched_service")
|
|
schedsrv.PlannedTaskArray[i].WoStatusTab.Update()
|
|
// 创建工单状态变更记录
|
|
eventsrv = EventSrv{}
|
|
eventsrv.TriggerService = "sched_service"
|
|
eventsrv.TriggerEvent = OUTPUT_EVENT_WO_PLAN
|
|
eventsrv.TriggerObjectId = schedsrv.LockedTaskArray[i].TaskId
|
|
eventsrv.OutputEvent = 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].WoStatusTab.Itemlst) - 1
|
|
if lastidx < 0 {
|
|
prevstatus = 0
|
|
}else{
|
|
prevstatus = schedsrv.UnPlannedTaskArray[i].WoStatusTab.Itemlst[lastidx].Status
|
|
}
|
|
|
|
// 如果与变更前的状态不一致,则插入状态变更记录
|
|
if schedsrv.UnPlannedTaskArray[i].Status != prevstatus {
|
|
SetTableLastModify(&schedsrv.UnPlannedTaskArray[i].WoStatusTab, schedsrv.UnPlannedTaskArray[i].WoStatusTab, MODIFY_MODE_UPDATE, "sched_service")
|
|
schedsrv.UnPlannedTaskArray[i].WoStatusTab.Update()
|
|
// 创建工单状态变更记录
|
|
eventsrv = EventSrv{}
|
|
eventsrv.TriggerService = "scheduler"
|
|
eventsrv.TriggerEvent = OUTPUT_EVENT_WO_NEW
|
|
eventsrv.TriggerObjectId = schedsrv.LockedTaskArray[i].TaskId
|
|
eventsrv.OutputEvent = 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) {
|
|
var (
|
|
wlsrv *WorklineSrv
|
|
ok bool
|
|
dura time.Duration
|
|
i,j,wipqty,totalrelqty,relqty int
|
|
)
|
|
|
|
// 获取产线对象
|
|
if wlsrv, ok = schedsrv.WorklineDict[worklineid]; !ok {
|
|
err = errors.New(fmt.Sprintf("产线%s不存在!", worklineid))
|
|
return
|
|
}
|
|
|
|
// 获取调度到产线上状态为>=24已锁定计划的批次订单并依次下达
|
|
// 对于状态为24的生产工单置位26,状态默认为20已计划
|
|
|
|
|
|
// 基于产线不同的派工方式依次对序列订单派工
|
|
switch wlsrv.RelCtrlWay {
|
|
case LINE_REL_BY_QTY:
|
|
// 获取在制数量 >=26 && <80
|
|
if wipqty, err = schedsrv.GetWipQty(worklineid); err != nil{
|
|
return
|
|
}
|
|
// 比较确认需要下达的序列订单总计数量
|
|
totalrelqty = wlsrv.RelCtrlQty - wipqty
|
|
// 按照已下达的批次订单的排序号,依次获取未下达的序列订单,并按照指定数量依次下达
|
|
for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
|
|
if schedsrv.LockedTaskArray[i].WoQtyTab.Planqty - schedsrv.LockedTaskArray[i].WoQtyTab.Releasedqty <= 0 {
|
|
continue
|
|
}
|
|
if int(schedsrv.LockedTaskArray[i].WoQtyTab.Planqty - schedsrv.LockedTaskArray[i].WoQtyTab.Releasedqty) >= totalrelqty {
|
|
relqty = totalrelqty
|
|
if err = schedsrv.LockedTaskArray[i].ReleaseSerialOrder(relqty); err != nil {
|
|
err = errors.New(fmt.Sprintf("下达工单%s出错%v!",schedsrv.LockedTaskArray[i].TaskId, err))
|
|
return
|
|
}
|
|
break
|
|
}else{
|
|
relqty = int(schedsrv.LockedTaskArray[i].WoQtyTab.Planqty - schedsrv.LockedTaskArray[i].WoQtyTab.Releasedqty)
|
|
if err = schedsrv.LockedTaskArray[i].ReleaseSerialOrder(relqty); err != nil {
|
|
err = errors.New(fmt.Sprintf("下达工单%s出错%v!",schedsrv.LockedTaskArray[i].TaskId, err))
|
|
return
|
|
}
|
|
totalrelqty = totalrelqty - relqty
|
|
continue
|
|
}
|
|
}
|
|
|
|
case LINE_REL_BY_DURATION:
|
|
// 按照已下达的批次订单的排序号,依次获取未下达的序列订单
|
|
// 比较序列订单计划结束时间和要求交期的差值,满足则下达
|
|
// 为每一个下达的序列订单生成订单消息,用于接口服务处理
|
|
for i = 0; i < len(schedsrv.LockedTaskArray); i++ {
|
|
dura = schedsrv.LockedTaskArray[i].Wotab.Planenddate.Sub(schedsrv.LockedTaskArray[i].Wotab.Planendtime)
|
|
if dura > wlsrv.RelCtrlDura {
|
|
continue
|
|
}
|
|
// 比较每一个序列订单的计划结束事件
|
|
for j = 0; j < len(schedsrv.LockedTaskArray[i].SerialTaskArray); j++ {
|
|
dura = schedsrv.LockedTaskArray[i].SerialTaskArray[j].SerialOrderTab.Planendtime.Sub(schedsrv.LockedTaskArray[i].Wotab.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) {
|
|
var (
|
|
serordtab models.OmSerialorder
|
|
)
|
|
|
|
serordtab = models.OmSerialorder{Plantnr: schedsrv.PlantNr}
|
|
if wipqty, err = serordtab.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 models.MeProject
|
|
snrtab models.Snrtab
|
|
stasksrv SerialTaskSrv
|
|
serordid string
|
|
ok bool
|
|
)
|
|
|
|
// 判断工单状态是否满足
|
|
if schedtask.WoStatusTab.Status != WO_STATUS_LOCKED {
|
|
err = errors.New(fmt.Sprintf("调度工单%s的状态%d不满足生成序列订单的条件!",schedtask.TaskId, schedtask.WoStatusTab.Status))
|
|
return
|
|
}
|
|
|
|
// 获取工单任务对应的项目号
|
|
if projtab, ok = schedsrv.ProjDict[schedtask.ProjectId]; !ok {
|
|
err = errors.New(fmt.Sprintf("调度工单%s的项目%s不存在!",schedtask.TaskId, schedtask.ProjectId))
|
|
return
|
|
}
|
|
|
|
// 从项目号获取序列订单号对应的流水号并依次创建生产订单的序列订单
|
|
snrtab = models.Snrtab{Plantnr: schedsrv.PlantNr, Snrid: projtab.Serialordersnr}
|
|
createqty = int(schedtask.WoQtyTab.Planqty - schedtask.WoQtyTab.Releasedqty)
|
|
for i = 0; i < createqty; i++ {
|
|
if serordid, err = snrtab.GetNextSnr(); 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 = TASK_TYPE_SERIALORDER
|
|
stasksrv.Status = 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
|
|
wostatusrectab models.OMWorkorderstatusrecLst
|
|
wostatusrectablst []models.OMWorkorderstatusrecLst
|
|
)
|
|
|
|
// 加载指定事件的开口消息
|
|
wostatusrectab = models.OMWorkorderstatusrecLst{Plantnr: schedsrv.PlantNr}
|
|
if wostatusrectablst, err = wostatusrectab.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 OUTPUT_EVENT_WO_PLAN:
|
|
case OUTPUT_EVENT_WO_LOCK:
|
|
// 生成批次工单的序列订单
|
|
if err = schedsrv.CreateTaskSerialOrder(&tasksrv); err != nil {
|
|
err = errors.New(fmt.Sprintf("为工单%s生成序列订单出错%v!",tasksrv.TaskId, err))
|
|
return
|
|
}
|
|
|
|
case OUTPUT_EVENT_WO_REL:
|
|
case OUTPUT_EVENT_WO_RUN:
|
|
case OUTPUT_EVENT_WO_CLOSE:
|
|
case OUTPUT_EVENT_WO_CANCEL:
|
|
default:
|
|
}
|
|
|
|
// 关闭事件消息
|
|
wostatusrectablst[i].Outputstatus = OUTPUT_TRIGGER_HANDLED
|
|
SetTableLastModify(&wostatusrectablst[i], wostatusrectablst[i], MODIFY_MODE_UPDATE, "msg_parser")
|
|
if err = wostatusrectablst[i].Update(); 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
|
|
serostatusrectab models.OmSerialorderstatusrecLst
|
|
serostatusrectablst []models.OmSerialorderstatusrecLst
|
|
)
|
|
|
|
// 加载指定事件的开口消息
|
|
serostatusrectab = models.OmSerialorderstatusrecLst{Plantnr: schedsrv.PlantNr}
|
|
if serostatusrectablst, err = serostatusrectab.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 OUTPUT_EVENT_SER_NEW:
|
|
case OUTPUT_EVENT_SER_REL:
|
|
case OUTPUT_EVENT_SER_RUN:
|
|
case OUTPUT_EVENT_SER_CLOSE:
|
|
default:
|
|
}
|
|
|
|
// 关闭事件消息
|
|
serostatusrectablst[i].Outputstatus = OUTPUT_TRIGGER_HANDLED
|
|
SetTableLastModify(&serostatusrectablst[i], serostatusrectablst[i], MODIFY_MODE_UPDATE, "msg_parser")
|
|
if err = serostatusrectablst[i].Update(); 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)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
|
|
}
|