You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

840 lines
30 KiB

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
}