1427 lines
79 KiB
Plaintext
1427 lines
79 KiB
Plaintext
Function Scheduling_Services(@Service, @Params)
|
|
/***********************************************************************************************************************
|
|
|
|
This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
|
|
permission from SRP Computer Solutions, Inc.
|
|
|
|
Name : Scheduling_Services
|
|
|
|
Description : Handler program for all module related services.
|
|
|
|
Notes : The generic parameters should contain all the necessary information to process the services. Often
|
|
this will be information like the data Record and Key ID.
|
|
|
|
Parameters :
|
|
Service [in] -- Name of the service being requested
|
|
Param1-10 [in/out] -- Additional request parameter holders
|
|
Response [out] -- Response to be sent back to the Controller (MCP) or requesting procedure
|
|
|
|
Metadata :
|
|
|
|
History : (Date, Initials, Notes)
|
|
06/01/17 dmb Original programmer. - [EPIOI-8]
|
|
|
|
***********************************************************************************************************************/
|
|
|
|
#pragma precomp SRP_PreCompiler
|
|
|
|
$insert APP_INSERTS
|
|
$insert SERVICE_SETUP
|
|
$insert REACTOR_EQUATES
|
|
$insert SCHED_DET_EQUATES
|
|
$insert WO_LOG_EQUATES
|
|
$insert COMPANY_EQUATES
|
|
$insert PROD_VER_EQUATES
|
|
$insert PRS_LAYER_EQU
|
|
$insert SCHEDULE_EVENT_SUMMMARY_EQUATES
|
|
$insert WO_SCHEDULE_EQUATES
|
|
$insert SCHED_DET_KEY_IDS_EQUATES
|
|
$insert WORK_ORDER_CHILD_KEY_IDS_EQUATES
|
|
$insert SCHEDULER_EQUATES
|
|
|
|
Equ NOTIFICATION_PERIOD$ to 12
|
|
Equ SECONDS_PER_DAY$ to 86400
|
|
Equ MIDNIGHT_AM$ to 0 ; // Midnight represented as the beginning of the day.
|
|
Equ MIDNIGHT_PM$ to 86400 ; // Midnight represented as the end of the day.
|
|
Equ SIX_AM$ to 21600
|
|
Equ NOON$ to 43200
|
|
Equ SIX_PM$ to 64800
|
|
|
|
Declare subroutine Error_Services, Scheduling_Services, Memory_Services, RList, Database_Services, SRP_JSON
|
|
Declare function SRP_Array, Scheduling_Services, Memory_Services, Database_Services, SRP_Sort_Array, Work_Order_Services
|
|
Declare function Epi_Part_Services, SRP_Math, SRP_Hash, obj_Prod_Spec, Reactor_Services, SRP_JSON
|
|
|
|
GoToService else
|
|
Error_Services('Set', Service : ' is not a valid service request within the ' : ServiceModule : ' services module.')
|
|
end
|
|
|
|
Return Response else ''
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Services
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetScheduleEvents
|
|
//
|
|
// Returns a JSON formatted array of schedule event objects. Schedule events will be filtered according to the search
|
|
// arguments passed in.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetScheduleEvents(StartDate, EndDate, ReactorNo, WorkOrderNo, BlockOut)
|
|
Debug
|
|
If Not(Num(StartDate)) then StartDate = Iconv(StartDate, 'D')
|
|
If Not(Num(EndDate)) then EndDate = Iconv(EndDate, 'D')
|
|
If BlockOut NE True$ then BlockOut = False$
|
|
|
|
ScheduleEvents = ''
|
|
|
|
ServiceKeyID := '*' : StartDate : '*' : EndDate : '*' : ReactorNo : '*' : WorkOrderNo : '*' : BlockOut
|
|
ServiceKeyID = SRP_Hash(ServiceKeyID, 'SHA-1')
|
|
|
|
If (StartDate NE '') then
|
|
If Memory_Services('IsValueCurrent', ServiceKeyID, 15, True$) then
|
|
ScheduleEvents = Memory_Services('GetValue', ServiceKeyID)
|
|
end else
|
|
If SRP_JSON(objScheduleEvents, 'NEW', 'OBJECT') then
|
|
If SRP_JSON(objScheduleEventArray, 'NEW', 'ARRAY') then
|
|
SchedulerKeyIDs = Scheduling_Services('SearchScheduler', StartDate, EndDate, ReactorNo, WorkOrderNo, BlockOut)
|
|
If Error_Services('NoError') then
|
|
If SchedulerKeyIDs NE '' then
|
|
SchedulerKeyIDs = SRP_Array('SortRows', SchedulerKeyIDs, 'AR1' : @FM : 'AR2' : @FM : 'AR3', 'LIST', @FM, '*')
|
|
For Each SchedulerKeyID in SchedulerKeyIDs using @FM setting fPos
|
|
ReactorNo = SchedulerKeyID[1, '*']
|
|
StartDate = SchedulerKeyID[Col2() + 1, '*']
|
|
EventID = SchedulerKeyID[Col2() + 1, '*']
|
|
SRP_Stopwatch('Start', 'GetScheduleEvent')
|
|
ScheduleEvent = Scheduling_Services('GetScheduleEvent', ReactorNo, StartDate, EventID, False$)
|
|
SRP_Stopwatch('Stop', 'GetScheduleEvent')
|
|
If SRP_JSON(objScheduleEvent, 'PARSE', ScheduleEvent) EQ '' then
|
|
SRP_JSON(objScheduleEventArray, 'ADD', objScheduleEvent)
|
|
SRP_JSON(objScheduleEvent, 'RELEASE')
|
|
end
|
|
Next SchedDetKeyID
|
|
SRP_JSON(objScheduleEvents, 'SET', 'ScheduleEvents', objScheduleEventArray)
|
|
ScheduleEvents = SRP_JSON(objScheduleEvents, 'STRINGIFY', 'FAST')
|
|
Memory_Services('SetValue', ServiceKeyID, ScheduleEvents)
|
|
end
|
|
end
|
|
SRP_JSON(objScheduleEventArray, 'RELEASE')
|
|
end else
|
|
Error_Services('Add', 'Error creating objScheduleEventArray in the ' : Service : ' service.')
|
|
end
|
|
SRP_JSON(objScheduleEvents, 'RELEASE')
|
|
end else
|
|
Error_Services('Add', 'Error creating objScheduleEvents in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'StartDate argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ScheduleEvents
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SearchScheduler
|
|
//
|
|
// Returns an array of SCHEDULER Key IDs based on the search parameters provided.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SearchScheduler(StartDate, EndDate, ReactorNo, WorkOrderNo, BlockOut)
|
|
|
|
SchedulerKeyIDs = ''
|
|
|
|
If Not(Num(StartDate)) then StartDate = Iconv(StartDate, 'D')
|
|
If Not(Num(EndDate)) then EndDate = Iconv(EndDate, 'D')
|
|
If BlockOut NE True$ then BlockOut = False$
|
|
|
|
ServiceKeyID := '*' : StartDate : '*' : EndDate : '*' : ReactorNo : '*' : WorkOrderNo : '*' : BlockOut
|
|
ServiceKeyID = SRP_Hash(ServiceKeyID, 'SHA-1')
|
|
|
|
If (StartDate NE '') then
|
|
If EndDate NE '' then
|
|
DateRange = Oconv(StartDate - 1, 'D4/') : '~' : Oconv(EndDate + 1, 'D4/')
|
|
DateRangeMatch = Database_Services('SearchIndex', 'SCHEDULER', 'START_DATE', DateRange, True$)
|
|
end else
|
|
DateRangeMatch = Database_Services('SearchIndex', 'SCHEDULER', 'START_DATE', '>=' : StartDate, True$)
|
|
end
|
|
Transfer DateRangeMatch to SchedulerKeyIDs
|
|
If ReactorNo NE '' then
|
|
ReactorMatch = Database_Services('SearchIndex', 'SCHEDULER', 'REACTOR_NO', ReactorNo, True$)
|
|
SchedulerKeyIDs = SRP_Array('Join', SchedulerKeyIDs, ReactorMatch, 'AND', @FM)
|
|
end
|
|
If WorkOrderNo NE '' then
|
|
WOChildKeyIDs = Database_Services('ReadDataRow', 'WORK_ORDER_CHILD_KEY_IDS', WorkOrderNo, True$, 5)
|
|
WorkOrderMatch = WOChildKeyIDs<WORK_ORDER_CHILD_KEY_IDS.SCHEDULER_KEY_IDS$>
|
|
Convert @VM to @FM in WorkOrderMatch
|
|
SchedulerKeyIDs = SRP_Array('Join', SchedulerKeyIDs, WorkOrderMatch, 'AND', @FM)
|
|
end
|
|
// Only filter out or return Block Out events if the Reactor No is specified. Otherwise, all Block Out
|
|
// will be included.
|
|
If ReactorNo NE '' then
|
|
BlockOutMatch = Database_Services('SearchIndex', 'SCHEDULER', 'BLOCK_OUT', 'Yes', True$)
|
|
If BlockOut then
|
|
SchedulerKeyIDs = SRP_Array('Join', SchedulerKeyIDs, BlockOutMatch, 'AND', @FM)
|
|
end else
|
|
SchedulerKeyIDs = SRP_Array('Join', SchedulerKeyIDs, BlockOutMatch, 'NOT', @FM)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'StartDate argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = SchedulerKeyIDs
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AdjustScheduleEvents
|
|
//
|
|
// Adjusts the schedule detail rows for the indicated reactor. This service will add or remove events for the indicated
|
|
// work order based on the number of events specified. This means other events for other work orders will be shifted
|
|
// as needed to allow the specified work order to have continuous events as well as eliminating gaps in the scheduler
|
|
// calendar. Note: blocked dates will need to be respected.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AdjustScheduleEvents(ReactorNo, WorkOrderNo, NumberOfDays, Date, Description)
|
|
Debug
|
|
NumberOfEvents = SRP_Math('CEILING', NumberOfDays, '', 0)
|
|
|
|
If Date EQ '' then Date = Date()
|
|
If Not(Num(Date)) then Date = Iconv(Date, 'D')
|
|
AllWorkOrderEvents = ''
|
|
PreviousEvents = '' ; // Used to track events that already exist on the schedule for this date that
|
|
; // should be left alone.
|
|
RemoveEventKeyIDs = '' ; // List of Scheduler Key IDs that will be removed when the service is finished.
|
|
AddEvents = '' ; // List of Scheduler Events (Appointments) that will be added when the service is finished.
|
|
|
|
If (ReactorNo NE '') AND (WorkOrderNo NE '') AND (NumberOfEvents NE '') AND (NumberOfEvents NE 0) then
|
|
// Get the block out events for this reactor.
|
|
BlockOutEvents = Scheduling_Services('GetScheduleEvents', Date - 1, '', ReactorNo, '', True$)
|
|
BlockOutKeys = SRP_Array('Rotate', BlockOutEvents, @FM, @VM)
|
|
BlockOutKeys = BlockOutKeys<2>
|
|
BlockOutDates = BlockOutKeys
|
|
Convert @VM to @FM in BlockOutDates
|
|
Convert '*' to @VM in BlockOutDates
|
|
BlockOutDates = SRP_Array('Rotate', BlockOutDates, @FM, @VM)
|
|
BlockOutDates = BlockOutDates<4>
|
|
|
|
// Get the indicated work order events for this reactor.
|
|
WorkOrderEvents = Scheduling_Services('GetScheduleEvents', Date - 1, '', ReactorNo, WorkOrderNo, False$)
|
|
WorkOrderKeys = SRP_Array('Rotate', WorkOrderEvents, @FM, @VM)
|
|
WorkOrderKeys = WorkOrderKeys<2>
|
|
Convert @VM to @FM in WorkOrderKeys
|
|
Convert '*' to @VM in WorkOrderKeys
|
|
WorkOrderKeys = SRP_Array('Rotate', WorkOrderKeys, @FM, @VM)
|
|
WorkOrderKeys = Delete(WorkOrderKeys, 1, 0, 0) ; // Remove the work orders
|
|
WorkOrderKeys = Delete(WorkOrderKeys, 1, 0, 0) ; // Remove the EPI Part
|
|
WorkOrderKeys = SRP_Array('Rotate', WorkOrderKeys, @FM, @VM)
|
|
Convert @VM to '*' in WorkOrderKeys
|
|
|
|
// Get the other work order events for this reactor.
|
|
OtherEvents = Scheduling_Services('GetScheduleEvents', Date - 1, '', ReactorNo, '', False$)
|
|
OtherEvents = SRP_Array('Join', OtherEvents, WorkOrderEvents, 'NOT', @FM)
|
|
OtherKeys = SRP_Array('Rotate', OtherEvents, @FM, @VM)
|
|
OtherKeys = OtherKeys<2>
|
|
Convert @VM to @FM in OtherKeys
|
|
Convert '*' to @VM in OtherKeys
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the work orders
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the EPI Part
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
Convert @VM to '*' in OtherKeys
|
|
|
|
// Initialize variables.
|
|
EventCount = 0
|
|
|
|
If OtherKeys NE '' then
|
|
// There are other schedule events for this reactor. Check to see if any of them fall on the same day the date assigned to the
|
|
// service (either the passed in date or the default date). If any exist, a determination needs to be made to see which one
|
|
// (if any) is the tail end of an existing work order series.
|
|
|
|
// Get all existing events for the previous and current dates.
|
|
CompareEvents = Scheduling_Services('GetScheduleEvents', Date - 1, Date, ReactorNo, '', False$)
|
|
|
|
If CompareEvents NE '' then
|
|
// There are other events already scheduled for the start date. Loop through each event to see if it is the tail end of an
|
|
// existing work order series.
|
|
For Each CompareEvent in CompareEvents using @FM
|
|
CompareSchedulerKeyID = CompareEvent<0, 2>
|
|
CompareWorkOrder = CompareSchedulerKeyID[1, '*']
|
|
CompareScheduleKeyID = Field(CompareSchedulerKeyID, '*', 3, 3)
|
|
// Check to see if this work order has an event in the past.
|
|
Events = Scheduling_Services('GetScheduleEvents', Date - 1, Date - 1, ReactorNo, CompareWorkOrder, False$)
|
|
If Events NE '' then PreviousEvents := Events : @FM
|
|
Next ScheduleEvent
|
|
PreviousEvents[-1, 1] = ''
|
|
If PreviousEvents NE '' then
|
|
// Since there is one or more work orders already on the schedule that should not be adjusted, find the one with the date furthest
|
|
// out into the future. If its date is after the proposed start date, adjust the start date to occur on the same date.
|
|
LatestDate = ''
|
|
For Each PreviousEvent in PreviousEvents using @FM
|
|
PreviousSchedulerKeyID = PreviousEvent<0, 2>
|
|
PreviousWorkOrder = PreviousSchedulerKeyID[1, '*']
|
|
Events = Scheduling_Services('GetScheduleEvents', Date, '', ReactorNo, PreviousWorkOrder, False$)
|
|
If Events NE '' then
|
|
LastEvent = Events[-1, 'B' : @FM]
|
|
LastSchedulerKeyID = LastEvent<0, 2>
|
|
LastDate = Field(LastSchedulerKeyID, '*', 4, 1)
|
|
If LastDate GT LatestDate then Transfer LastDate to LatestDate
|
|
end
|
|
Next PreviousEvent
|
|
If LatestDate GT Date then Transfer LatestDate to Date
|
|
end
|
|
end
|
|
end
|
|
|
|
If NumberOfEvents LT 0 then
|
|
// The number of events is being reduced. Remove these from the end for this work order. Then find all other
|
|
// work orders in the sfame reactor and shift them back by the number of events indicated.
|
|
|
|
NumberOfEvents = Neg(NumberOfEvents)
|
|
// Reverse sort the keys and events. Always keep the keys and events in sync.
|
|
WorkOrderKeys = SRP_Array('SortRows', WorkOrderKeys, 'DR2', 'LIST', @FM, '*')
|
|
WorkOrderEvents = SRP_Array('SortRows', WorkOrderEvents, 'DR3', 'LIST', @FM, @VM)
|
|
|
|
// Remove the extra work order events.
|
|
Loop
|
|
WorkOrderKey = WorkOrderKeys<1>
|
|
WorkOrderEvent = WorkOrderEvents<1>
|
|
WorkOrderKeys = Delete(WorkOrderKeys, 1, 0, 0)
|
|
WorkOrderEvents = Delete(WorkOrderEvents, 1, 0, 0)
|
|
EventCount += 1
|
|
// Delete Schedule Detail record.
|
|
RemoveEventKeyIDs := WorkOrderEvent<0, 2> : @FM
|
|
Scheduling_Services('DeleteScheduleDetail', WorkOrderKey)
|
|
Until (EventCount GE NumberOfEvents)
|
|
Repeat
|
|
|
|
// Now that the extra work order events have been removed, check to see if there are any more
|
|
// work order keys. If so, then use the last schedule date for the work order to be the
|
|
// starting schedule date. Otherwise, go back to the previous schedule date.
|
|
If WorkOrderKeys NE '' then
|
|
ScheduleDate = Field(WorkOrderKeys[1, @FM], '*', 2, 1)
|
|
end else
|
|
// All of the work order events were removed so use the date of the first event and then
|
|
// subtract 1 to be safe in case the tip was not stacked with the tail of a previous
|
|
// list of work orders.
|
|
ScheduleDate = Field(WorkOrderKey, '*', 2, 1) - 1
|
|
// However, if the previous list of work orders is extended into the future already, then
|
|
// use the latest date from that list instead since this is definitely the correct starting date.
|
|
If ScheduleDate LT Date then ScheduleDate = Date
|
|
end
|
|
|
|
end else
|
|
|
|
// The number of events is being increased (or added if this is a new work order). Increased work order events will be added to
|
|
// the end of the existing eents. New work order events will be added to the indicated date unless a series of work orders
|
|
// was already on the schedule. In this case the end of this work order series will be used as the start date. All other work order
|
|
// events in the same reactor will be shifted forward.
|
|
If WorkOrderKeys NE '' then
|
|
// An already scheduled work order is being adjusted. Get the last scheduled event and read the schedule detail record to use
|
|
// as a template for new events that will be added.
|
|
ScheduleKeyID = WorkOrderKeys[-1, 'B' : @FM]
|
|
ScheduleEvent = WorkOrderEvents[-1, 'B' : @FM]
|
|
ScheduleDate = Field(ScheduleKeyID, '*', 2, 1)
|
|
* ScheduleDetRow = Scheduling_Services('GetScheduleDetail', ScheduleKeyID)
|
|
ScheduleDetRow = Database_Services('ReadDataRow', 'SCHED_DET', ScheduleKeyID)
|
|
ScheduleDetRow<SCHED_DET_DESC$> = '' ; // New schedule detail rows will not have any description.
|
|
end else
|
|
// There are no events for the indicated work order. This implies that a new work order is being added to the scheduler.
|
|
|
|
// In order to work with the adjust logic, a pseudo-event needs to be created. This will also provide a template for the
|
|
// new work order events being added to the schedule. Note, a sequence of 99 for the ScheduleKeyID is used to avoid any
|
|
// possible conflicts with real schedule Key IDs.
|
|
ScheduleKeyID = ReactorNo : '*' : Date : '*' : 99
|
|
WOLogRow = Database_Services('ReadDataRow', 'WO_LOG', WorkOrderNo)
|
|
NoteFlag = Description NE ''
|
|
CustNo = WOLogRow<WO_LOG_CUST_NO$>
|
|
EpiPartNo = WOLogRow<WO_LOG_EPI_PART_NO$>
|
|
WOClosedFlag = False$
|
|
HotLotFlag = WOLogRow<WO_LOG_WO_MAT_KEY_HOTLOT$> NE ''
|
|
|
|
If CustNo NE '' then
|
|
CompanyRow = Database_Services('ReadDataRow', 'COMPANY', CustNo)
|
|
CustName = CompanyRow<COMPANY_ABBREV$>
|
|
end else
|
|
CustName = ''
|
|
end
|
|
Begin Case
|
|
Case CustName _EQC 'International Rectifier' ; CustName = 'IR'
|
|
Case CustName _EQC 'IRNewport' ; CustName = 'Newport'
|
|
Case CustName _EQC 'IFX (Kulim)' ; CustName = 'Kulim'
|
|
Case CustName _EQC 'IFX Austria AG' ; CustName = 'Austria'
|
|
Case CustName _EQC 'Tower Semiconductor' ; CustName = 'Tower'
|
|
End Case
|
|
|
|
StartDTM = (Date - 1) : '.0'
|
|
EndDTM = Date : '.0'
|
|
CurrentFlag = True$
|
|
|
|
Begin Case
|
|
Case HotLotFlag
|
|
BackColor = 'LightCoral'
|
|
Case Otherwise$
|
|
BackColor = 'LightSteelBlue'
|
|
End Case
|
|
ForeColor = 'Black'
|
|
Title = WorkOrderNo : ' (' : CustName : ' ' : EpiPartNo : ')'
|
|
ScheduleEvent = ReactorNo : @VM : WorkOrderNo : '*' : EpiPartNo : '*' : ScheduleKeyID : @VM : StartDTM : @VM : EndDTM : @VM : BackColor : @VM : ForeColor : @VM : Title
|
|
ScheduleEvent := @VM : Description : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : HotLotFlag : @SVM : NoteFlag : @SVM : WOClosedFlag : @SVM : CurrentFlag
|
|
|
|
// Set the schedule date to be one day before the intended start date. This will allow the adjust logic to use the actual schedule start date.
|
|
ScheduleDate = Date - 1
|
|
|
|
// Create the schedule detail database row.
|
|
ScheduleDetRow = ''
|
|
ScheduleDetRow<SCHED_DET_WO_NO$> = WorkOrderNo
|
|
ScheduleDetRow<SCHED_DET_DESC$> = Description
|
|
end
|
|
|
|
// Create the new work order events.
|
|
ScheduleDate += 1
|
|
DayRemain = NumberOfDays
|
|
Loop
|
|
Locate ScheduleDate in BlockOutDates using @VM setting vPos then
|
|
// Do not schedule on the same date as a block out event.
|
|
end else
|
|
Sequence = 1
|
|
KeyFound = False$ ; // Assume key not found for now.
|
|
Loop
|
|
NewScheduleKeyID = ReactorNo : '*' : ScheduleDate : '*' : Sequence
|
|
Locate NewScheduleKeyID in OtherKeys using @FM setting fPos then
|
|
Sequence += 1
|
|
end else
|
|
// Increase the event count to determine if enough events have been created.
|
|
EventCount += 1
|
|
KeyFound = True$
|
|
If WorkOrderKeys EQ '' then
|
|
WorkOrderKeys = NewScheduleKeyID
|
|
end else
|
|
WorkOrderKeys := @FM : NewScheduleKeyID
|
|
end
|
|
SchedulerKey = ScheduleEvent<0, 2>
|
|
SchedulerKey = Field(SchedulerKey, '*', 1, 2) : '*' : NewScheduleKeyID
|
|
ScheduleEvent<0, 2> = SchedulerKey
|
|
If @UserName EQ 'DANIEL_ST' then
|
|
If EventCount LT NumberOfEvents then
|
|
// Assume this schedule detail will be a full day.
|
|
DayLengthCode = 4
|
|
end else
|
|
// Calculate the portion of the day using the remaining time available.
|
|
DayLengthCode = DayRemain / .25
|
|
end
|
|
ScheduleDetRow<SCHED_DET_DAY_LENGTH_CODE$> = DayLengthCode
|
|
DayLength = DayLengthCode * .25
|
|
Swap '0.' with '.' in DayLength
|
|
Begin Case
|
|
Case (EventCount EQ 1) OR (EventCount EQ NumberOfEvents)
|
|
// This event must be the Head or Tail of the event series. Assume it will start
|
|
// at the beginning of the day.
|
|
StartDTM = ScheduleDate : '.0'
|
|
If DayLength EQ 1 then
|
|
// This event is a full day.
|
|
EndDTM = ScheduleDate + 1 : '.0'
|
|
end else
|
|
EndDTM = ScheduleDate : DayLength
|
|
end
|
|
Case Otherwise$
|
|
// This event must be a standalone event or the middle of the event series.
|
|
StartDTM = ScheduleDate : '.0'
|
|
EndDTM = ScheduleDate + 1 : '.0'
|
|
End Case
|
|
end else
|
|
StartDTM = ScheduleDate : '.0'
|
|
EndDTM = ScheduleDate + 1 : '.0'
|
|
end
|
|
ScheduleEvent<0, 3> = StartDTM
|
|
ScheduleEvent<0, 4> = EndDTM
|
|
If WorkOrderEvents EQ '' then
|
|
WorkOrderEvents = ScheduleEvent
|
|
end else
|
|
WorkOrderEvents := @FM : ScheduleEvent
|
|
end
|
|
// Deduct from the number of days needed to create events for.
|
|
DayRemain -= 1
|
|
// Create a new Schedule Event
|
|
AddEvents := ScheduleEvent : @FM
|
|
Scheduling_Services('SetScheduleDetail', NewScheduleKeyID, ScheduleDetRow)
|
|
end
|
|
Until KeyFound
|
|
Repeat
|
|
end
|
|
ScheduleDate += 1
|
|
Until (EventCount GE NumberOfEvents)
|
|
Repeat
|
|
|
|
// This starts the other schedule events on the same date as the last new work order event.
|
|
ScheduleDate = Field(NewScheduleKeyID, '*', 2, 1)
|
|
|
|
end
|
|
|
|
// Now that the current work order list has been adjusted whether it is being reduced or increased, call the logic
|
|
// to adjust other work order events.
|
|
GoSub AdjustOtherWorkOrderEvents
|
|
|
|
end else
|
|
Error_Services('Add', 'ReactorNo, WorkOrderNo, or NumberOfEvents argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
// This service does not return a response to complete it's purpose, but for analysis and reporting purposes the adjusted
|
|
// events will be returned to the caller if requested.
|
|
RemoveEventKeyIDs[-1, 1] = ''
|
|
AddEvents[-1, 1] = ''
|
|
Response = RemoveEventKeyIDs : @RM : AddEvents
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddBlockOutEvents
|
|
//
|
|
// Adds one or more block out events to the schedule.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddBlockOutEvents(ReactorNo, StartDate, EndDate, Description)
|
|
|
|
// Always have an end date. No end date means this block out event will only be for one day.
|
|
If EndDate EQ '' then EndDate = StartDate
|
|
AllWorkOrderEvents = ''
|
|
PreviousEvents = '' ; // Used to track events that already exist on the schedule for this date that
|
|
; // should be left alone.
|
|
WorkOrderKeys = ''
|
|
WorkOrderEvents = ''
|
|
RemoveEventKeyIDs = ''
|
|
AddEvents = ''
|
|
|
|
If (ReactorNo NE '') AND (StartDate NE '') then
|
|
// Get the block out events for this reactor.
|
|
BlockOutEvents = Scheduling_Services('GetScheduleEvents', StartDate - 1, '', ReactorNo, '', True$)
|
|
|
|
// Get the other work order events for this reactor.
|
|
OtherEvents = Scheduling_Services('GetScheduleEvents', StartDate - 1, '', ReactorNo, '', False$)
|
|
OtherEvents = SRP_Array('Join', OtherEvents, BlockOutEvents, 'NOT', @FM)
|
|
OtherKeys = SRP_Array('Rotate', OtherEvents, @FM, @VM)
|
|
OtherKeys = OtherKeys<2>
|
|
Convert @VM to @FM in OtherKeys
|
|
Convert '*' to @VM in OtherKeys
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the work orders
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the EPI Part
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
Convert @VM to '*' in OtherKeys
|
|
|
|
// In order to work with the adjust logic, a pseudo-event needs to be created. This will also provide a template for the
|
|
// new work order events being added to the schedule. Note, a sequence of 99 for the ScheduleKeyID is used to avoid any
|
|
// possible conflicts with real schedule Key IDs.
|
|
ScheduleKeyID = ReactorNo : '*' : StartDate : '*' : 99
|
|
NoteFlag = Description NE ''
|
|
WorkOrderNo = ''
|
|
CustNo = ''
|
|
EpiPartNo = ''
|
|
WOClosedFlag = False$
|
|
HotLotFlag = False$
|
|
CustName = ''
|
|
StartDTM = StartDate : '.0'
|
|
EndDTM = (StartDate + 1) : '.0'
|
|
CurrentFlag = False$
|
|
BackColor = 'Plum'
|
|
ForeColor = 'Black'
|
|
BlockOutType = '' ; // Future enhancement.
|
|
Title = 'Block Out' : ' (' : BlockOutType : ')'
|
|
ScheduleEvent = ReactorNo : @VM : WorkOrderNo : '*' : EpiPartNo : '*' : ScheduleKeyID : @VM : StartDTM : @VM : EndDTM : @VM : BackColor : @VM : ForeColor : @VM : Title
|
|
ScheduleEvent := @VM : Description : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : HotLotFlag : @SVM : NoteFlag : @SVM : WOClosedFlag : @SVM : CurrentFlag
|
|
|
|
// Set the schedule date to the intended start date.
|
|
ScheduleDate = StartDate
|
|
|
|
// Create the schedule detail database row.
|
|
ScheduleDetRow = ''
|
|
ScheduleDetRow<SCHED_DET_DESC$> = Description
|
|
ScheduleDetRow<SCHED_DET_BLOCK_OUT$> = True$
|
|
ScheduleDetRow<SCHED_DET_BLOCK_OUT_TYPE$> = '' ; // Future enhancement.
|
|
|
|
// Create the new block out events.
|
|
For ScheduleDate = StartDate to EndDate
|
|
Sequence = 1
|
|
KeyFound = False$ ; // Assume key not found for now.
|
|
Loop
|
|
NewScheduleKeyID = ReactorNo : '*' : ScheduleDate : '*' : Sequence
|
|
Locate NewScheduleKeyID in OtherKeys using @FM setting fPos then
|
|
Sequence += 1
|
|
end else
|
|
KeyFound = True$
|
|
SchedulerKey = ScheduleEvent<0, 2>
|
|
SchedulerKey = Field(SchedulerKey, '*', 1, 2) : '*' : NewScheduleKeyID
|
|
ScheduleEvent<0, 2> = SchedulerKey
|
|
ScheduleEvent<0, 3> = ScheduleDate : '.0'
|
|
ScheduleEvent<0, 4> = ScheduleDate + 1 : '.0'
|
|
// Create a new Schedule Event
|
|
AddEvents := ScheduleEvent : @FM
|
|
Scheduling_Services('SetScheduleDetail', NewScheduleKeyID, ScheduleDetRow)
|
|
end
|
|
Until KeyFound
|
|
Repeat
|
|
Next ScheduleDate
|
|
|
|
// Get an updated list of the block out events for this reactor. This will include the block outs
|
|
// we just added.
|
|
BlockOutEvents = Scheduling_Services('GetScheduleEvents', StartDate, '', ReactorNo, '', True$)
|
|
BlockOutKeys = SRP_Array('Rotate', BlockOutEvents, @FM, @VM)
|
|
BlockOutKeys = BlockOutKeys<2>
|
|
BlockOutDates = BlockOutKeys
|
|
Convert @VM to @FM in BlockOutDates
|
|
Convert '*' to @VM in BlockOutDates
|
|
BlockOutDates = SRP_Array('Rotate', BlockOutDates, @FM, @VM)
|
|
BlockOutDates = BlockOutDates<4>
|
|
|
|
// Get the other work order events for this reactor.
|
|
OtherEvents = Scheduling_Services('GetScheduleEvents', StartDate, '', ReactorNo, '', False$)
|
|
OtherKeys = SRP_Array('Rotate', OtherEvents, @FM, @VM)
|
|
OtherKeys = OtherKeys<2>
|
|
Convert @VM to @FM in OtherKeys
|
|
Convert '*' to @VM in OtherKeys
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the work orders
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the EPI Part
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
Convert @VM to '*' in OtherKeys
|
|
|
|
// Now that the block outs have been added, see if any work order events need to be adjusted.
|
|
GoSub AdjustOtherWorkOrderEvents
|
|
|
|
end else
|
|
Error_Services('Add', 'ReactorNo or StartDate argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
// Return to the caller any events that were removed and added so the visual display can be updated if needed.
|
|
RemoveEventKeyIDs[-1, 1] = ''
|
|
AddEvents[-1, 1] = ''
|
|
Response = RemoveEventKeyIDs : @RM : AddEvents
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// CancelBlockOutEvent
|
|
//
|
|
// Removes a block out event from the schedule.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service CancelBlockOutEvent(BlockOutEventKeyID)
|
|
|
|
AllWorkOrderEvents = ''
|
|
PreviousEvents = '' ; // Used to track events that already exist on the schedule for this date that
|
|
; // should be left alone.
|
|
WorkOrderKeys = ''
|
|
WorkOrderEvents = ''
|
|
RemoveEventKeyIDs = ''
|
|
AddEvents = ''
|
|
|
|
If BlockOutEventKeyID NE '' then
|
|
|
|
ScheduleKeyID = Field(BlockOutEventKeyID, '*', 3, 3)
|
|
ReactorNo = ScheduleKeyID[1, '*']
|
|
Date = ScheduleKeyID[Col2() + 1, '*']
|
|
Sequence = ScheduleKeyID[Col2() + 1, '*']
|
|
RemoveEventKeyIDs := BlockOutEventKeyID : @FM
|
|
Scheduling_Services('DeleteScheduleDetail', ScheduleKeyID)
|
|
|
|
// Set the schedule date to the block out date.
|
|
ScheduleDate = Date
|
|
|
|
// Get an updated list of the block out events for this reactor. This will include the block outs
|
|
// we just added.
|
|
BlockOutEvents = Scheduling_Services('GetScheduleEvents', Date, '', ReactorNo, '', True$)
|
|
BlockOutKeys = SRP_Array('Rotate', BlockOutEvents, @FM, @VM)
|
|
BlockOutKeys = BlockOutKeys<2>
|
|
BlockOutDates = BlockOutKeys
|
|
Convert @VM to @FM in BlockOutDates
|
|
Convert '*' to @VM in BlockOutDates
|
|
BlockOutDates = SRP_Array('Rotate', BlockOutDates, @FM, @VM)
|
|
BlockOutDates = BlockOutDates<4>
|
|
|
|
// Get the other work order events for this reactor.
|
|
OtherEvents = Scheduling_Services('GetScheduleEvents', Date, '', ReactorNo, '', False$)
|
|
OtherKeys = SRP_Array('Rotate', OtherEvents, @FM, @VM)
|
|
OtherKeys = OtherKeys<2>
|
|
Convert @VM to @FM in OtherKeys
|
|
Convert '*' to @VM in OtherKeys
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the work orders
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the EPI Part
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
Convert @VM to '*' in OtherKeys
|
|
|
|
// Now that the block outs have been added, see if any work order events need to be adjusted.
|
|
GoSub AdjustOtherWorkOrderEvents
|
|
|
|
end else
|
|
Error_Services('Add', 'BlockOutEventKeyID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
// Return to the caller any events that were removed and added so the visual display can be updated if needed.
|
|
RemoveEventKeyIDs[-1, 1] = ''
|
|
AddEvents[-1, 1] = ''
|
|
Response = RemoveEventKeyIDs : @RM : AddEvents
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetScheduleEvent
|
|
//
|
|
// Returns a JSON formatted object of information for a specific schedule event.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetScheduleEvent(ReactorNo, StartDate, EventID)
|
|
SRP_Stopwatch('Start', Service)
|
|
If Not(Num(StartDate)) then StartDate = Iconv(StartDate, 'D')
|
|
|
|
ServiceKeyID := '*' : ReactorNo : '*' : StartDate : '*' : EventID
|
|
ScheduleEvent = ''
|
|
If (ReactorNo NE '') AND (StartDate NE '') AND (EventID NE '') then
|
|
If SRP_JSON(objScheduleEvent, 'NEW', 'OBJECT') then
|
|
SchedulerKeyID = ReactorNo : '*' : StartDate : '*' : EventID
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'EventID', EventID, 'STRING')
|
|
SchedulerRow = Database_Services('ReadDataRow', 'SCHEDULER', SchedulerKeyID, True$, 15)
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'StartDate', Oconv(StartDate, 'D4/'), 'STRING')
|
|
EndDate = SchedulerRow<SCHEDULER.END_DATE$>
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'EndDate', Oconv(EndDate, 'D4/'), 'STRING')
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'ModifiedDTM', Oconv(SchedulerRow<SCHEDULER.MODIFIED$>, 'DT/4^HS'), 'STRING')
|
|
Note = SchedulerRow<SCHEDULER.DESCRIPTION$>
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'Note', Note, 'STRING')
|
|
BlockOut = SchedulerRow<SCHEDULER.BLOCK_OUT$>
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'BlockOut', Oconv(BlockOut, 'BYes,No'), 'STRING')
|
|
BlockOutType = SchedulerRow<SCHEDULER.BLOCK_OUT_TYPE$>
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'BlockOutType', BlockOutType, 'STRING')
|
|
BackColor = SchedulerRow<SCHEDULER.BACKCOLOR$>
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'BackColor', BackColor, 'STRING')
|
|
ForeColor = SchedulerRow<SCHEDULER.FORECOLOR$>
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'ForeColor', ForeColor, 'STRING')
|
|
StartTime = SchedulerRow<SCHEDULER.START_TIME$>
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'StartTime', Oconv(StartTime, 'MTH'), 'STRING')
|
|
EndTime = SchedulerRow<SCHEDULER.END_TIME$>
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'EndTime', Oconv(EndTime, 'MTH'), 'STRING')
|
|
Reactor = Reactor_Services('GetReactor', ReactorNo)
|
|
If SRP_JSON(objReactor, 'PARSE', Reactor) EQ '' then
|
|
SRP_JSON(objScheduleEvent, 'SET', 'Reactor', objReactor)
|
|
SRP_JSON(objReactor, 'RELEASE')
|
|
end
|
|
WorkOrderNo = EventID
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'WorkOrderStep', SchedulerRow<SCHEDULER.WO_STEP$>, 'STRING')
|
|
WorkOrder = Work_Order_Services('GetWorkOrder', WorkOrderNo, False$)
|
|
If SRP_JSON(objWorkOrder, 'PARSE', WorkOrder) EQ '' then
|
|
SRP_JSON(objScheduleEvent, 'SET', 'WorkOrder', objWorkOrder)
|
|
WorkOrderNo = SRP_JSON(objWorkOrder, 'GETVALUE', 'WorkOrderNumber')
|
|
EpiPartNo = SRP_JSON(objWorkOrder, 'GETVALUE', 'EpiPartNumber')
|
|
CustNameShort = SRP_JSON(objWorkOrder, 'GETVALUE', 'Company.NameShort')
|
|
SRP_JSON(objWorkOrder, 'RELEASE')
|
|
end else
|
|
WorkOrderNo = ''
|
|
EpiPartNo = ''
|
|
CustNameShort = ''
|
|
end
|
|
StartDateTime = StartDate + (StartTime / SECONDS_PER_DAY$)
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'StartDateTime', Oconv(StartDateTime, 'DT2/^H'), 'STRING')
|
|
EndDateTime = EndDate + (EndTime / SECONDS_PER_DAY$)
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'EndDateTime', Oconv(EndDateTime, 'DT2/^H'), 'STRING')
|
|
TotalScheduledDays = EndDateTime - StartDateTime
|
|
TotalScheduledDays = SRP_Math('CEILING', TotalScheduledDays, '', 2)
|
|
SRP_JSON(objScheduleEvent, 'SETVALUE', 'TotalScheduledDays', TotalScheduledDays, 'STRING')
|
|
ScheduleEvent = SRP_JSON(objScheduleEvent, 'STRINGIFY', 'FAST')
|
|
SRP_JSON(objScheduleEvent, 'RELEASE')
|
|
end else
|
|
Error_Services('Add', 'Error creating objScheduleEvent in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ReactorNo, StartDate, or EventID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ScheduleEvent
|
|
SRP_Stopwatch('Start', Service)
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetScheduleDetail
|
|
//
|
|
// Creates or updates the SCHED_DET row for the indicated Key ID.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetScheduleDetail(ScheduleKeyID, ScheduleDetail)
|
|
|
|
If (ScheduleKeyID NE '') AND (ScheduleDetail NE '') then
|
|
TableName = 'SCHED_DET'
|
|
Database_Services('WriteDataRow', Tablename, ScheduleKeyID, ScheduleDetail, True$)
|
|
end else
|
|
Error_Services('Add', 'ScheduleKeyID or ScheduleDetail argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddScheduleEvents
|
|
//
|
|
// Adds schedule events for the indicated work order on the indicated reactor starting on the indicated start date for
|
|
// the indicated number of days. All future schedule events will automatically be adjusted future into the future
|
|
// accordingly. The exception to this are schedule events which begin on or before the indicated start date. In this
|
|
// case, the new schedule events will be placed after the current schedule event.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddScheduleEvents(WorkOrderNo, ReactorNo, RequestedStartDate, NumberOfDays, Description)
|
|
|
|
If Not(Num(RequestedStartDate)) then RequestedStartDate = Iconv(RequestedStartDate, 'D')
|
|
If NumberOfDays EQ '' then
|
|
NumberOfDays = Scheduling_Services('GetAdjustedDays', WorkOrderNo)
|
|
end
|
|
|
|
CancelledSchedDetKeyIDs = ''
|
|
AddedSchedDetKeyIDs = ''
|
|
Debug
|
|
If (WorkOrderNo NE '') AND (ReactorNo NE '') AND (RequestedStartDate NE '') AND (NumberOfDays GT 0) then
|
|
ActualStartDate = '' ; // This will be used to store the actual start date the system is able to find.
|
|
RemainingSeconds = NumberOfDays * SECONDS_PER_DAY$ ; // Convert the total days of the new schedule events into seconds.
|
|
Loop
|
|
// Get the SCHED_DET Key IDs already scheduled for this reactor and schedule date. The basic rule is that no
|
|
// schedule event already engaged (i.e., started on or before the requested start date) will be moved. The new
|
|
// schedule event must begin in the first available time slot on or after the requested start date. All
|
|
// schedule events that start after the requested start date will be moved into the future to make room for
|
|
// new schedule events.
|
|
* SchedDetKeyIDsRow = Database_Services('ReadDataRow', 'SCHED_DET_KEY_IDS', ReactorNo : '*' : RequestedStartDate)
|
|
WOSchedDetKeyIDs = SchedDetKeyIDsRow<SCHED_DET_KEY_IDS_WORK_ORDER_SCHED_DET_KEY_IDS$>
|
|
BOSchedDetKeyIDs = SchedDetKeyIDsRow<SCHED_DET_KEY_IDS_BLOCK_OUT_SCHED_DET_KEY_IDS$>
|
|
SchedDetKeyIDs = SRP_Array('Clean', WOSchedDetKeyIDs : @VM : BOSchedDetKeyIDs, 'TrimAndMakeUnique', @VM)
|
|
If SchedDetKeyIDs EQ '' then
|
|
// Nothing is already scheduled. Use this date and set the start time (midnight). Calculate the end time
|
|
// based on whether there are more seconds in the schedule than are in a full day.
|
|
ActualStartDate = RequestedStartDate
|
|
ActualStartTime = MIDNIGHT_AM$
|
|
AvailableSeconds = SECONDS_PER_DAY$
|
|
If RemainingSeconds GE AvailableSeconds then
|
|
ActualStopTime = MIDNIGHT_PM$
|
|
end else
|
|
ActualStopTime = RemainingSeconds
|
|
end
|
|
end else
|
|
// There are existing schedule events. Check to see if there is any remaining portion of the day that
|
|
// can be used to start the new schedule event.
|
|
GoSub GetLastEndTime ; // Assumes SchedDetKeyIDs is populated. Returns LastEndTime variable.
|
|
If LastEndTime LT MIDNIGHT_PM$ then
|
|
// The last schedule event on this date ends before midnight. Use this date and set the
|
|
// start time to be the end time of the existing schedule event.
|
|
ActualStartDate = RequestedStartDate
|
|
ActualStartTime = LastEndTime
|
|
AvailableSeconds = SECONDS_PER_DAY$ - ActualStartTime
|
|
If RemainingSeconds GE AvailableSeconds then
|
|
ActualStopTime = MIDNIGHT_PM$
|
|
end else
|
|
ActualStopTime = RemainingSeconds
|
|
end
|
|
end
|
|
end
|
|
Until ActualStartDate NE ''
|
|
RequestedStartDate += 1
|
|
Repeat
|
|
|
|
// To simplify the process of adding schedule events, remove all future schedule events related to work orders.
|
|
// Save all future SCHED_DET rows into cache so they can be added back later.
|
|
If ActualStopTime EQ MIDNIGHT_PM$ then
|
|
// Start the search on the next date since the new schedule event will end at midnight.
|
|
SearchDate = ActualStartDate + 1
|
|
end else
|
|
// Start the search on the actual start date since the new schedule event will end before
|
|
// midnight.
|
|
SearchDate = ActualStartDate
|
|
end
|
|
SchedDetKeyIDs = Scheduling_Services('SearchSchedDet', SearchDate, '', ReactorNo, '', False$)
|
|
If SchedDetKeyIDs NE '' then
|
|
For Each SchedDetKeyID in SchedDetKeyIDs using @FM
|
|
MoveScheduleEvent = False$ ; // Assume false for now.
|
|
FutureSchedDetRow = Database_Services('ReadDataRow', 'SCHED_DET', SchedDetKeyID)
|
|
FutureReactorNo = SchedDetKeyID[1, '*']
|
|
FutureScheduleDate = SchedDetKeyID[Col2() + 1, '*']
|
|
If ActualStartDate EQ FutureScheduleDate then
|
|
// Make sure this schedule event occurs after the new schedule event before nominating it to be
|
|
// moved.
|
|
FutureStartTime = FutureSchedDetRow<SCHED_DET_START_TIME$>
|
|
If FutureStartTime GT ActualStartTime then MoveScheduleEvent = True$
|
|
end else
|
|
MoveScheduleEvent = True$
|
|
end
|
|
If MoveScheduleEvent EQ True$ then
|
|
Memory_Services('SetValue', Service : '*' : SchedDetKeyID, FutureSchedDetRow)
|
|
* Database_Services('DeleteDataRow', 'SCHED_DET', SchedDetKeyID, True$)
|
|
end
|
|
Next SchedDetKeyID
|
|
end
|
|
|
|
// Create the SCHED_DET row base.
|
|
SchedDetRow = ''
|
|
SchedDetRow<SCHED_DET_WO_NO$> = WorkOrderNo
|
|
SchedDetRow<SCHED_DET_DESC$> = Description
|
|
SchedDetRow<SCHED_DET_MODIFIED$> = Iconv(Oconv(Date(), 'D4/') : ' ' : Oconv(Time(), 'MTH'), 'DTM')
|
|
|
|
// Now add the new schedule events. Stop when the number of seconds remaining to be scheduled reaches 0.
|
|
// Schedule around block out events if they exist.
|
|
Transfer ActualStartDate to ScheduleDate ; // Begin with the actual start date.
|
|
Transfer ActualStartTime to StartTime ; // Begin with the actual start time.
|
|
Loop
|
|
Until RemainingSeconds LE 0
|
|
SchedDetKeyIDsRow = Database_Services('ReadDataRow', 'SCHED_DET_KEY_IDS', ReactorNo : '*' : ScheduleDate)
|
|
SchedDetKeyIDs = SchedDetKeyIDsRow<SCHED_DET_KEY_IDS_BLOCK_OUT_SCHED_DET_KEY_IDS$>
|
|
If SchedDetKeyIDs NE '' then
|
|
// There are existing block out schedule events. Check to see if there is any remaining portion of the
|
|
// day that can be used to start the new schedule event.
|
|
GoSub GetLastEndTime ; // Assumes SchedDetKeyIDs is populated. Returns LastEndTime variable.
|
|
If LastEndTime LT MIDNIGHT_PM$ then
|
|
// The last schedule event on this date ends before midnight. Use this date and set the
|
|
// start time to be the end time of the existing schedule event.
|
|
StartTime = LastEndTime
|
|
end
|
|
end
|
|
|
|
SchedDetRow<SCHED_DET_START_TIME$> = StartTime
|
|
// Calculate the portion of the day remaining in the current day. Most of the time this will be the whole
|
|
// day (i.e., DayPortion = 1), but if this is the first day of the schedule event, it might be starting
|
|
// late in the day.
|
|
AvailableSeconds = SECONDS_PER_DAY$ - StartTime
|
|
If RemainingSeconds GE AvailableSeconds then
|
|
StopTime = MIDNIGHT_PM$
|
|
end else
|
|
StopTime = RemainingSeconds
|
|
end
|
|
RemainingSeconds -= AvailableSeconds
|
|
SchedDetRow<SCHED_DET_END_TIME$> = StopTime
|
|
// Find the next available SchedDetKeyID for the curent reactor and schedule date and create the
|
|
// SCHED_DET row.
|
|
SchedDetKeyIDsRow = Database_Services('ReadDataRow', 'SCHED_DET_KEY_IDS', ReactorNo : '*' : ScheduleDate)
|
|
SchedDetKeyIDs = SchedDetKeyIDsRow<SCHED_DET_KEY_IDS_WORK_ORDER_SCHED_DET_KEY_IDS$>
|
|
SchedDetKeyIDs = SRP_Array('SortRows', SchedDetKeyIDs, 'DR3', 'LIST', @VM, '*')
|
|
// Get the last sequence number used and increase by one to create a unique SchedDetKeyID.
|
|
Sequence = Field(SchedDetKeyIDs<0, 1>, '*', 3, 1) + 1
|
|
SchedDetKeyID = ReactorNo : '*' : ScheduleDate : '*' : Sequence
|
|
Database_Services('WriteDataRow', 'SCHED_DET', SchedDetKeyID, SchedDetRow, True$)
|
|
If Error_Services('NoError') then
|
|
// SCHED_DET row was successfully created. Add the Key ID to the list.
|
|
AddedSchedDetKeyIDs := SchedDetKeyID : @VM
|
|
end
|
|
|
|
// Increment the schedule date and reset the start time.
|
|
ScheduleDate += 1
|
|
StartTime = MIDNIGHT_AM$
|
|
Repeat
|
|
AddedSchedDetKeyIDs[-1, 1] = ''
|
|
end else
|
|
Error_Services('Add', 'WorkOrderNo, ReactorNo, RequestedStartDate, or NumberOfDays argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = CancelledSchedDetKeyIDs
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// CancelScheduleEvents
|
|
//
|
|
// Cancels all schedule events for the indicated work order. If a reactor number is specified, then only events on that
|
|
// reactor are cancelled. The adjust future events flag indicates whether events in the future should be automatically
|
|
// adjusted, meaning that an attempt will be made to relocate these events to fill in the gap left behind by the
|
|
// cancelled events.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service CancelScheduleEvents(WorkOrderNo, ReactorNo, AdjustFutureEvents)
|
|
|
|
CancelledSchedDetKeyIDs = ''
|
|
|
|
If WorkOrderNo NE '' then
|
|
WOScheduleRow = Database_Services('ReadDataRow', 'WO_SCHEDULE', WorkOrderNo)
|
|
SchedDetKeyIDs = WOScheduleRow<WO_SCHEDULE_SCHED_DET_KEY_IDS$>
|
|
If SchedDetKeyIDs NE '' then
|
|
For Each SchedDetKeyID in SchedDetKeyIDs using @VM
|
|
ScheduleReactorNo = SchedDetKeyID[1, '*']
|
|
If (ReactorNo EQ '') OR (ReactorNo EQ ScheduleReactorNo) then
|
|
Database_Services('DeleteDataRow', 'SCHED_DET', SchedDetKeyID, True$)
|
|
If Error_Services('NoError') then
|
|
CancelledSchedDetKeyIDs := SchedDetKeyID : @FM
|
|
end
|
|
end
|
|
Next SchedDetKeyID
|
|
end
|
|
CancelledSchedDetKeyIDs[-1, 1] = ''
|
|
end else
|
|
Error_Services('Add', 'WorkOrderNo argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = CancelledSchedDetKeyIDs
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// DeleteScheduleDetail
|
|
//
|
|
// Deletes the SCHED_DET row for the indicated Key IDs.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service DeleteScheduleDetail(ScheduleKeyIDs)
|
|
|
|
If ScheduleKeyIDs NE '' then
|
|
TableName = 'SCHED_DET'
|
|
For Each ScheduleKeyID in ScheduleKeyIDs using @FM
|
|
Database_Services('DeleteDataRow', Tablename, ScheduleKeyID, True$)
|
|
Next ScheduleKeyID
|
|
end else
|
|
Error_Services('Add', 'ScheduleKeyID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetAdjustedDays
|
|
//
|
|
// Returns the calculated adjusted number of days needed to process the remaining wafers for the indicated work order.
|
|
// Note: this can return a negative value if there are more scheduled events needed to complete the remaining wafers.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetAdjustedDays(WorkOrderNo)
|
|
|
|
ServiceKeyID := '*' : WorkOrderNo
|
|
AdjustedDays = ''
|
|
|
|
If Memory_Services('IsValueCurrent', ServiceKeyID, 5, True$) then
|
|
AdjustedDays = Memory_Services('GetValue', ServiceKeyID)
|
|
end else
|
|
If WorkOrderNo NE '' then
|
|
// Get the list of existing SCHED_DET Key IDs sorted by Schedule Date.
|
|
WOScheduleRow = Database_Services('ReadDataRow', 'WO_SCHEDULE', WorkOrderNo, True$, 15)
|
|
SchedDetKeyIDs = WOScheduleRow<WO_SCHEDULE_SCHED_DET_KEY_IDS$>
|
|
SchedDetKeyIDs = SRP_Array('SortRows', SchedDetKeyIDs, 'AR2', 'LIST', @VM, '*')
|
|
// Get the first SCHED_DET Key ID. Use this to get a schedule object, which will also
|
|
// contain the total scheduled days.
|
|
TotalScheduledDays = 0
|
|
Date = Date()
|
|
If SchedDetKeyIDs NE '' then
|
|
FirstSchedDetKeyID = SchedDetKeyIDs<0, 1>
|
|
For Each SchedDetKeyID in SchedDetKeyIDs using @VM
|
|
ReactorNo = SchedDetKeyID[1, '*']
|
|
ScheduleDate = SchedDetKeyID[Col2() + 1, '*']
|
|
Sequence = SchedDetKeyID[Col2() + 1, '*']
|
|
// If the schedule date for the event is on the same date being referenced or later,
|
|
// consider this to be a future event and add its scheduled time to the total scheduled
|
|
// days. This will be used to determine if any adjustment is necessary.
|
|
If ScheduleDate GE Date then
|
|
ScheduleEvent = Scheduling_Services('GetScheduleEvent', ReactorNo, ScheduleDate, Sequence, True$)
|
|
GoSub ParseScheduleEvent
|
|
If DayLengthCode EQ '' then DayLengthCode = 4
|
|
TotalScheduledDays += DayLengthCode * .25
|
|
end
|
|
Next SchedDetKeyID
|
|
end else
|
|
// This work order has not yet ben scheduled. The adjusted days should indicate the actual number
|
|
// of days needed to process this order from beginning to end.
|
|
WorkOrder = Work_Order_Services('GetWorkOrder', WorkOrderNo, True$)
|
|
GoSub ParseWorkOrder
|
|
FirstScheduleDate = ''
|
|
end
|
|
// Make sure the Reactor Type assigned to this work order is formatted to the newer type.
|
|
Begin Case
|
|
Case WOReactorType _EQC 'ASM+' ; WOReactorType = 'ASM+'
|
|
Case WOReactorType _EQC 'EPP' ; WOReactorType = 'EpiPro'
|
|
Case WOReactorType _EQC 'EPS' ; WOReactorType = 'ASM'
|
|
Case WOReactorType _EQC 'HTR' ; WOReactorType = 'HTR'
|
|
Case WOReactorType _EQC 'GAN' ; WOReactorType = 'GaN'
|
|
Case WOReactorType _EQC '' ; WOReactorType = '***'
|
|
End Case
|
|
// Get the wafers per day that can be processed. This takes into consideration the current reactor
|
|
// utilization value. Thus, this is a realistic number rather than an ideal number.
|
|
WafersPerDay = Epi_Part_Services('GetAdjustedWafersPerDayScheduler', EpiPartNo, WOReactorType)
|
|
If Error_Services('NoError') then
|
|
DaysNeeded = SRP_Math('ROUND', (WafersRemaining / WafersPerDay) * 4, '', 0) / 4
|
|
AdjustedDays = DaysNeeded - TotalScheduledDays
|
|
If (WafersRemaining EQ 0) OR ((AdjustedDays LT 0) AND (FirstScheduleDate EQ Date)) then
|
|
// This implies that the work order being analyzed is already finished. However, this is
|
|
// due to the current work order event finishing the job so this specific event needs to
|
|
// be kept in the list. The adjusted days counter needs to be increased by 1 to allow
|
|
// this to happen.
|
|
AdjustedDays += 1
|
|
end
|
|
Memory_Services('SetValue', ServiceKeyID, AdjustedDays)
|
|
end
|
|
end else
|
|
Error_Services('Add', 'WorkOrderNo argument was missing from the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
Response = AdjustedDays
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AutoScheduler
|
|
//
|
|
// Analyzes all work orders events for the current date for all reactors. Determines the number of days required for
|
|
// each work order to complete the remaining number of wafers. Each work order is adjusted automatically based on the
|
|
// adjuted number of days calculated.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AutoScheduler()
|
|
SRP_Stopwatch('Reset')
|
|
SRP_Stopwatch('Start', Service)
|
|
Log = ''
|
|
Reactors = Reactor_Services('GetReactors')
|
|
Reactors = Reactors[1, @RM] ; // Remove the meta data from the end.
|
|
Reactors = SRP_Array('Rotate', Reactors, @FM, @VM)
|
|
ReactorNos = Reactors<3>
|
|
|
|
For Each ReactorNo in ReactorNos using @VM
|
|
ScheduleEvents = Scheduling_Services('GetScheduleEvents', Date(), '', ReactorNo, '', False$)
|
|
WorkOrderNos = SRP_Array('Rotate', ScheduleEvents, @FM, @VM)
|
|
WorkOrderNos = WorkOrderNos<2>
|
|
Convert @VM to @FM in WorkOrderNos
|
|
Convert '*' to @VM in WorkOrderNos
|
|
WorkOrderNos = SRP_Array('Rotate', WorkOrderNos, @FM, @VM)
|
|
WorkOrderNos = WorkOrderNos<1>
|
|
WorkOrderNos = SRP_Array('Clean', WorkOrderNos, 'TrimAndMakeUnique', @VM)
|
|
If WorkOrderNos NE '' then
|
|
For Each WorkOrderNo in WorkOrderNos using @VM
|
|
// Determine how many days has this Work order has been scheduled for starting from today
|
|
AdjustedDays = Scheduling_Services('GetAdjustedDays', WorkOrderNo)
|
|
Log := 'Reactor: ' : ReactorNo : ' - WorkOrder: ' : WorkOrderNo : ' - Number of days: ' : AdjustedDays : @FM
|
|
Log := ReactorNo : ',' : WorkOrderNo : ',' : AdjustedDays : @FM
|
|
If Error_Services('NoError') then
|
|
If AdjustedDays NE '' AND AdjustedDays NE 0 then
|
|
AdjustedEvents = Scheduling_Services('AdjustScheduleEvents', ReactorNo, WorkOrderNo, AdjustedDays, Date())
|
|
If Error_Services('NoError') then
|
|
Log := AdjustedEvents : @FM
|
|
end else
|
|
Log := Error_Services('GetMessage') : @FM
|
|
end
|
|
end
|
|
end else
|
|
Message = Error_Services('GetMessage')
|
|
Message := ' - Reactor No: ' : ReactorNo
|
|
Error_Services('Set', Message)
|
|
Log := Error_Services('GetMessage') : @FM
|
|
end
|
|
Next WorkOrderNo
|
|
end
|
|
Next ReactorNo
|
|
|
|
SRP_Stopwatch('Stop', Service)
|
|
If @UserName EQ 'DANIEL_ST' then
|
|
Open 'SYSLISTS' to hsyslists then
|
|
Log = SRP_Array('SortRows', Log, 'AL1', 'LIST', @FM, @VM)
|
|
Write Log to hSysLists, 'SCHEDULERENGINE' else Debug
|
|
end
|
|
SRP_Stopwatch('ShowAll')
|
|
end
|
|
end service
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Internal GoSubs
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AdjustOtherWorkOrderEvents
|
|
//
|
|
// Logic dedicated to adjusting all other work orders for a specific reactor. This is called by the AdjustScheduleEvents
|
|
// service.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
AdjustOtherWorkOrderEvents:
|
|
|
|
PreviousKeys = '' ; // List of previous schedule detail keys that should be referenced later on.
|
|
If PreviousEvents NE '' then
|
|
// This means one or more work orders has been identified as already being scheduled in the past. All events
|
|
// related to them shoud be removed from the OtherKeys and OtherEvents variables. This will prevent them from being
|
|
// adjusted.
|
|
For Each PreviousEvent in PreviousEvents using @FM
|
|
PreviousSchedulerKeyID = PreviousEvent<0, 2>
|
|
PreviousWorkOrder = PreviousSchedulerKeyID[1, '*']
|
|
NumOtherEvents = DCount(OtherEvents, @FM)
|
|
For OtherEventCnt = NumOtherEvents to 1 Step -1
|
|
OtherEvent = OtherEvents<OtherEventCnt>
|
|
OtherSchedulerKeyID = OtherEvent<0, 2>
|
|
OtherWorkOrder = OtherSchedulerKeyID[1, '*']
|
|
If OtherWorkOrder EQ PreviousWorkOrder then
|
|
PreviousKeys := OtherKeys<OtherEventCnt> : @FM
|
|
OtherEvents = Delete(OtherEvents, OtherEventCnt, 0, 0)
|
|
OtherKeys = Delete(OtherKeys, OtherEventCnt, 0, 0)
|
|
end
|
|
Next OtherEventCnt
|
|
Next PreviousEvent
|
|
PreviousKeys[-1, 1] = ''
|
|
end
|
|
|
|
// Move the work order events and keys into the "All" work order event and key lists. This will be the master list
|
|
// that will be adjusted and eventually returned by this service.
|
|
Transfer WorkOrderKeys to AllWorkOrderKeys
|
|
Transfer WorkOrderEvents to AllWorkOrderEvents
|
|
|
|
// If there were previous events that were removed from the Other events and keys, there might not be any
|
|
// other events that need to be adjuted. Check to see if there are other events before proceeding.
|
|
If OtherEvents NE '' then
|
|
If AllWorkOrderKeys NE '' then
|
|
AllWorkOrderKeys := @FM : OtherKeys
|
|
AllWorkOrderEvents := @FM : OtherEvents
|
|
end else
|
|
AllWorkOrderKeys = OtherKeys
|
|
AllWorkOrderEvents = OtherEvents
|
|
end
|
|
AllWorkOrderKeys = SRP_Array('SortRows', AllWorkOrderKeys, 'AR2', 'LIST', @FM, '*')
|
|
AllWorkOrderKeys = SRP_Array('Clean', AllWorkOrderKeys, 'TrimAndMakeUnique', @FM)
|
|
AllWorkOrderEvents = SRP_Array('SortRows', AllWorkOrderEvents, 'AR3', 'LIST', @FM, @VM)
|
|
AllWorkOrderEvents = SRP_Array('Clean', AllWorkOrderEvents, 'TrimAndMakeUnique', @FM)
|
|
|
|
// The original OtherEvents and OtherKeys lists were sorted by reactor number and then by
|
|
// work order in order to keep the like work orders together. However, these now need to be
|
|
// resorted in strict chronological order in order to maintain priority of work orders that
|
|
// are scheduled in the future. Otherwise, work orders intended to be scheduled further
|
|
// out in the calendar might get adjusted to occur prior to a higher priority work
|
|
// order.
|
|
OtherEvents = SRP_Array('SortRows', OtherEvents, 'AR1' : @FM : 'AR3', 'LIST', @FM, @VM)
|
|
MatchKeys = SRP_Array('Rotate', OtherEvents, @FM, @VM)
|
|
MatchKeys = MatchKeys<2>
|
|
Convert @VM to @FM in MatchKeys
|
|
Convert '*' to @VM in MatchKeys
|
|
// These keys are sorted by work order and then by date. This puts the oldest date first
|
|
// so priorty can be established.
|
|
MatchKeys = SRP_Array('SortRows', MatchKeys, 'AR1' : @FM : 'AR4', 'LIST', @FM, @VM)
|
|
MatchKeys = SRP_Array('Rotate', MatchKeys, @FM, @VM)
|
|
NewOtherEvents = ''
|
|
// Create a new list of other events but pre-pend the oldest date related to each work order.
|
|
// This will create a temporary priority sorting column.
|
|
If OtherEvents NE '' then
|
|
For Each OtherEvent in OtherEvents using @FM
|
|
SchedulerKeyID = OtherEvent<0, 2>
|
|
WorkOrderNo = SchedulerKeyID[1, '*']
|
|
Locate WorkOrderNo in MatchKeys<1> using @VM setting vPos then
|
|
FirstDate = MatchKeys<4, vPos>
|
|
NewOtherEvents := FirstDate : @VM : OtherEvent : @FM
|
|
end
|
|
Next OtherEvent
|
|
NewOtherEvents[-1, 1] = ''
|
|
Transfer NewOtherEvents to OtherEvents
|
|
end
|
|
// Now sort the other events by the temporary column.
|
|
OtherEvents = SRP_Array('SortRows', OtherEvents, 'AR1', 'LIST', @FM, @VM)
|
|
OtherEvents = SRP_Array('Rotate', OtherEvents, @FM, @VM)
|
|
// Remove the temporary column and restore other events to its normal format.
|
|
OtherEvents = Delete(OtherEvents, 1, 0, 0)
|
|
OtherEvents = SRP_Array('Rotate', OtherEvents, @FM, @VM)
|
|
// Since other events has been sorted, the other keys needs to be rebuilt.
|
|
OtherKeys = SRP_Array('Rotate', OtherEvents, @FM, @VM)
|
|
OtherKeys = OtherKeys<2>
|
|
Convert @VM to @FM in OtherKeys
|
|
Convert '*' to @VM in OtherKeys
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the work orders
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0) ; // Remove the EPI Part
|
|
OtherKeys = SRP_Array('Rotate', OtherKeys, @FM, @VM)
|
|
Convert @VM to '*' in OtherKeys
|
|
|
|
// Relocate the pre-existing work order events.
|
|
PreviousScheduleDate = ''
|
|
PreviousWorkOrder = ''
|
|
Loop
|
|
Until (OtherKeys EQ '')
|
|
Locate ScheduleDate in BlockOutDates using @VM setting vPos then
|
|
// Do not schedule on the same date as a block out event.
|
|
end else
|
|
// Get the next schedule key that needs to be adjusted. It will always be the first
|
|
// item in the list of keys.
|
|
OtherKey = OtherKeys<1>
|
|
// Look for this schedule in the combined list of all work order keys (which should
|
|
// always be found). Then use the index position to extract the schedule detail information.
|
|
// Remove the schedule key and event from the master lists so they don't get processed again.
|
|
Locate OtherKey in AllWorkOrderKeys using @FM setting fPos then
|
|
OtherEvent = AllWorkOrderEvents<fPos>
|
|
AllWorkOrderKeys = Delete(AllWorkOrderKeys, fPos, 0, 0)
|
|
AllWorkOrderEvents = Delete(AllWorkOrderEvents, fPos, 0, 0)
|
|
end else
|
|
// This condition should never occur.
|
|
OtherEvent = ''
|
|
end
|
|
|
|
// Get the current work order for the event being adjusted. Compare it to the previous
|
|
// work order already adjusted (if applicable). If the work order is different, then use
|
|
// don't advance the schedule date. Just increase the sequence so it can be added to the
|
|
// same date. Otherwise, advance the schedule date and reset the sequence to 1.
|
|
CurrentWorkOrder = OtherEvent<0, 2>[1, '*']
|
|
OtherKeys = Delete(OtherKeys, 1, 0, 0)
|
|
OtherEvents = Delete(OtherEvents, 1, 0, 0)
|
|
Begin Case
|
|
Case PreviousWorkOrder EQ ''
|
|
// Use the current schedule date as already assigned above.
|
|
Sequence = 1
|
|
Case CurrentWorkOrder EQ PreviousWorkOrder
|
|
// Use the newly advanced schedule date. Sequence to 1.
|
|
Sequence = 1
|
|
Case CurrentWorkOrder NE PreviousWorkOrder
|
|
// Current work order is different from previous work order.
|
|
// Use the previous schedule date but increase the sequence.
|
|
ScheduleDate = PreviousScheduleDate
|
|
Sequence += 1
|
|
End Case
|
|
|
|
// Using the starting values from above, look for the next available schedule key that
|
|
// the current event can use.
|
|
KeyFound = False$ ; // Assume key not found for now.
|
|
Loop
|
|
// Get the next "new" schedule detail key that is available.
|
|
NewScheduleKeyID = ReactorNo : '*' : ScheduleDate : '*' : Sequence
|
|
Locate NewScheduleKeyID in PreviousKeys : @FM : AllWorkOrderKeys using @FM setting fPos then
|
|
// The new schedule key already exists. Just increase the sequence counter to create
|
|
// another new schedule key to compare with.
|
|
Sequence += 1
|
|
end else
|
|
// We now have a "new" schedule detail key. Update the database using the new
|
|
// schedule detail key and then delete the original key.
|
|
KeyFound = True$
|
|
|
|
// The event detail contains information from the original schedule date. This needs to be
|
|
// adjusted to reflect the new schedule date.
|
|
OrigOtherEvent = OtherEvent ; // Used to append to the RemoveEventKeyIDs list below.
|
|
SchedulerKey = OtherEvent<0, 2>
|
|
SchedulerKey = Field(SchedulerKey, '*', 1, 2) : '*' : NewScheduleKeyID
|
|
OtherEvent<0, 2> = SchedulerKey
|
|
OtherEvent<0, 3> = ScheduleDate : '.0'
|
|
OtherEvent<0, 4> = ScheduleDate + 1 : '.0'
|
|
|
|
// For reporting purposes, add the updated event to the list of all work order events.
|
|
AllWorkOrderKeys := @FM : NewScheduleKeyID
|
|
AllWorkOrderEvents := @FM : OtherEvent
|
|
|
|
// OtherKey is the original key being adjusted. Delete it first before adding the
|
|
// new scheduler key.
|
|
* ScheduleDetRow = Scheduling_Services('GetScheduleDetail', OtherKey)
|
|
ScheduleDetRow = Database_Services('ReadDataRow', 'SCHED_DET', OtherKey)
|
|
If Error_Services('NoError') then
|
|
RemoveEventKeyIDs := OrigOtherEvent<0, 2> : @FM
|
|
Scheduling_Services('DeleteScheduleDetail', OtherKey)
|
|
If Error_Services('NoError') then
|
|
AddEvents := OtherEvent : @FM
|
|
Scheduling_Services('SetScheduleDetail', NewScheduleKeyID, ScheduleDetRow)
|
|
end
|
|
end
|
|
end
|
|
Until KeyFound
|
|
Repeat
|
|
// Store the current schedule date in the previous schedule date variable so it can be used to compare
|
|
// with in the next loop.
|
|
PreviousScheduleDate = ScheduleDate
|
|
// Store the current work order in the previous work order variable so it can be used to compare with
|
|
// in the next loop.
|
|
PreviousWorkOrder = CurrentWorkOrder
|
|
end
|
|
// Advanced to the next schedule date whether a new schedule event was created or if a block out date was encountered.
|
|
ScheduleDate += 1
|
|
Repeat
|
|
end
|
|
|
|
return
|
|
|
|
|
|
ParseScheduleEvent:
|
|
|
|
If Assigned(objScheduleEvent) else objScheduleEvent = 0
|
|
If objScheduleEvent LE 0 then
|
|
SRP_JSON(objScheduleEvent, 'PARSE', ScheduleEvent)
|
|
end
|
|
If objScheduleEvent GT 0 then
|
|
ReactorNo = SRP_JSON(objScheduleEvent, 'GETVALUE', 'Reactor.ReactorNumber')
|
|
ReactorType = SRP_JSON(objScheduleEvent, 'GETVALUE', 'Reactor.Type')
|
|
SusceptorSize = SRP_JSON(objScheduleEvent, 'GETVALUE', 'Reactor.SusceptorSize')
|
|
ScheduleDate = SRP_JSON(objScheduleEvent, 'GETVALUE', 'ScheduleDate')
|
|
ScheduleDate = Iconv(ScheduleDate, 'D')
|
|
Sequence = SRP_JSON(objScheduleEvent, 'GETVALUE', 'Sequence')
|
|
BackColor = SRP_JSON(objScheduleEvent, 'GETVALUE', 'BackColor')
|
|
ForeColor = SRP_JSON(objScheduleEvent, 'GETVALUE', 'ForeColor')
|
|
ModifiedDTM = Iconv(SRP_JSON(objScheduleEvent, 'GETVALUE', 'ModifiedDTM'), 'DT')
|
|
BlockOut = Iconv(SRP_JSON(objScheduleEvent, 'GETVALUE', 'BlockOut'), 'BYes,No')
|
|
BlockOutType = SRP_JSON(objScheduleEvent, 'GETVALUE', 'BlockOutType')
|
|
Description = SRP_JSON(objScheduleEvent, 'GETVALUE', 'Note')
|
|
DayLengthCode = SRP_JSON(objScheduleEvent, 'GETVALUE', 'DayLengthCode')
|
|
StartTime = Iconv(SRP_JSON(objScheduleEvent, 'GETVALUE', 'StartTime'), 'MT')
|
|
EndTime = Iconv(SRP_JSON(objScheduleEvent, 'GETVALUE', 'EndTime'), 'MT')
|
|
If EndTime EQ MIDNIGHT_AM$ then EndTime = MIDNIGHT_PM$
|
|
objWorkOrder = SRP_JSON(objScheduleEvent, 'GET', 'WorkOrder')
|
|
GoSub ParseWorkOrder
|
|
If BackColor EQ '' then
|
|
Begin Case
|
|
Case BackColor NE ''
|
|
// Set in the schedule detail record. Use current color.
|
|
Case HotLot
|
|
BackColor = 'LightCoral'
|
|
Case Closed
|
|
BackColor = 'LightGray'
|
|
Case BlockOut
|
|
BackColor = 'Plum'
|
|
Case Otherwise$
|
|
BackColor = 'LightSteelBlue'
|
|
End Case
|
|
end
|
|
Current = False$ ; // Assume false for now.
|
|
If (ScheduleDate GE Date()) then
|
|
If (ModifiedDTM NE '') then
|
|
CurrentDTM = Iconv(Oconv(Date(), 'D4/') : ' ' : Oconv(Time(), 'MTH'), 'DTM')
|
|
ElapseTime = (CurrentDTM - ModifiedDTM) * 24
|
|
IF (ElapseTime LE 12) then
|
|
CurrentFlag = True$
|
|
end
|
|
end
|
|
end
|
|
If ForeColor EQ '' then ForeColor = 'Black'
|
|
FirstSchedDetKeyID = SRP_JSON(objScheduleEvent, 'GETVALUE', 'FirstSchedDetKeyID')
|
|
FirstReactorNo = FirstSchedDetKeyID[1, '*']
|
|
FirstScheduleDate = Oconv(FirstSchedDetKeyID[Col2() + 1, '*'], 'D4/')
|
|
LastSchedDetKeyID = SRP_JSON(objScheduleEvent, 'GETVALUE', 'LastSchedDetKeyID')
|
|
LastReactorNo = LastSchedDetKeyID[1, '*']
|
|
LastScheduleDate = Oconv(LastSchedDetKeyID[Col2() + 1, '*'], 'D4/')
|
|
TotalScheduledDays = SRP_JSON(objScheduleEvent, 'GETVALUE', 'TotalScheduledDays')
|
|
ScheduleEvent = SRP_JSON(objScheduleEvent, 'STRINGIFY', 'FAST')
|
|
SRP_JSON(objScheduleEvent, 'RELEASE')
|
|
end else
|
|
|
|
end
|
|
|
|
return
|
|
|
|
|
|
ParseWorkOrder:
|
|
|
|
If Assigned(objWorkOrder) else objWorkOrder = 0
|
|
If objWorkOrder LE 0 then
|
|
SRP_JSON(objWorkOrder, 'PARSE', WorkOrder)
|
|
end
|
|
If objWorkOrder GT 0 then
|
|
WorkOrderNo = SRP_JSON(objWorkOrder, 'GETVALUE', 'WorkOrderNumber')
|
|
EpiPartNo = SRP_JSON(objWorkOrder, 'GETVALUE', 'EpiPartNumber')
|
|
WOReactorType = SRP_JSON(objWorkOrder, 'GETVALUE', 'ReactorType')
|
|
PSN = SRP_JSON(objWorkOrder, 'GETVALUE', 'PSN')
|
|
Recipe = SRP_JSON(objWorkOrder, 'GETVALUE', 'Recipe')
|
|
HotLot = Iconv(SRP_JSON(objWorkOrder, 'GETVALUE', 'HotLot'), 'BYes,No')
|
|
Closed = Iconv(SRP_JSON(objWorkOrder, 'GETVALUE', 'Closed'), 'BYes,No')
|
|
TotalWafers = SRP_JSON(objWorkOrder, 'GETVALUE', 'TotalWafers')
|
|
WafersRemaining = SRP_JSON(objWorkOrder, 'GETVALUE', 'WafersRemaining')
|
|
PercentComplete = SRP_JSON(objWorkOrder, 'GETVALUE', 'PercentComplete')
|
|
CustNameShort = SRP_JSON(objWorkOrder, 'GETVALUE', 'Company.NameShort')
|
|
SRP_JSON(objWorkOrder, 'RELEASE')
|
|
end else
|
|
|
|
end
|
|
|
|
return
|
|
|
|
GetLastEndTime:
|
|
|
|
EndTimes = ''
|
|
For Each SchedDetKeyID in SchedDetKeyIDs using @VM
|
|
SchedDetRow = Database_Services('ReadDataRow', 'SCHED_DET', SchedDetKeyID)
|
|
EndTimes := SchedDetRow<SCHED_DET_END_TIME$> : @FM
|
|
Next SchedDetKeyID
|
|
EndTimes[-1, 1] = ''
|
|
// Sort the list of end times so the latest time is at the top.
|
|
EndTimes = SRP_Array('SortRows', EndTimes, 'DR1', 'ARRAY', @FM, @VM)
|
|
LastEndTime = EndTimes<1>
|
|
|
|
end
|
|
|