open-insight/LSL2/STPROC/SCHEDULE_SERVICES.txt

3564 lines
150 KiB
Plaintext

Function Schedule_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 : Schedule_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. - [IREPIOI-8]
01/07/18 dmb Update the AdjustScheduleEvents service to treat 1-day events as previous events so they
will get skipped over when future events are adjusted. - [IREPIOI-14]
05/09/19 dmb Update AdjustOtherWorkOrderEvents gosub to sort AllWorkOrderEvents using the existing
sort order of AllWorkOrderKeys instead of sorting the array directly because the data
doesn't allow a predictible sort. - [IREPIOI-70]
***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler
$insert APP_INSERTS
$insert SERVICE_SETUP
$insert RLIST_EQUATES
$insert REACTOR_EQUATES
$insert SCHED_DET_NG_EQUATES
$insert WO_LOG_EQUATES
$Insert WO_MAT_EQUATES
$insert WO_SCHEDULE_NG_EQUATES
$insert COMPANY_EQUATES
$insert PROD_VER_EQUATES
$insert PRS_LAYER_EQU
$insert SCHEDULE_EVENT_SUMMMARY_EQUATES
$insert EPI_PART_EQUATES
$insert PROD_SPEC_EQUATES
$insert SCHED_HIST_EQUATES
$Insert RDS_EQUATES
Equ new_exist$ To 0 ; * Reduce Mode 0
Equ next_cur$ To 1
Equ add_exist$ To 2
EQU NOTIFICATION_PERIOD$ TO 12
Declare subroutine Error_Services, Schedule_Services, Memory_Services, RList, Database_Services, Logging_Services
Declare subroutine Btree.Extract, Set_Status, Reduce, FSMsg, Messaging_Services, obj_Notes, Make.List
Declare function SRP_Array, Schedule_Services, Memory_Services, Database_Services, SRP_Sort_Array, Datetime
Declare function Epi_Part_Services, SRP_Math, SRP_Hash, obj_Prod_Spec, Logging_Services, Environment_Services
Declare function Work_Order_Services, RTI_CreateGUID, Reactor_Services, Schedule_Services, NextKey, SRP_Datetime
Declare function SRP_Time, Lsl_Users_Services
Date = Oconv(Date(), 'D4/')
LogFileName = Date[7, 4] : '-' : Date[1, 2] : '-' : Date[4, 2] : ' Scheduler Log.csv'
Headers = 'Logging DTM' : @FM : 'Service Step' : @FM : 'Service Notes'
ColumnWidths = 20 : @FM : 30 : @FM : 150
GoToService else
Error_Services('Set', Service : ' is not a valid service request within the ' : ServiceModule : ' services module.')
end
Return Response else ''
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options BLOCK_GROUP = 'HARD_BLOCK', 'SOFT_BLOCK'
Options ACTIONS = 'ADD', 'STOP', 'CANCEL', 'MODIFY'
Options BOOLEAN = True$, False$
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------------------------------------
// GetReactors
//
// Returns an array of reactors that will be used to populate the SRP Schedule control. All reactors will be returned
// other than 0.
//----------------------------------------------------------------------------------------------------------------------
Service GetReactors(ReactorType, SusceptorSize)
ServiceKeyID := '*' : Reactortype : '*' : SusceptorSize
Reactors = Memory_Services('GetValue', ServiceKeyID)
If Reactors EQ '' then
TableName = 'REACTOR'
hReactors = Database_Services('GetTableHandle', TableName)
If Error_Services('NoError') then
Select hReactors
Done = False$
ReactorArray = ''
MetaArray = ''
Loop
Readnext ReactorNo ELSE Done = True$
Until Done
If ReactorNo GT 0 then
* ReactorRec = Database_Services('ReadDataRow', TableName, ReactorNo)
ReactorRec = Schedule_Services('GetReactorDetails', ReactorNo)
If Error_Services('NoError') then
AcceptReactor = True$ ; // Assume accepted for now.
Type = ReactorRec<REACTOR_REACT_TYPE$>
Size = ReactorRec<REACTOR_SUSC_POCKET_SIZE$>
SecondChamber = ReactorRec<REACTOR_SECOND_CHAMBER$>
If (ReactorType NE '') AND (Type NE ReactorType) then
If (ReactorType[1, 3] EQ 'ASM' OR ReactorType EQ 'EPS') AND (Type[1, 3] EQ 'ASM' OR Type EQ 'EPS') else
// The reactor type for this work order does not match the required reactor type.
AcceptReactor = False$
end
end
If AcceptReactor AND SusceptorSize NE '' then
If Size NE SusceptorSize then
// The susceptor size for this work order does not match the required susceptor size.
AcceptReactor = False$
end
end
If (Type _EQC 'EPP' OR Type _EQC 'EpiPro') AND SecondChamber EQ '' then
AcceptReactor = False$
end
If AcceptReactor then
Begin Case
Case Type _eqc 'ASM+' ; Type = 'ASM+'
Case Type _eqc 'EPP' ; Type = 'EpiPro'
Case Type _eqc 'EPS' ; Type = 'ASM'
Case Type _eqc 'HTR' ; Type = 'HTR'
Case Type _eqc 'GAN' ; Type = 'GaN'
Case Type _eqc '' ; Type = '***'
End Case
Assignment = ReactorRec<REACTOR_REACT_ASSIGNMENT$>
Location = ReactorRec<REACTOR_LOCATIONX$>
Begin Case
Case Assignment EQ 'M'
// Dedicated
Background = 'Pink'
Case Assignment EQ 'C'
// Non-Dedicated
Background = 'LightGreen'
Case Assignment EQ 'G'
// GaN
Background = 'SkyBlue'
Case Assignment EQ 'O'
// Out-of-Service
Background = 'Silver'
Case Otherwise$
// Default to Dedicated
Background = 'Pink'
End Case
EntityName = ReactorNo
If SecondChamber NE '' then EntityName := '/' : SecondChamber
ReactorArray := 'ENT' : @VM : 1 : @VM : ReactorNo : @VM : EntityName : @VM : Type : CRLF$ : Size : @VM : 'Black' : @VM : Background : @FM
MetaArray := ReactorNo : @VM : Oconv(Assignment, '[REACT_ASSIGN_CONV]') : @VM : Location : @FM
end
end
end
Repeat
ReactorArray[-1, 1] = ''
ReactorArray = SRP_Array('SortRows', ReactorArray, 'AR3', 'LIST', @FM, @VM)
MetaArray[-1, 1] = ''
MetaArray = SRP_Array('SortRows', MetaArray, 'AR1', 'LIST', @FM, @VM)
Reactors = ReactorArray : @RM : MetaArray
Memory_Services('SetValue', ServiceKeyID, Reactors)
end
end
Response = Reactors
end service
//----------------------------------------------------------------------------------------------------------------------
// GetReactorDetails
//
//----------------------------------------------------------------------------------------------------------------------
Service GetReactorDetails(ReactorNo)
ServiceKeyID := '*' : ReactorNo
ReactorDetails = Memory_Services('GetValue', ServiceKeyID)
If ReactorDetails EQ '' then
If (ReactorNo NE '') AND (ReactorNo NE 0) then
TableName = 'REACTOR'
ReactorDetails = Database_Services('ReadDataRow', TableName, ReactorNo)
If Error_Services('NoError') then
Memory_Services('SetValue', ServiceKeyID, ReactorDetails)
end
end else
Error_Services('Add', 'ReactorNo argument was missing from the ' : Service : ' service.')
end
end
Response = ReactorDetails
end service
//----------------------------------------------------------------------------------------------------------------------
// GetReactorAssignments
//
// Returns an array of key pair "Reactor:Assignment" that will be used to populate the SRP Schedule control. All reactors will be returned
// other than 0.
//----------------------------------------------------------------------------------------------------------------------
Service GetReactorAssignments()
ReactorAssignments = Memory_Services('GetValue', ServiceKeyID)
ReactorAssignments = ''
If ReactorAssignments EQ '' then
TableName = 'REACTOR'
hReactors = Database_Services('GetTableHandle', TableName)
If Error_Services('NoError') then
Select hReactors
Done = False$
LOOP
READNEXT ReactorNo ELSE Done = True$
UNTIL Done
If ReactorNo GT 0 then
ReactorRec = Database_Services('ReadDataRow', TableName, ReactorNo)
If Error_Services('NoError') then
Assignment = ReactorRec<REACTOR_REACT_ASSIGNMENT$>
ReactorAssignments := ReactorNo : @VM : Assignment : @FM
end
end
REPEAT
ReactorAssignments[-1, 1] = ''
ReactorAssignments = SRP_Array('SortRows', ReactorAssignments, 'AR1', 'LIST', @FM, @VM)
Memory_Services('SetValue', ServiceKeyID, ReactorAssignments)
end
end
Response = ReactorAssignments
end service
//----------------------------------------------------------------------------------------------------------------------
// GetTimeBlockList
//
// Returns an array of reactors assignments that will be used to populate the SRP Schedule control. All reactors will be
// returned other than 0.
//----------------------------------------------------------------------------------------------------------------------
Service GetTimeBlockList(StartDate, EndDate)
TimeBlockList = ''
ReactorAssignments = Schedule_Services('GetReactorAssignments')
For Each ReactorAssignment in ReactorAssignments using @FM
ReactorNo = ReactorAssignment<0, 1>
Assignment = ReactorAssignment<0, 2>
For Date = StartDate to EndDate
StartDTM = Date : '.0'
EndDTM = (Date + 1) : '.0'
Begin Case
Case Assignment EQ 'M'
// Dedicated
TimeBlockList := ReactorNo : @VM : ReactorNo : '*' : Date : @VM : StartDTM : @VM : EndDTM : @VM : 'Pink' : @FM
Case Assignment EQ 'C'
// Non-Dedicated
TimeBlockList := ReactorNo : @VM : ReactorNo : '*' : Date : @VM : StartDTM : @VM : EndDTM : @VM : 'LightGreen' : @FM
Case Assignment EQ 'G'
// GaN
TimeBlockList := ReactorNo : @VM : ReactorNo : '*' : Date : @VM : StartDTM : @VM : EndDTM : @VM : 'SkyBlue' : @FM
Case Assignment EQ 'O'
// Out-of-Service
TimeBlockList := ReactorNo : @VM : ReactorNo : '*' : Date : @VM : StartDTM : @VM : EndDTM : @VM : 'Silver' : @FM
Case Otherwise$
// Default to Dedicated
TimeBlockList := ReactorNo : @VM : ReactorNo : '*' : Date : @VM : StartDTM : @VM : EndDTM : @VM : 'Pink' : @FM
End Case
Next Date
Next ReactorAssignment
TimeBlockList[-1, 1] = ''
Response = TimeBlockList
end service
//----------------------------------------------------------------------------------------------------------------------
// GetScheduleEvents
//
// Returns an array of schedule details that will be used to populate the SRP Schedule control.
//----------------------------------------------------------------------------------------------------------------------
Service GetScheduleEvents(StartDate, EndDate, ReactorNo, WorkOrderNo, BlockOut)
SRP_Stopwatch('Reset')
SRP_Stopwatch('Start', Service)
ScheduleEvents = ''
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$
If (StartDate NE '') then
SchedDetKeyIDs = Schedule_Services('GetScheduleDetailKeys', StartDate, EndDate, ReactorNo, WorkOrderNo, BlockOut)
If SchedDetKeyIDs NE '' then
SchedDetKeyIDs = SRP_Array('SortRows', SchedDetKeyIDs, 'AR1' : @FM : 'AR2' : @FM : 'AR3', 'LIST', @FM, '*')
If Error_Services('NoError') then
PrevWorkOrderNo = ''
Lum = 80
For Each SchedDetKeyID in SchedDetKeyIDs using @FM setting fPos
SchedDetRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetKeyID)
ReactorNo = SchedDetRec<SCHED_DET_NG.REACT_NO$>
WorkOrderNo = SchedDetRec<SCHED_DET_NG.WO_NO$>
StartDTM = SchedDetRec<SCHED_DET_NG.START_DTM$>
StopDTM = SchedDetRec<SCHED_DET_NG.STOP_DTM$>
ScheduleEventSummary = Schedule_Services('GetScheduleEventSummary', SchedDetKeyID, False$)
ScheduleEvents := ReactorNo : @VM
ScheduleEvents := SchedDetKeyID : @VM ; //WorkOrderNo : '*' : ScheduleEventSummary<SES_EPIPARTNO$> : '*' : ReactorNo : '*' : ScheduleDate : @VM
ScheduleEvents := ScheduleEventSummary<SES_STARTDTM$> : @VM
ScheduleEvents := ScheduleEventSummary<SES_ENDDTM$> : @VM
ScheduleEvents := ScheduleEventSummary<SES_BACKCOLOR$> : ' L=' : Lum : @VM
ScheduleEvents := ScheduleEventSummary<SES_FORECOLOR$> : @VM
ScheduleEvents := ScheduleEventSummary<SES_TITLE$> : @VM
ScheduleEvents := ScheduleEventSummary<SES_DESCRIPTION$> : @VM
ScheduleEvents := @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM
ScheduleEvents := ScheduleEventSummary<SES_HOTLOTFLAG$> : @SVM : ScheduleEventSummary<SES_NOTEFLAG$> : @SVM : ScheduleEventSummary<SES_WOCLOSEDFLAG$> : @SVM : ScheduleEventSummary<SES_CURRENTFLAG$>
ScheduleEvents := @FM
Next SchedDetKeyID
ScheduleEvents[-1, 1] = ''
ScheduleEvents = SRP_Array('SortRows', ScheduleEvents, 'AR1' : @FM : 'AR3' : @FM : 'AR4' : @FM : 'AL7', 'LIST', @FM, @VM)
end
end
end else
Error_Services('Add', 'StartDate argument was missing from the ' : Service : ' service.')
end
Response = ScheduleEvents
SRP_Stopwatch('Stop', Service)
end service
Service GetScheduleEvent(SchedDetKeyID)
SchedEvent = ''
If SchedDetKeyID NE '' then
Lum = 80
SchedDetRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetKeyID)
ReactorNo = SchedDetRec<SCHED_DET_NG.REACT_NO$>
WorkOrderNo = SchedDetRec<SCHED_DET_NG.WO_NO$>
StartDTM = SchedDetRec<SCHED_DET_NG.START_DTM$>
StopDTM = SchedDetRec<SCHED_DET_NG.STOP_DTM$>
ScheduleEventSummary = Schedule_Services('GetScheduleEventSummary', SchedDetKeyID, False$)
SchedEvent = ReactorNo : @VM
SchedEvent := SchedDetKeyID : @VM
SchedEvent := ScheduleEventSummary<SES_STARTDTM$> : @VM
SchedEvent := ScheduleEventSummary<SES_ENDDTM$> : @VM
SchedEvent := ScheduleEventSummary<SES_BACKCOLOR$> : ' L=' : Lum : @VM
SchedEvent := ScheduleEventSummary<SES_FORECOLOR$> : @VM
SchedEvent := ScheduleEventSummary<SES_TITLE$> : @VM
SchedEvent := ScheduleEventSummary<SES_DESCRIPTION$> : @VM
SchedEvent := @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM
SchedEvent := ScheduleEventSummary<SES_HOTLOTFLAG$> : @SVM : ScheduleEventSummary<SES_NOTEFLAG$> : @SVM : ScheduleEventSummary<SES_WOCLOSEDFLAG$> : @SVM : ScheduleEventSummary<SES_CURRENTFLAG$>
end
Response = SchedEvent
end service
//----------------------------------------------------------------------------------------------------------------------
// GetScheduleEventSummary
//
// Returns an @FM array of event summary data related to the indicated reactor and work order.
//----------------------------------------------------------------------------------------------------------------------
Service GetScheduleEventSummary(SchedDetKeyID, IncludeWaferDetails)
If IncludeWaferDetails NE True$ then IncludeWaferDetails = False$
ScheduleEventSummary = ''
If SchedDetKeyID NE '' then
SchedDetRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetKeyID)
ReactorNo = SchedDetRec<SCHED_DET_NG.REACT_NO$>
WorkOrderNo = SchedDetRec<SCHED_DET_NG.WO_NO$>
StartDTM = SchedDetRec<SCHED_DET_NG.START_DTM$>
StopDTM = SchedDetRec<SCHED_DET_NG.STOP_DTM$>
Desc = SchedDetRec<SCHED_DET_NG.DESC$>
Modified = SchedDetRec<SCHED_DET_NG.MODIFIED$>
BlockOut = SchedDetRec<SCHED_DET_NG.BLOCK_OUT$>
BlockOutType = SchedDetRec<SCHED_DET_NG.BLOCK_OUT_TYPE$>
BackColor = SchedDetRec<SCHED_DET_NG.BACKCOLOR$>
ForeColor = SchedDetRec<SCHED_DET_NG.FORECOLOR$>
EventWfrQty = SchedDetRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
ReactorDetails = Schedule_Services('GetReactorDetails', ReactorNo)
Type = ReactorDetails<REACTOR_REACT_TYPE$>
Size = ReactorDetails<REACTOR_SUSC_POCKET_SIZE$>
WOLogRow = Memory_Services('GetValue', 'WOLogRow' : '*' : ReactorNo : '*' : WorkOrderNo, True$, 5)
If WOLogRow EQ '' then
WOLogRow = Work_Order_Services('GetWorkOrderLogDetail', WorkOrderNo)
Memory_Services('SetValue', 'WOLogRow' : '*' : ReactorNo : '*' : WorkOrderNo, WOLogRow)
end
CustNo = WOLogRow<WO_LOG_CUST_NO$>
EpiPartNo = WOLogRow<WO_LOG_EPI_PART_NO$>
ProdVerNo = WOLogRow<WO_LOG_PROD_VER_NO$>
TotalWafers = SchedDetRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
WOClosedFlag = SchedDetRec<SCHED_DET_NG.EVENT_COMP$>
HotLotFlag = WOLogRow<WO_LOG_HOT_FLAG$>
If Memory_Services('IsValueExpired', 'CompanyRow' : '*' : ReactorNo : '*' : WorkOrderNo, 60, True$) then
CompanyRow = Database_Services('ReadDataRow', 'COMPANY', CustNo)
Memory_Services('SetValue', 'CompanyRow' : '*' : ReactorNo : '*' : WorkOrderNo, CompanyRow)
end else
CompanyRow = Memory_Services('GetValue', 'CompanyRow' : '*' : ReactorNo : '*' : WorkOrderNo)
end
CustName = CompanyRow<COMPANY_ABBREV$>
Begin Case
Case CustName _EQC 'International Rectifier' ; CustNameShort = 'IR'
Case CustName _EQC 'IRNewport' ; CustNameShort = 'Newport'
Case CustName _EQC 'IFX (Kulim)' ; CustNameShort = 'Kulim'
Case CustName _EQC 'IFX Austria AG' ; CustNameShort = 'Austria'
Case CustName _EQC 'Tower Semiconductor' ; CustNameShort = 'Tower'
Case Otherwise$ ; CustNameShort = CustName
End Case
TableName = 'SCHED_DET_NG'
NoteFlag = ''
If BlockOut then
Title = 'Block Out' : ' (' : BlockOutType : ')'
end else
Title = WorkOrderNo : ' (' : CustNameShort : ' ' : EpiPartNo : ')'
end
HardBlock = ( (BlockOutType EQ 'Out of Service (OOS)') or (BlockOutType EQ 'Down No Material (DNM)') or (BlockOutType EQ 'Factory Down Time') )
Begin Case
Case BackColor NE ''
// Set in the schedule detail record. Use current color.
Case HotLotFlag
BackColor = 'LightCoral'
Case WOClosedFlag
BackColor = 'LightGray'
Case HardBlock
BackColor = 'LightGoldenRodYellow'
Case BlockOut
BackColor = 'Plum'
Case Otherwise$
BackColor = 'LightSteelBlue'
End Case
Begin Case
Case ForeColor NE ''
// Set in the schedule detail record. Use current color.
Case NoteFlag AND HotLotFlag
ForeColor = 'Yellow'
Case NoteFlag AND BlockOut
ForeColor = 'Indigo'
Case NoteFlag
ForeColor = 'Red'
Case Otherwise$
ForeColor = 'Black'
End Case
If IncludeWaferDetails EQ True$ then
Remaining = Schedule_Services('GetEventRemainingWfrs', SchedDetKeyID)
If TotalWafers GT 0 then
Complete = (TotalWafers - Remaining) / TotalWafers * 100
end else
Complete = 0
end
TotalDays = SRP_Math('ROUND', StopDTM - StartDTM, 1)
ProdVerRow = Memory_Services('GetValue', 'ProdVerRow' : '*' : ReactorNo : '*' : WorkOrderNo, True$, 5)
If ProdVerRow EQ '' then
ProdVerRow = Database_Services('ReadDataRow', 'PROD_VER', ProdVerNo)
Memory_Services('SetValue', 'ProdVerRow' : '*' : ReactorNo : '*' : WorkOrderNo, ProdVerRow)
end
PSN = ProdVerRow<PROD_VER_PROC_STEP_PSN$>
LayerSpecs = obj_Prod_Spec('GetLayerProp',PSN:@RM:@RM:1) ;* Returns specs for all layers in internal format
LayerSpec = FIELD(LayerSpecs,@RM,1) ;* Take the first Layer
LayerSet = FIELD(LayerSpec,@FM,1) ;* Not used here but shown for clarity
LayerSpec = FIELD(LayerSpec,@FM,2,99) ;* LayerSpec without the LayerSet
Recipe = LayerSpec<PRS_LAYER_RECIPE$>
end else
Remaining = ''
TotalWafers = ''
Complete = ''
TotalDays = ''
PSN = ''
Recipe = ''
end
StartDate = Field(StartDTM, '.', 1)
EndDate = Field(StopDTM, '.', 1)
ScheduleEventSummary := Type : @FM
ScheduleEventSummary := Size : @FM
ScheduleEventSummary := CustName : @FM
ScheduleEventSummary := CustNameShort : @FM
ScheduleEventSummary := EpiPartNo : @FM
ScheduleEventSummary := Oconv(StartDate, 'D4/') : @FM
ScheduleEventSummary := Oconv(EndDate, 'D4/') : @FM
ScheduleEventSummary := TotalDays : @FM
ScheduleEventSummary := EventWfrQty : @FM
ScheduleEventSummary := Remaining : @FM
ScheduleEventSummary := Oconv(Complete, 'MD0[ %]S') : @FM
ScheduleEventSummary := WOClosedFlag : @FM
ScheduleEventSummary := HotLotFlag : @FM
ScheduleEventSummary := '' : @FM ; //CurrentFlag : @FM
ScheduleEventSummary := NoteFlag : @FM
ScheduleEventSummary := StartDTM : @FM
ScheduleEventSummary := StopDTM : @FM
ScheduleEventSummary := Title : @FM
ScheduleEventSummary := Desc : @FM
ScheduleEventSummary := BackColor : @FM
ScheduleEventSummary := ForeColor : @FM
ScheduleEventSummary := PSN : @FM
ScheduleEventSummary := Recipe : @FM
ScheduleEventSummary := '' : @FM ; // Day Length Code ?
ScheduleEventSummary := BlockOut : @FM
ScheduleEventSummary := BlockOutType
end else
Error_Services('Add', 'ReactorNo or WorkOrderNo argument was missing from the ' : Service : ' service.')
end
Response = ScheduleEventSummary
end service
//----------------------------------------------------------------------------------------------------------------------
// GetScheduleDetailKeys
//
// Returns an array of schedule detail KeyIDs based on the arguments provided.
//----------------------------------------------------------------------------------------------------------------------
Service GetScheduleDetailKeys(StartDate, EndDate, ReactorNo, WorkOrderNo, BlockOut, InProcess=BOOLEAN)
ScheduleDetailKeys = ''
If Not(Num(StartDate)) then StartDate = Iconv(StartDate, 'DT')
If Not(Num(EndDate)) then EndDate = Iconv(EndDate, 'DT')
If BlockOut NE True$ then BlockOut = False$
If ( (ReactorNo NE '') or (StartDate NE '') ) then
CursorVar = ''
SortList = 'REACT_NO' : @FM : 'START_DTM'
Flag = ''
Done = False$
ReduceScript = ''
If ReactorNo NE '' then ReduceScript := 'WITH {REACT_NO} EQ "':ReactorNo:'" AND '
If StartDate NE '' then ReduceScript := "WITH {START_DTM} GT ":Quote(OConv(StartDate, 'DT/^3H')):' AND '
// Additional filters can be added here
ReduceScript[-1, -5] = ''
GoSub ClearCursors
Reduce(ReduceScript, SortList, 1, 'SCHED_DET_NG', CursorVar, Flag)
If Flag then
Select 'SCHED_DET_NG' By SortList using CursorVar then
Open 'SCHED_DET_NG' to hTable then
Loop
ReadNext Key using CursorVar by AT Else Done = True$
Until Done
Locate Key in ScheduleDetailKeys using @FM setting fPos else
// This ensures the list is unique. Duplicates can be found by the Select statement
// if the index on REACT_NO or START_DTM is compromised.
ScheduleDetailKeys<-1> = Key
end
Repeat
end
end
end
GoSub ClearCursors
If InProcess then
// Perform a second select to ensure currently running events get selected.
Flag = ''
Done = False$
ReduceScript = 'WITH {UNPROCESSED_CASS} NE ""'
// Additional filters can be added here
GoSub ClearCursors
Reduce(ReduceScript, SortList, 1, 'SCHED_DET_NG', CursorVar, Flag)
If Flag then
Select 'SCHED_DET_NG' By SortList using CursorVar then
Open 'SCHED_DET_NG' to hTable then
Loop
ReadNext Key using CursorVar by AT Else Done = True$
Until Done
Locate Key in ScheduleDetailKeys using @FM setting fPos else
// This ensures the list is unique. Duplicates can be found by the Select statement
// if the index on REACT_NO or START_DTM is compromised.
ScheduleDetailKeys<-1> = Key
end
Repeat
end
end
end
end
end else
Error_Services('Add', 'StartDate argument was missing from the ' : Service : ' service.')
end
Response = ScheduleDetailKeys
end service
Service CancelScheduleEvent(SchedDetKeyID, ShiftEvents)
RemSchedDetKeys = ''
AddSchedDetKeys = ''
If SchedDetKeyID NE '' then
SchedDetRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetKeyID)
ReactNo = SchedDetRec<SCHED_DET_NG.REACT_NO$>
ReactRec = Database_Services('ReadDataRow', 'REACTOR', ReactNo)
ReactSchedEvents = ReactRec<REACTOR_SCHED_EVENTS$>
Locate SchedDetKeyID in ReactSchedEvents using @VM setting vPos then
PrevSchedKeyID = ReactSchedEvents<0, vPos - 1>
If PrevSchedKeyID NE '' then
// Mark this event as not complete.
PrevSchedDetRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', PrevSchedKeyID)
PrevSchedDetRec<SCHED_DET_NG.EVENT_COMP$> = 0
Database_Services('WriteDataRow', 'SCHED_DET_NG', PrevSchedKeyID, PrevSchedDetRec, True$, False$, True$)
end
end
// Delete canceled event record
Database_Services('DeleteDataRow', 'SCHED_DET_NG', SchedDetKeyID)
end
// Record schedule change in SCHED_HIST table
Schedule_Services('AddSchedHist', SchedDetKeyID, ReactNo, 'CANCEL', @User4)
Response = RemSchedDetKeys
end service
Service GetStartCassNo(SchedDetKey)
RDSNo = ''
CassNo = ''
If SchedDetKey NE '' then
SchedDetRec = Schedule_Services('GetScheduleDetail', SchedDetKey)
WONo = SchedDetRec<SCHED_DET_NG.WO_NO$>
ReactorNo = SchedDetRec<SCHED_DET_NG.REACT_NO$>
EventStartDTM = SchedDetRec<SCHED_DET_NG.START_DTM$>
StartDate = EventStartDTM[1, 'F.']
StartTime = (EventStartDTM[-1, 'B.'] * 86400)
StartTime = StartTime[1, 5]
// Find first cassette with START_DT and START_TIME after event START_DTM
Query = 'SELECT RDS WITH WO EQ ':WONo:' AND WITH REACTOR EQ ':ReactorNo:' BY DATE_IN BY TIME_IN'
Set_Status(0)
GoSub ClearCursors
Rlist(Query, Target_ActiveList$, '', '', '')
If @RecCount then
Done = False$
EOF = False$
Loop
ReadNext RDSNo else EOF = True$
Until EOF EQ True$
DTMIn = Xlate('RDS', RDSNo, 'DATETIME_IN', 'X')
If DTMIN GE EventStartDTM then
Done = True$
end
Until Done EQ True$
Repeat
end
GoSub ClearCursors
errCode = ''
If Get_Status(errCode) then
ErrorMsg = 'Error in service ':Service:' module. Error code':errCode
Error_Services('Add', ErrorMsg)
end
end
If RDSNo NE '' then CassNo = Xlate('RDS', RDSNo, 'CASS_NO', 'X')
Response = CassNo
end service
Service GetEventLastCassNo(SchedDetKey)
RDSNo = ''
CassNo = ''
If SchedDetKey NE '' then
SchedDetRec = Schedule_Services('GetScheduleDetail', SchedDetKey)
ReactorNo = SchedDetRec<SCHED_DET_NG.REACT_NO$>
EventStopDTM = SchedDetRec<SCHED_DET_NG.STOP_DTM$>
WONo = SchedDetRec<SCHED_DET_NG.WO_NO$>
EventStartDTM = SchedDetRec<SCHED_DET_NG.START_DTM$>
StartDate = EventStartDTM[1, 'F.']
StartTime = (EventStartDTM[-1, 'B.'] * 86400)
StartTime = StartTime[1, 5]
StopDate = EventStopDTM[1, 'F.']
StopTime = (EventStopDTM[-1, 'B.'] * 86400)
StopTime = StopTime[1, 5]
// Find first cassette with START_DT and START_TIME after event START_DTM
Query = 'SELECT RDS WITH WO EQ ':WONo:' AND WITH REACTOR EQ ':ReactorNo:' BY-DSND DATE_OUT BY-DSND TIME_OUT'
Set_Status(0)
GoSub ClearCursors
Rlist(Query, Target_ActiveList$, '', '', '')
If @RecCount then
Done = False$
EOF = False$
Loop
ReadNext RDSNo else EOF = True$
Until EOF EQ True$
DTMOut = Xlate('RDS', RDSNo, 'DATETIME_OUT', 'X')
If DTMOut LE EventStopDTM then
Done = True$
end
Until Done EQ True$
Repeat
end
GoSub ClearCursors
errCode = ''
If Get_Status(errCode) then
ErrorMsg = 'Error in service ':Service:' module. Error code':errCode
Error_Services('Add', ErrorMsg)
end
end
If RDSNo NE '' then CassNo = Xlate('RDS', RDSNo, 'CASS_NO', 'X')
Response = CassNo
end service
Service GetEventRemainingWfrs(SchedDetKey)
WfrsRemaining = 0
If SchedDetKey NE '' then
SchedDetRec = Schedule_Services('GetScheduleDetail', SchedDetKey)
WONo = SchedDetRec<SCHED_DET_NG.WO_NO$>
ReactType = Xlate('WO_LOG', WONo, 'REACT_TYPE', 'X')
SchedQty = SchedDetRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
UnprocessedCassettes = SchedDetRec<SCHED_DET_NG.UNPROCESSED_CASS$>
NumUnprocessedCassettes = DCount(UnprocessedCassettes, @VM)
If ReactType NE 'EPP' then
WfrsRemaining = 25 * NumUnprocessedCassettes
end else
If UnprocessedCassettes NE '' then
QueryEndDtm = SchedDetRec<SCHED_DET_NG.START_DTM$>
PrevSchedWfrQty = Schedule_Services('GetScheduledWfrQty', WONo, QueryEndDtm)
StartSlot = Mod(PrevSchedWfrQty, 25) + 1
StartCass = SRP_Math('CEILING', ( (PrevSchedWfrQty + 1) / 25))
If StartCass EQ 0 then StartCass = 1
LastUnprocCass = UnprocessedCassettes<0, NumUnprocessedCassettes>
TotalQty = PrevSchedWfrQty + SchedQty
StopSlot = Mod(TotalQty, 25)
If StopSlot EQ 0 then StopSlot = 25
For each UnprocCass in UnprocessedCassettes using @VM setting vPos
WMIKey = WONo:'*1*':UnprocCass
If RowExists('WM_IN', WMIKey) then
RDSKeys = Xlate('WM_IN', WMIKey, 'RDS_NO', 'X')
UnloadDtms = Xlate('RDS', RDSKeys, 'DATETIME_OUT', 'X')
If UnprocCass EQ StartCass then
StartIndex = StartSlot
end else
StartIndex = 1
end
For SlotIndex = StartIndex to 25
If UnloadDtms<0, SlotIndex> EQ '' then WfrsRemaining += 1
Until ( (UnprocCass EQ LastUnprocCass) and (SlotIndex EQ StopSlot) )
Next SlotIndex
end else
WfrsRemaining += 25
end
Next UnprocCass
end
end
Response = WfrsRemaining
end
If WfrsRemaining GT 0 then
Response = WfrsRemaining
end else
Response = 0
Error_Services('Add', 'Error in ':Service:' service. Negative wafers remaining value returned!')
end
end service
Service GetEventCompStatus(SchedDetKey)
If SchedDetKey NE '' then
SchedDetRec = Schedule_Services('GetScheduleDetail', SchedDetKey)
SchedQty = SchedDetRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
NumUnprocWfrs = Schedule_Services('GetEventRemainingWfrs', SchedDetKey)
NumWfrsProcessed = SchedQty - NumUnprocWfrs
If ( (SchedQty NE '') and (SchedQty GT 0) ) then
PercentComplete = (NumWfrsProcessed / SchedQty)
Response = PercentComplete
end
end
end service
Service GetCurrentEvent(ReactNo)
SchedDetKey = ''
If ReactNo NE '' then
CurrDTM = Datetime()
Query = 'SELECT SCHED_DET_NG WITH REACT_NO EQ "':ReactNo:'" AND WITH EVENT_COMP NE 1 BY START_DTM'
Set_Status(0)
GoSub ClearCursors
Rlist(Query, TARGET_ACTIVELIST$, '', '', '')
errCode = ''
If Not(Get_Status(errCode)) then
ReadNext SchedDetKey else EOF = True$
Response = SchedDetKey
end else
ErrorMsg = 'Error in service ':Service:' module. Error code ':errCode
Error_Services('Add', ErrorMsg)
end
GoSub ClearCursors
end
end service
Service CurrentEventIsBlock(ReactNo)
If ReactNo NE '' then
CurrentSchedDetKey = Schedule_Services('GetCurrentEvent', ReactNo)
If Error_Services('NoError') then
CurrentSchedDetRecord = Database_Services('ReadDataRow', 'SCHED_DET_NG', CurrentSchedDetKey)
CurrentEventIsABlock = CurrentSchedDetRecord<SCHED_DET_NG.BLOCK_OUT$>
If CurrentEventIsABlock EQ '' then
CurrentEventIsABlock = False$
end
Response = CurrentEventIsABlock
end else
Error = Error_Services('GetMessage')
ErrorMsg = 'Error in service ':Service:' module. ':Error
Error_Services('Add', ErrorMsg)
end
end else
ErrorMsg = 'Error in service ':Service:' module. Invalid reactor number.'
Error_Services('Add', ErrorMsg)
end
end service
Service GetNextEvent(ReactNo)
SchedDetKey = ''
If ReactNo NE '' then
CurrDTM = Datetime()
Query = 'SELECT SCHED_DET_NG WITH REACT_NO EQ "':ReactNo:'" AND WITH EVENT_COMP NE 1 BY START_DTM'
Set_Status(0)
GoSub ClearCursors
Rlist(Query, TARGET_ACTIVELIST$, '', '', '')
errCode = ''
If Not(Get_Status(errCode)) then
EOF = False$
ReadNext SchedDetKey else EOF = True$
If EOF EQ True$ then
ErrorMsg = 'Error in service ':Service:' module. No incomplete events in schedule'
Error_Services('Add', ErrorMsg)
end else
ReadNext SchedDetKey else EOF = True$
If EOF EQ True$ then
ErrorMsg = 'Error in service ':Service:' module. No incomplete events in schedule after current event'
Error_Services('Add', ErrorMsg)
end else
Response = SchedDetKey
end
end
end else
ErrorMsg = 'Error in service ':Service:' module. Error code ':errCode
Error_Services('Add', ErrorMsg)
end
GoSub ClearCursors
end
end service
Service GetEngagedEvent(ReactNo)
SchedDetKey = ''
If ReactNo NE '' then
CurrDTM = Datetime()
Query = 'SELECT SCHED_DET_NG WITH REACT_NO EQ "':ReactNo:'" AND WITH UNPROCESSED_CASS NE "" AND WITH EVENT_COMP NE 1 BY START_DTM'
Set_Status(0)
GoSub ClearCursors
Rlist(Query, TARGET_ACTIVELIST$, '', '', '')
errCode = ''
If Not(Get_Status(errCode)) then
ReadNext SchedDetKey else EOF = True$
Response = SchedDetKey
end else
ErrorMsg = 'Error in service ':Service:' module. Error code ':errCode
Error_Services('Add', ErrorMsg)
end
GoSub ClearCursors
end
end service
Service GetLastEngagedEvent(ReactNo)
SchedDetKey = ''
If ReactNo NE '' then
CurrDTM = Datetime()
Query = 'SELECT SCHED_DET_NG WITH REACT_NO EQ "':ReactNo:'" AND WITH START_DTM LT "':CurrDTM:'" '
Query := 'BY-DSND START_DTM'
Set_Status(0)
GoSub ClearCursors
Rlist(Query, Target_ActiveList$, '', '', '')
ReadNext SchedDetKey else EOF = True$
GoSub ClearCursors
errCode = ''
If Get_Status(errCode) then
ErrorMsg = 'Error in service ':Service:' module. Error code':errCode
Error_Services('Add', ErrorMsg)
end else
Response = SchedDetKey
end
end
end service
Service NextEventIsSamePsn(ReactNo)
Response = False$
If ReactNo NE '' then
CurrentSchedDetKey = Schedule_Services('GetCurrentEvent', ReactNo)
If Error_Services('NoError') then
CurrentEventIsABlock = Schedule_Services('CurrentEventIsBlock', ReactNo)
If CurrentEventIsABlock EQ False$ then
CurrentSchedDetRecord = Database_Services('ReadDataRow', 'SCHED_DET_NG', CurrentSchedDetKey)
CurrentEventWoNo = CurrentSchedDetRecord<SCHED_DET_NG.WO_NO$>
CurrentEventWoMatRecord = Database_Services('ReadDataRow', 'WO_MAT', CurrentEventWoNo:'*1')
CurrentEventRdsNo = CurrentEventWoMatRecord<WO_MAT_RDS_NO$>
CurrentEventRdsRecord = Database_Services('ReadDataRow', 'RDS', CurrentEventRdsNo)
CurrentEventPsn = CurrentEventRdsRecord<RDS_PROD_SPEC_ID$>
NextSchedDetKey = Schedule_Services('GetNextEvent', ReactNo)
If Error_Services('NoError') then
NextEventIsABlock = Schedule_Services('NextEventIsBlock', ReactNo)
If NextEventIsABlock EQ False$ then
NextSchedDetRecord = Database_Services('ReadDataRow', 'SCHED_DET_NG', NextSchedDetKey)
NextEventWoNo = NextSchedDetRecord<SCHED_DET_NG.WO_NO$>
NextEventWoMatRecord = Database_Services('ReadDataRow', 'WO_MAT', NextEventWoNo:'*1')
NextEventRdsNo = NextEventWoMatRecord<WO_MAT_RDS_NO$>
NextEventRdsRecord = Database_Services('ReadDataRow', 'RDS', NextEventRdsNo)
NextEventPsn = NextEventRdsRecord<RDS_PROD_SPEC_ID$>
PsnsMatch = CurrentEventPsn EQ NextEventPsn
Response = PsnsMatch
end
end
end
end
end else
ErrorMsg = 'Error in service ':Service:' module. Invalid reactor number.'
Error_Services('Add', ErrorMsg)
end
end service
Service NextEventIsBlock(ReactNo)
If ReactNo NE '' then
NextSchedDetKey = Schedule_Services('GetNextEvent', ReactNo)
If Error_Services('NoError') then
NextSchedDetRecord = Database_Services('ReadDataRow', 'SCHED_DET_NG', NextSchedDetKey)
NextEventIsABlock = NextSchedDetRecord<SCHED_DET_NG.BLOCK_OUT$>
If NextEventIsABlock EQ '' then
NextEventIsABlock = False$
end
Response = NextEventIsABlock
end else
Error = Error_Services('GetMessage')
ErrorMsg = 'Error in service ':Service:' module. ':Error
Error_Services('Add', ErrorMsg)
end
end else
ErrorMsg = 'Error in service ':Service:' module. Invalid reactor number.'
Error_Services('Add', ErrorMsg)
end
end service
//----------------------------------------------------------------------------------------------------------------------
// AdjustScheduleEvents
//
// Adjusts the schedule detail rows for a given reactor.
//----------------------------------------------------------------------------------------------------------------------
Service AdjustScheduleEvents(ReactNo)
HaveLock = False$
hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
Lock hSysLists, ServiceKeyID:'*':ReactNo then
HaveLock = True$
CurrDTM = Datetime()
// Log a line break to separate service calls
LogData = ''
LogData<1> = ''
LogData<2> = ''
LogData<3> = ''
Schedule_Services('LogActivity', ReactNo, LogData)
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '1'
LogData<3> = 'Begin Adjusting Reactor ':ReactNo
Schedule_Services('LogActivity', ReactNo, LogData)
OrigReactNo = ReactNo
Done = False$
// Mark all hard blocks with end datetimes before the current date time as complete.
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '2'
LogData<3> = 'Checking for hard block events to close.'
Schedule_Services('LogActivity', ReactNo, LogData)
EOF = False$
Query = 'SELECT SCHED_DET_NG WITH REACT_NO EQ ':Quote(ReactNo):' AND WITH BLOCK_OUT EQ ':Quote(True$)
Query := ' AND WITH STOP_DTM LT ':Quote(CurrDtm):' AND WITH EVENT_COMP NE 1 AND WITH HARD_BLOCK EQ 1'
GoSub ClearCursors
Set_Status(0)
RList(Query, TARGET_ACTIVELIST$, '', '', '')
If Not(Get_Status(ErrCode)) then
LoopIndex = 0
Loop
ReadNext OldBlockOutKey else EOF = True$
Until EOF
LoopIndex += 1
OldBlockOutRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', OldBlockOutKey)
If Error_Services('NoError') then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '2.':LoopIndex
LogData<3> = 'Marking previous hard block event complete: ':OldBlockOutKey
Schedule_Services('LogActivity', ReactNo, LogData)
OldBlockOutRec<SCHED_DET_NG.EVENT_COMP$> = True$
Database_Services('WriteDataRow', 'SCHED_DET_NG', OldBlockOutKey, OldBlockOutRec, True$, False$, True$)
end
Repeat
end
// Find the current event and start adjusting from there.
CurrEventKey = Schedule_Services('GetCurrentEvent', ReactNo)
If CurrEventKey EQ '' then
CurrEventKey = Schedule_Services('GetLastEngagedEvent', ReactNo)
end
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '3.1'
LogData<3> = 'Located starting event to adjust from: ':CurrEventKey
Schedule_Services('LogActivity', ReactNo, LogData)
ReactEventList = Xlate('REACTOR', ReactNo, 'SCHED_EVENTS', 'X')
OrigReactEventList = ReactEventList
NewReactEventList = ''
If ReactEventList NE '' then
NumEvents = DCount(ReactEventList, @VM)
NonHBList = ''
HBList = ''
TruncatePos = ''
// Build list of non-hard block events and a list of hard block events.
Locate CurrEventKey in ReactEventList using @VM setting vPos then
TruncatePos = vPos
For EventKeyIndex = vPos to NumEvents
EventKey = ReactEventList<0, EventKeyIndex>
HardBlock = Xlate('SCHED_DET_NG', EventKey, 'HARD_BLOCK', 'X')
If HardBlock EQ True$ then
HBList<0, -1> = EventKey
end else
NonHBList<0, -1> = EventKey
end
Next EventKeyIndex
// Create a new list up to, but not including, the current event.
For EventKeyIndex = 1 to (vPos - 1)
NewReactEventList<0, EventKeyIndex> = ReactEventList<0, EventKeyIndex>
Next EventKeyIndex
end
If NonHBList NE '' then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '3.2'
LogData<3> = 'Attempting to merge non-hard block consecutive events with matching work orders'
Schedule_Services('LogActivity', ReactNo, LogData)
// Merge all non-hard block events (merge consecutive events with matching work orders)
NumNonHBEvents = DCount(NonHBList, @VM)
// Stop at the second to last event so that we don't overstep the array
For EventIndex = 1 to (NumNonHBEvents - 1)
// Look ahead at the next event to see if the work order numbers match. If so, then "merge" the events.
// Merging two events
// - Add scheduled quantities together into first event
// - Delete second event record
// - Delete event from NonHBList
// - Decrement NumNonHBEvents
// - Update next event pointer
ThisEventKey = NonHBList<0, EventIndex>
ThisEventWONo = Xlate('SCHED_DET_NG', ThisEventKey, 'WO_NO', 'X')
ThisEventBlockOut = Xlate('SCHED_DET_NG', ThisEventKey, 'BLOCK_OUT', 'X')
NextEventKey = NonHBList<0, EventIndex + 1>
NextEventWONo = Xlate('SCHED_DET_NG', NextEventKey, 'WO_NO', 'X')
NextEventBlockOut = Xlate('SCHED_DET_NG', NextEventKey, 'BLOCK_OUT', 'X')
// Do not merge blockout events!
If ( (ThisEventBlockOut NE True$) and (NextEventBlockOut NE True$) and (ThisEventWONo EQ NextEventWONo) ) then
// Merge the events
NextEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', NextEventKey)
NextEventWfrQty = NextEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
NextEventUnprocCass = NextEventRec<SCHED_DET_NG.UNPROCESSED_CASS$>
NextEventProcCass = NextEventRec<SCHED_DET_NG.PROCESSED_CASS$>
NextNextEventKey = NonHBList<EventIndex + 2>
ThisEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', ThisEventKey)
ThisEventWfrQty = ThisEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
ThisEventUnprocCass = ThisEventRec<SCHED_DET_NG.UNPROCESSED_CASS$>
ThisEventProcCass = ThisEventRec<SCHED_DET_NG.PROCESSED_CASS$>
// Merge wafer quantities and cassette lists
NewEventWfrQty = ThisEventWfrQty + NextEventWfrQty
ThisEventUnprocCass = SRP_Array('Join', ThisEventUnprocCass, NextEventUnprocCass, 'OR', @VM)
ThisEventProcCass = SRP_Array('Join', ThisEventProcCass, NextEventProcCass, 'OR', @VM)
ThisEventUnprocCass = SRP_Array('SortSimpleList', ThisEventUnprocCass, 'AscendingNumbers', @VM)
ThisEventProcCass = SRP_Array('SortSimpleList', ThisEventProcCass, 'AscendingNumbers', @VM)
// Merging split EpiPro events will result in the cassette between the two events
// being in both the processed and unprocessed cassette lists.
CutoverCassList = SRP_Array('Join', ThisEventUnprocCass, ThisEventProcCass, 'AND', @VM)
If CutoverCassList NE '' then
For each CutoverCassNo in CutoverCassList using @VM
Locate CutoverCassNo in ThisEventProcCass using @VM setting vPos then
ThisEventProcCass = Delete(ThisEventProcCass, 0, vPos, 0)
end
Next CutoverCassNo
end
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '3.3'
LogData<3> = 'Merging ':ThisEventKey:' with ':ThisEventWfrQty:' wafers and ':NextEventKey:' with ':NextEventWfrQty:' wafers'
Schedule_Services('LogActivity', ReactNo, LogData)
ThisEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$> = NewEventWfrQty
ThisEventRec<SCHED_DET_NG.UNPROCESSED_CASS$> = ThisEventUnprocCass
ThisEventRec<SCHED_DET_NG.PROCESSED_CASS$> = ThisEventProcCass
ThisEventRec<SCHED_DET_NG.EVENT_COMP$> = False$
Database_Services('WriteDataRow', 'SCHED_DET_NG', ThisEventKey, ThisEventRec, True$, False$, True$)
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '3.4'
LogData<3> = 'Merged events into ':ThisEventKey:' with a new quantity of ':NewEventWfrQty
Schedule_Services('LogActivity', ReactNo, LogData)
Database_Services('DeleteDataRow', 'SCHED_DET_NG', NextEventKey, True$, False$)
NonHBList = Delete(NonHBList, 0, (EventIndex + 1), 0)
NumNonHBEvents -= 1
end
Next EventIndex
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4'
LogData<3> = 'Begin rescheduling non-hard block events'
Schedule_Services('LogActivity', ReactNo, LogData)
// Reschedule all non-hard block events
For each EventKey in NonHBList using @VM setting vPos
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos
LogData<3> = 'Begin rescheduling event ':EventKey
Schedule_Services('LogActivity', ReactNo, LogData)
EventRec = Schedule_Services('GetScheduleDetail', EventKey)
BlockOut = EventRec<SCHED_DET_NG.BLOCK_OUT$>
EpiPro = (Xlate('REACTOR', ReactNo, 'REACT_TYPE', 'X') EQ 'EPP')
If BlockOut NE True$ then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.1'
LogData<3> = 'Original event details:'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event work order: ':EventRec<SCHED_DET_NG.WO_NO$>
Schedule_Services('LogActivity', ReactNo, LogData)
Logdata<3> = 'Event start dtm: ':OConv(EventRec<SCHED_DET_NG.START_DTM$>, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event stop dtm: ':OConv(EventRec<SCHED_DET_NG.STOP_DTM$>, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event quantity: ':EventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event complete: ':OConv(EventRec<SCHED_DET_NG.EVENT_COMP$>, "Btrue,false")
Schedule_Services('LogActivity', ReactNo, LogData)
EventAdjustment = Schedule_Services('GetEventAdjustment', EventKey)
UnprocessedCass = EventRec<SCHED_DET_NG.UNPROCESSED_CASS$>
ProcessedCass = EventRec<SCHED_DET_NG.PROCESSED_CASS$>
AllCassList = SRP_Array('Join', UnprocessedCass, ProcessedCass, 'OR', @VM)
AllCassList = SRP_Array('SortSimpleList', AllCassList, 'AscendingNumbers', @VM)
EventComp = (UnprocessedCass EQ '')
If Not(EventRec<SCHED_DET_NG.EVENT_COMP$>) and EventComp then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.1.1'
LogData<3> = 'Closing event ':EventKey
Schedule_Services('LogActivity', ReactNo, LogData)
end
EventRec<SCHED_DET_NG.EVENT_COMP$> = EventComp
WONo = EventRec<SCHED_DET_NG.WO_NO$>
ReactNo = EventRec<SCHED_DET_NG.REACT_NO$>
EventWfrs = EventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
WOQty = Xlate('WO_LOG', WONo, 'QTY', 'X')
// If work order event is engaged then set start DTM to first cassette DTM in.
// If previous event end time overlaps (e.g. soft block finished sooner than scheduled)
// then truncate it. If work order event is not engaged yet, then set start DTM to previous
// event's stop DTM.
RDSNo = ''
DTMIn = ''
EventEngaged = False$
WOMatKeys = ''
For each SchedCassNo in AllCassList using @VM setting SchedIndex
WOMatKeys<0, SchedIndex> = WONo:'*':SchedCassNo
Next SchedCassNo
If EpiPro then
FirstCass = AllCassList<0, 1>
If FirstCass NE '' then
WMIKey = WONo:'*1*':FirstCass
SchedRDSNos = Xlate('WM_IN', WMIKey, 'RDS_NO', 'X')
// Look at the next slot to be scheduled!
QueryEndDtm = EventRec<SCHED_DET_NG.STOP_DTM$>
// Get the number of wafers scheduled up to and including this event.
// This should give us the correct starting wafer slot.
ScheduledWfrQty = Schedule_Services('GetScheduledWfrQty', WONo, QueryEndDtm)
ScheduledWfrQty -= EventWfrs
SchedSlot = Mod(ScheduledWfrQty, 25) + 1
SchedRDSNos = Field(SchedRDSNos, @VM, SchedSlot, (25 - SchedSlot))
end
end else
SchedRDSNos = Xlate('WO_MAT', WOMatKeys, 'RDS_NO', 'X')
end
SchedDateIns = Xlate('RDS', SchedRDSNos, 'DATE_IN', 'X')
If SchedDateIns NE '' then
FirstLoad = ''
For each SchedDateIn in SchedDateIns using @VM setting DateInPos
If SchedDateIn NE '' then
RDSNo = SchedRDSNos<0, DateInPos>
DTMIn = Xlate('RDS', RDSNo, 'DATETIME_IN', 'X')
EventEngaged = True$
end
Until EventEngaged EQ True$
Next SchedDateIn
end
If EventEngaged then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2'
LogData<3> = 'Event ':EventKey:' engaged.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting start dtm to first RDS load dtm: ':OConv(DTMIn, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
// Work order event is engaged so set start DTM to first cassette DTM in.
EventStartDTM = DTMIn
// Ensure previous event doesn't overlap.
CurrNumEvents = DCount(NewReactEventList, @VM)
If CurrNumEvents GT 0 then
PrevEventKey = NewReactEventList<0, CurrNumEvents>
If PrevEventKey NE '' then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2.1'
LogData<3> = 'Ensuring previous event ':PrevEventKey:' does not overlap.'
Schedule_Services('LogActivity', ReactNo, LogData)
PrevEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', PrevEventKey)
PrevEventRecOrig = PrevEventRec
PrevEventOrigStartDTM = PrevEventRec<SCHED_DET_NG.START_DTM$>
PrevEventOrigStopDTM = PrevEventRec<SCHED_DET_NG.STOP_DTM$>
PrevEventOrigQty = PrevEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
PrevEventWONo = PrevEventRec<SCHED_DET_NG.WO_NO$>
PrevEventBlockOut = PrevEventRec<SCHED_DET_NG.BLOCK_OUT$>
PrevEventDuration = PrevEventOrigStopDTM - PrevEventOrigStartDTM
// Ensure previous event is marked as complete
If PrevEventRec<SCHED_DET_NG.EVENT_COMP$> NE True$ then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2.1.1'
LogData<3> = 'Closing previous event ':PrevEventKey:'.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
PrevEventRec<SCHED_DET_NG.EVENT_COMP$> = True$
If Not(PrevEventBlockOut) then
// Set previous event's stop datetime to the last processed cassette/run's unload datetime
PrevEventProcCass = PrevEventRec<SCHED_DET_NG.PROCESSED_CASS$>
PrevEventUnprocCass = PrevEventRec<SCHED_DET_NG.UNPROCESSED_CASS$>
AllPrevEventSchedCass = SRP_Array('Join', PrevEventProcCass, PrevEventUnprocCass, 'OR', @VM)
AllPrevEventSchedCass = SRP_Array('SortSimpleList', AllPrevEventSchedCass, 'AscendingNumbers', @VM)
NumPrevEventSchedCass = DCount(AllPrevEventSchedCass, @VM)
PrevEventEndCassNo = AllPrevEventSchedCass<0, NumPrevEventSchedCass>
If EpiPro then
PrevSchedWfrQty = Schedule_Services('GetScheduledWfrQty', WONo, PrevEventOrigStopDTM)
PrevEventEndSlotNo = Mod(PrevSchedWfrQty, 25)
If PrevEventEndSlotNo EQ 0 then PrevEventEndSlotNo = 25
WMIKey = PrevEventWONo:'*1*':PrevEventEndCassNo
WMIRdsList = Xlate('WM_IN', WMIKey, 'RDS_NO', 'X')
RDSNo = WMIRdsList<0, PrevEventEndSlotNo>
end else
WOMatKey = PrevEventWONo:'*':PrevEventEndCassNo
RDSNo = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
end
If RDSNo NE '' then
DtmOut = Xlate('RDS', RDSNo, 'DATETIME_OUT', 'X')
If ( (DtmOut NE '') and (DtmOut LT PrevEventOrigStopDtm) ) then
PrevEventStopDTM = DtmOut
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2.2'
LogData<3> = 'Previous event stop dtm set to last RDS unload dtm: ':OConv(DtmOut, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
end else
If PrevEventOrigStopDtm LT EventStartDtm then
PrevEventStopDtm = PrevEventOrigStopDtm
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2.2'
LogData<3> = 'Previous event stop dtm less than current event start dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Preserving previous event stop dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
end else
PrevEventStopDtm = EventStartDtm
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2.2'
LogData<3> = 'Previous event stop dtm greater than current event start dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting previous event stop dtm to current event start dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
end
end else
If PrevEventOrigStopDtm LT EventStartDtm then
PrevEventStopDtm = PrevEventOrigStopDtm
end else
PrevEventStopDtm = EventStartDtm
end
end
end else
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2.2'
LogData<3> = 'Previous event is a block event. '
Schedule_Services('LogActivity', ReactNo, LogData)
If PrevEventOrigStopDtm GT EventStartDtm then
PrevEventStopDtm = EventStartDTM
LogData<3> = 'Previous event stop dtm greater than current event start dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting previous event stop dtm to current event start dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
end else
PrevEventStopDtm = PrevEventOrigStopDtm
LogData<3> = 'Previous event stop dtm less than current event start dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Preserving previous event stop dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
Schedule_Services('LogActivity', ReactNo, LogData)
end
PrevEventRec<SCHED_DET_NG.STOP_DTM$> = PrevEventStopDtm
// Check if events will overlap
If PrevEventStopDtm GT EventStartDtm then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2.3'
LogData<3> = 'Calculated previous event stop dtm greater than current event start dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting current event start dtm to previous event stop dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
EventStartDtm = PrevEventStopDtm
end
If PrevEventRec NE PrevEventRecOrig then
Database_Services('WriteDataRow', 'SCHED_DET_NG', PrevEventKey, PrevEventRec, True$, False$, True$)
end
end
end
EventStopDTM = CurrDTM + EventAdjustment
end else
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2'
LogData<3> = 'Event ':EventKey:' not engaged. '
Schedule_Services('LogActivity', ReactNo, LogData)
// Work order event not engaged, so set start DTM to previous event's stop DTM.
PrevEventKey = ''
EventIsHardBlock = False$
Locate EventKey in OrigReactEventList using @VM setting OrigEventIndex then
If OrigEventIndex GT 1 then
SearchEventIndex = OrigEventIndex - 1
Loop
PrevEventKey = OrigReactEventList<0, SearchEventIndex>
EventExists = RowExists('SCHED_DET_NG', PrevEventKey)
If EventExists then EventIsHardBlock = Xlate('SCHED_DET_NG', PrevEventKey, 'HARD_BLOCK', 'X')
SearchEventIndex -= 1
Until ( ( (EventExists EQ True$) and (EventIsHardBlock NE True$) ) or (SearchEventIndex EQ 1) )
Repeat
end else
PrevEventKey = ''
end
end
If PrevEventKey NE '' then
PrevEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', PrevEventKey)
PrevEventStopDTM = PrevEventRec<SCHED_DET_NG.STOP_DTM$>
PrevEventComp = PrevEventRec<SCHED_DET_NG.EVENT_COMP$>
If ( (PrevEventComp NE True$) and (PrevEventStopDTM GT CurrDTM) ) then
EventStartDTM = PrevEventStopDTM
LogData<3> = 'Previous event not complete.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting start dtm to to previous event stop dtm: ':OConv(PrevEventStopDTM, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
end else
EventStartDTM = CurrDTM
LogData<3> = 'Previous event complete.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting start dtm to to current dtm: ':OConv(CurrDTM, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
end
end else
EventStartDTM = CurrDTM
LogData<3> = 'No previous event found.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting start dtm to to current dtm: ':OConv(CurrDTM, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
end
// Set stop DTM to start DTM + event adjustment
EventStopDTM = EventStartDTM + EventAdjustment
LogData<3> = 'New event stop dtm: ':OConv(EventStopDTM, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
end
// Check if event is complete (i.e. no more cassettes to process).
// If event is complete, then set stop DTM to last processed cassette's datetime
// out. Otherwise, adjust stop DTM to current DTM + event adjustment.
If EventComp then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.3'
LogData<3> = 'Event ':EventKey:' complete. '
Schedule_Services('LogActivity', ReactNo, LogData)
// Unprocessed cassette list must be empty to get here.
EventRec<SCHED_DET_NG.EVENT_COMP$> = True$
EventWONo = EventRec<SCHED_DET_NG.WO_NO$>
EventProcCass = EventRec<SCHED_DET_NG.PROCESSED_CASS$>
NumEventProcCass = DCount(EventProcCass, @VM)
EventEndCassNo = EventProcCass<0, NumEventProcCass>
If EpiPro then
SchedWfrQty = Schedule_Services('GetScheduledWfrQty', WONo, EventStopDtm)
EventEndSlotNo = Mod(SchedWfrQty, 25)
If EventEndSlotNo EQ 0 then EventEndSlotNo = 25
WMIKey = EventWONo:'*1*':EventEndCassNo
WMIRdsList = Xlate('WM_IN', WMIKey, 'RDS_NO', 'X')
RDSNo = WMIRdsList<0, EventEndSlotNo>
end else
WOMatKey = EventWONo:'*':EventEndCassNo
RDSNo = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
end
If RDSNo NE '' then
DtmOut = Xlate('RDS', RDSNo, 'DATETIME_OUT', 'X')
If DtmOut NE '' then
EventStopDTM = DtmOut
LogData<3> = 'Setting event stop dtm to last unloaded RDS ':OConv(DtmOut, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
end else
// Failsafe
EventStopDtm = CurrDtm
LogData<3> = 'Last RDS does not unloaded.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting stop dtm to current dtm ':OConv(CurrDtm, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
end
end else
// Failsafe
EventStopDtm = CurrDtm
LogData<3> = 'Error! Null last RDS.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Setting stop dtm to current dtm ':OConv(CurrDtm, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
end
end
EventRec<SCHED_DET_NG.START_DTM$> = EventStartDTM
EventRec<SCHED_DET_NG.STOP_DTM$> = EventStopDTM
Database_Services('WriteDataRow', 'SCHED_DET_NG', EventKey, EventRec, True$, False$, True$)
end else
// Soft block
// Adjust start and stop DTMs so that the soft block immediately follows the previous event
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.1'
LogData<3> = 'Original event details:'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event work order: ':EventRec<SCHED_DET_NG.WO_NO$>
Schedule_Services('LogActivity', ReactNo, LogData)
Logdata<3> = 'Event start dtm: ':OConv(EventRec<SCHED_DET_NG.START_DTM$>, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event stop dtm: ':OConv(EventRec<SCHED_DET_NG.STOP_DTM$>, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event quantity: ':EventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event complete: ':OConv(EventRec<SCHED_DET_NG.EVENT_COMP$>, "Btrue,false")
Schedule_Services('LogActivity', ReactNo, LogData)
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.2'
LogData<3> = 'Adjusting soft block event.'
Schedule_Services('LogActivity', ReactNo, LogData)
CurrNumEvents = DCount(NewReactEventList, @VM)
PrevEventKey = NewReactEventList<0, CurrNumEvents>
EventStartDTM = EventRec<SCHED_DET_NG.START_DTM$>
EventStopDTM = EventRec<SCHED_DET_NG.STOP_DTM$>
EventDuration = EventStopDTM - EventStartDTM
If PrevEventKey NE '' then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.3'
LogData<3> = 'Previous event found: ':PrevEventKey:' '
Schedule_Services('LogActivity', ReactNo, LogData)
PrevEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', PrevEventKey)
PrevEventStopDTM = PrevEventRec<SCHED_DET_NG.STOP_DTM$>
EventStartDTM = PrevEventStopDTM
EventStopDTM = EventStartDTM + EventDuration
EventRec<SCHED_DET_NG.START_DTM$> = EventStartDTM
EventRec<SCHED_DET_NG.STOP_DTM$> = EventStopDTM
Database_Services('WriteDataRow', 'SCHED_DET_NG', EventKey, EventRec, True$, False$, True$)
LogData<3> = 'Setting soft block start dtm to previous event stop dtm.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Adjusting soft block stop dtm by original event duration.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
end
// Add event into new reactor event list
NewReactEventList<0, -1> = EventKey
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '4.':vPos:'.4'
LogData<3> = 'Adjusted event details:'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event work order: ':EventRec<SCHED_DET_NG.WO_NO$>
Schedule_Services('LogActivity', ReactNo, LogData)
Logdata<3> = 'Event start dtm: ':OConv(EventRec<SCHED_DET_NG.START_DTM$>, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event stop dtm: ':OConv(EventRec<SCHED_DET_NG.STOP_DTM$>, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event quantity: ':EventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event complete: ':OConv(EventRec<SCHED_DET_NG.EVENT_COMP$>, "Btrue,false")
Schedule_Services('LogActivity', ReactNo, LogData)
Next EventKey
end
If HBList NE '' then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5'
LogData<3> = 'Hard block event(s) found.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Splitting events and inserting as needed.'
Schedule_Services('LogActivity', ReactNo, LogData)
// Insert all hard block events.
// - Find insert position in list.
// - Scan the reactor event list from end to beginning
// - If not a block event then
// - Split work order event
// - Calculate the number of wafers (round down for whole cassettes) that can be run in
// the time window (between event start time and hard block start).
// - Calculate event stop time
// - Insert hard block into list
// - Calculate remaining number of wafers (cassettes)
// - Create a new event with remaining number of wafers (start time will be hard block stop time)
// - Insert new event after hard block
// - Adjust downstream events
For each HBEventKey in HBList using @VM setting vPos
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.1'
LogData<3> = 'Rescheduling hard block event ':HBEventKey:'.'
Schedule_Services('LogActivity', ReactNo, LogData)
HBInserted = False$
HBEventRec = Schedule_Services('GetScheduleDetail', HBEventKey)
HBEventStartDTM = HBEventRec<SCHED_DET_NG.START_DTM$>
HBEventStopDTM = HBEventRec<SCHED_DET_NG.STOP_DTM$>
// Find position in event list to insert hard block and determine if we need to split an event
// (e.g. if there is an event occuring in the hard block time slot).
NumSchedEvents = DCount(NewReactEventList, @VM)
If CurrEventKey NE '' then
Locate CurrEventKey in NewReactEventList using @VM setting StartEventIndex then
// Traverse forwards
StepInc = 1
end else
// Traverse backwards
StartEventIndex = NumSchedEvents
StepInc = -1
end
end else
// Traverse backwards
StartEventIndex = NumSchedEvents
StepInc = -1
end
EventIndex = StartEventIndex
Loop
ThisEventKey = NewReactEventList<0, EventIndex>
NextEventKey = NewReactEventList<0, EventIndex + StepInc>
If ThisEventKey NE '' then
ThisEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', ThisEventKey)
ThisEventWONo = ThisEventRec<SCHED_DET_NG.WO_NO$>
ReactType = Xlate('WO_LOG', ThisEventWONo, 'REACT_TYPE', 'X')
ThisEventStartDTM = ThisEventRec<SCHED_DET_NG.START_DTM$>
ThisEventStopDTM = ThisEventRec<SCHED_DET_NG.STOP_DTM$>
ThisEventTotalWfrQty = ThisEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
If NextEventKey NE '' then
NextEventStartDtm = Xlate('SCHED_DET_NG', NextEventKey, 'START_DTM', 'X')
end else
NextEventStartDtm = ''
end
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2'
LogData<3> = 'Checking for insertion point with event ':ThisEventKey:'.'
Schedule_Services('LogActivity', ReactNo, LogData)
If ( (ThisEventStartDTM LE HBEventStartDTM) and ( (NextEventStartDtm GT HBEventStartDtm) or (NextEventKey EQ '') ) ) then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.1'
LogData<3> = 'Found insertion point on event ':ThisEventKey:'.'
Schedule_Services('LogActivity', ReactNo, LogData)
HBInserted = True$
// Found insertion position
// Need to determine if we need to split
// Check if event is a block event
BlockEvent = ThisEventRec<SCHED_DET_NG.BLOCK_OUT$>
TimeConflict = (ThisEventStopDTM GT HBEventStartDTM)
If ( (BlockEvent NE True$) and (TimeConflict EQ True$) ) then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.2'
LogData<3> = 'Confirmed event ':ThisEventKey:' is not a block event and there is time conflict.'
Schedule_Services('LogActivity', ReactNo, LogData)
FirstEventProcCassList = ''
FirstEventUnprocCassList = ''
FirstLoadedRDS = ''
LastLoadedRDS = ''
LastUnloadedRDS = ''
Query = "SELECT RDS WITH WO EQ ":ThisEventWONo:" AND WITH DATETIME_IN GE '"
Query := OConv(ThisEventStartDTM, 'DT/^S'):"' AND WITH DATETIME_IN LE '"
Query := OConv(HBEventStartDTM, 'DT/^S'):"' BY DATETIME_IN"
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.3'
LogData<3> = 'Selecting RDS records to associate with first split event.'
Schedule_Services('LogActivity', ReactNo, LogData)
RList(Query, TARGET_ACTIVELIST$, '', '', '')
If Not(Get_Status(ErrCode)) then
RDSKeys = ''
EOF = False$
Loop
ReadNext RDSKey else EOF = True$
Until EOF
RDSKeys<0, -1> = RDSKey
Repeat
If RDSKeys NE '' then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.4'
LogData<3> = 'RDS keys found. Building unprocessed and processed cassette lists.'
Schedule_Services('LogActivity', ReactNo, LogData)
For each RDSKey in RDSKeys using @VM
If FirstLoadedRDS EQ '' then FirstLoadedRDS = RDSKey
DtmOut = Xlate('RDS', RDSKey, 'DATETIME_OUT', 'X')
If ReactType EQ 'EPP' then
WMIKeys = Xlate('RDS', RDSKey, 'WM_IN_KEY', 'X')
For each WMIKey in WMIKeys using @VM
CassNo = Field(WMIKey, '*', 3)
If DtmOut NE '' then
LastUnloadedRDS = RDSKey
Locate CassNo in FirstEventProcCassList using @VM setting Dummy else
FirstEventProcCassList<0, -1> = CassNo
end
end else
LastLoadedRDS = RDSKey
Locate CassNo in FirstEventUnprocCassList using @VM setting Dummy else
FirstEventUnprocCassList<0, -1> = CassNo
end
end
Next WMIKey
end else
CassNo = Xlate('RDS', RDSKey, 'CASS_NO', 'X')
If DtmOut NE '' then
LastUnloadedRDS = RDSKey
FirstEventProcCassList<0, -1> = CassNo
end else
LastLoadedRDS = RDSKey
FirstEventUnprocCassList<0, -1> = CassNo
end
end
Next RDSKey
// Cleanup EpiPro cassette lists.
FirstEventUnprocCassList = SRP_Array('Join', FirstEventUnprocCassList, FirstEventProcCassList, 'NOT', @VM)
end else
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.4'
LogData<3> = 'No RDS keys found.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
end
FirstEventProcCassCount = DCount(FirstEventProcCassList, @VM)
FirstEventUnprocCassCount = DCount(FirstEventUnprocCassList, @VM)
FirstEventRemCassCount = 0
If HBEventStartDTM GT CurrDtm then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.4.1'
LogData<3> = 'Hard block event not engaged yet.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Estimating the number of cassettes that can be run before the hard block.'
Schedule_Services('LogActivity', ReactNo, LogData)
TimeWindow = HBEventStartDTM - CurrDTM
// Calculate number of whole cassettes we can run in this window
FirstEventRemWfrEstimate = Schedule_Services('GetWaferEstimate', ThisEventWONo, TimeWindow)
FirstEventRemCassCount += SRP_Math('FLOOR', (FirstEventRemWfrEstimate / 25) )
FirstEventUnprocCassCount += FirstEventRemCassCount
end
// If it is estimated that additional cassettes can run before the hard block event,
// then we'll work with cassette quantities of 25. If it is estimated that no addtional
// whole cassettes can run before the hard block, then determine the exact event wafer
// quantity to ensure the event begins and ends on an RDS and not on a whole cassette.
If FirstEventRemCassCount GT 0 then
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.5'
LogData<3> = 'At least one full cassette can run, so estimating stop dtm of event.'
Schedule_Services('LogActivity', ReactNo, LogData)
FirstEventUnprocWfrQty = FirstEventUnprocCassCount * 25
// Determine stop DTM of first split event
EventRuntime = Schedule_Services('GetEventDuration', ThisEventWONo, FirstEventUnprocWfrQty)
ThisEventStopDTM = CurrDTM + EventRuntime
If ThisEventStopDTM GT HBEventStartDtm then
ThisEventStopDtm = HBEventStartDtm
end
// Set new wafer qty for first split
FirstEventProcWfrQty = FirstEventProcCassCount * 25
FirstEventWfrQty = FirstEventUnprocWfrQty + FirstEventProcWfrQty
FirstEventCassCount = FirstEventWfrQty / 25
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.5.1'
LogData<3> = 'First split event wafer quantity ':FirstEventWfrQty
Schedule_Services('LogActivity', ReactNo, LogData)
// Determine unprocessed cass list
If FirstEventProcCassCount GT 0 then
LastProcCassNo = FirstEventProcCassList<0, FirstEventProcCassCount>
end else
LastProcCassNo = 0
end
FirstEventUnprocCassList = ''
For CassIndex = 1 to FirstEventUnprocCassCount
FirstEventUnprocCassList<0, -1> = (LastProcCassNo + CassIndex)
Next CassIndex
end else
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.5'
LogData<3> = 'It is estimated that no additional cassettes can be processed in the time left before the hard block event.'
Schedule_Services('LogActivity', ReactNo, LogData)
// It is estimated that no additional cassettes can be processed in the time left before the hard block event.
If FirstEventUnprocCassList NE '' then
ThisEventStopDTM = HBEventStartDTM
If ReactType EQ 'EPP' then
// Find slot event starts on
FirstLoadedWMIs = Xlate('RDS', FirstLoadedRDS, 'WM_IN_KEY', 'X')
FirstLoadedWMI = FirstLoadedWMIs[1, 'F':@VM]
FirstLoadedWMIRdsList = Xlate('WM_IN', FirstLoadedWMI, 'RDS_NO', 'X')
Done = False$
For SlotIndex = 1 to 25 Step 1
If FirstLoadedWMIRdsList<0, SlotIndex> EQ FirstLoadedRDS then
StartSlot = SlotIndex
Done = True$
end
Until Done
Next StartSlot
// Find slot event stops on
LastLoadedWMIs = Xlate('RDS', LastLoadedRDS, 'WM_IN_KEY', 'X')
LastLoadedWMI = LastLoadedWMIs[-1, 'B':@VM]
LastLoadedWMIRdsList = Xlate('WM_IN', LastLoadedWMI, 'RDS_NO', 'X')
Done = False$
For SlotIndex = 25 to 1 Step -1
If LastLoadedWMIRdsList<0, SlotIndex> EQ LastLoadedRDS then
StopSlot = SlotIndex
Done = True$
end
Until Done
Next SlotIndex
FirstEventWfrQty = ( (FirstEventProcCassCount - 1) * 25 ) + (25 - StartSlot + 1) + ( (FirstEventUnprocCassCount - 1) * 25 ) + StopSlot
end else
FirstEventWfrQty = ( FirstEventProcCassCount * 25 ) + ( FirstEventUnprocCassCount * 25)
end
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.6'
LogData<3> = 'First split event has unprocessed cassettes left.'
Schedule_Services('LogActivity', ReactNo, LogData)
end else
// Get the last RDS that ran and use that as the end of this event.
LastUnloadDtm = Xlate('RDS', LastUnloadedRDS, 'DATETIME_OUT', 'X')
If LastUnloadDtm LT HBEventStartDtm then
ThisEventStopDtm = LastUnloadDtm
end else
ThisEventStopDTM = HBEventStartDTM
end
If ReactType EQ 'EPP' then
// Find slot event starts on
FirstLoadedWMIs = Xlate('RDS', FirstLoadedRDS, 'WM_IN_KEY', 'X')
FirstLoadedWMI = FirstLoadedWMIs[1, 'F':@VM]
FirstLoadedWMIRdsList = Xlate('WM_IN', FirstLoadedWMI, 'RDS_NO', 'X')
Done = False$
For SlotIndex = 1 to 25 Step 1
If FirstLoadedWMIRdsList<0, SlotIndex> EQ FirstLoadedRDS then
StartSlot = SlotIndex
Done = True$
end
Until Done
Next StartSlot
// Find slot event stops on
LastUnloadedWMIs = Xlate('RDS', LastUnloadedRDS, 'WM_IN_KEY', 'X')
LastUnloadedWMI = LastUnloadedWMIs[-1, 'B':@VM]
LastUnloadedWMIRdsList = Xlate('WM_IN', LastUnloadedWMI, 'RDS_NO', 'X')
Done = False$
For SlotIndex = 25 to 1 Step -1
If LastUnloadedWMIRdsList<0, SlotIndex> EQ LastUnloadedRDS then
StopSlot = SlotIndex
Done = True$
end
Until Done
Next SlotIndex
FirstEventWfrQty = ( (FirstEventProcCassCount - 2) * 25 ) + (25 - StartSlot + 1) + StopSlot
end else
FirstEventWfrQty = ( FirstEventProcCassCount * 25 )
end
ThisEventRec<SCHED_DET_NG.EVENT_COMP$> = True$
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.6'
LogData<3> = 'First split event no unprocessed cassettes left.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Marking event complete.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
end
// Update first split event
If FirstEventWfrQty GT 100 then
ThisEventRec<SCHED_DET_NG.PROCESSED_CASS$> = FirstEventProcCassList
ThisEventRec<SCHED_DET_NG.UNPROCESSED_CASS$> = FirstEventUnprocCassList
ThisEventRec<SCHED_DET_NG.STOP_DTM$> = ThisEventStopDTM
ThisEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$> = FirstEventWfrQty
Database_Services('WriteDataRow', 'SCHED_DET_NG', ThisEventKey, ThisEventRec, True$, False$, True$)
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.7'
LogData<3> = 'First split event updated. Wafer quantity: ':FirstEventWfrQty:'. Stop dtm ':ThisEventStopDtm
Schedule_Services('LogActivity', ReactNo, LogData)
end else
// Event is too small to schedule. Move the wafers to the second split event.
FirstEventWfrQty = 0
Database_Services('DeleteDataRow', 'SCHED_DET_NG', ThisEventKey, True$, False$)
NewReactEventList = Delete(NewReactEventList, 0, EventIndex, 0)
EventIndex -= 1
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.7'
LogData<3> = 'First split event wafer quantity too small.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Deleting event and moving all wafers to second split event.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
// Insert hard block into reactor event list after first split
HBEventIndex = EventIndex + 1
NewReactEventList = Insert(NewReactEventList, 0, HBEventIndex, 0, HBEventKey)
// Calculate second split event quantity
SecondEventWfrQty = ThisEventTotalWfrQty - FirstEventWfrQty
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.8'
LogData<3> = 'Determining if second split event is necessary.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Second split event wafer quantity ':SecondEventWfrQty:'.'
Schedule_Services('LogActivity', ReactNo, LogData)
If SecondEventWfrQty GT 0 then
SecondEventUnprocCassList = ''
SecondEventProcCassList = ''
// Look at the next slot to be scheduled!
// Get the number of wafers scheduled up to this event.
// This should give us the correct starting wafer slot.
PrevSchedWfrQty = Schedule_Services('GetScheduledWfrQty', ThisEventWONo, HBEventStopDTM)
StartCassNo = SRP_Math('CEILING', ( (PrevSchedWfrQty + 1) / 25))
If StartCassNo EQ 0 then StartCassNo = 1
SecondEventNumCass = SRP_Math('CEILING', (SecondEventWfrQty / 25))
FirstRDS = ''
If ReactType EQ 'EPP' then
// Example: Event starts on WM_IN cassette 6 (i.e. 172125*1*6), slot 13.
StartSlotNo = Mod(PrevSchedWfrQty, 25) + 1
StopCassNo = StartCassNo + SecondEventNumCass - 1
StopSlotNo = Mod((PrevSchedWfrQty + SecondEventWfrQty), 25)
If StopSlotNo EQ 0 then StopSlotNo = 25
For CassIndex = 1 to SecondEventNumCass
CassNo = (StartCassNo + CassIndex - 1)
WMIKey = ThisEventWONo:'*1*':CassNo
WMIRdsKeys = Xlate('WM_IN', WMIKey, 'RDS_NO', 'X')
EventCassRDSKeys = ''
Begin Case
Case CassNo EQ StartCassNo
StartIndex = StartSlotNo
StopIndex = 25
Case CassNo EQ StopCassNo
StartIndex = 1
StopIndex = StopSlotNo
Case ( (CassNo EQ StartCassNo) and (CassNo EQ StopCassNo) )
StartIndex = StartSlotNo
StopIndex = StopSlotNo
Case Otherwise$
StartIndex = 1
StopIndex = 25
End Case
For SlotIndex = StartIndex to StopIndex
If FirstRDS EQ '' then FirstRDS = WMIRdsKeys<0, SlotIndex>
EventCassRDSKeys<0, -1> = WMIRdsKeys<0, SlotIndex>
Next SlotIndex
EventCassRDSKeys = SRP_Array('Clean', EventCassRDSKeys, 'TrimAndMakeUnique', @VM)
// Now we have a list of RDS keys that only pertain to this event.
If EventCassRDSKeys NE '' then
// Are all of the RDS runs unloaded?
NumUnloadedRDS = 0
UnloadDtms = Xlate('RDS', EventCassRDSKeys, 'DATETIME_OUT', 'X')
If UnloadDtms NE '' then
For each UnloadDtm in UnloadDtms using @VM
If UnloadDtm NE '' then NumUnloadedRds += 1
Next UnloadDtm
end
NumRDS = DCount(EventCassRDSKeys, @VM)
CassProcessed = (NumRds EQ NumUnloadedRds)
end else
CassProcessed = False$
end
If CassProcessed then
SecondEventProcCassList<0, -1> = CassNo
end else
SecondEventUnprocCassList<0, -1> = CassNo
end
Next CassIndex
SecondEventProcCassList = SRP_Array('Join', SecondEventProcCassList, SecondEventUnprocCassList, 'NOT', @VM)
end else
// Example: Event starts on WO_MAT cassette 7. We don't need to schedule down to the
// wafer levels with non-epipro work order events.
For CassIndex = 1 to SecondEventNumCass
CassNo = (StartCassNo + CassIndex - 1)
WOMatKey = ThisEventWONo:'*':CassNo
RDSNo = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
If FirstRDS EQ '' then FirstRDS = RdsNo
UnloadDtm = Xlate('RDS', RDSNo, 'DATETIME_OUT', 'X')
CassProcessed = (UnloadDtm NE '')
If CassProcessed then
SecondEventProcCassList<0, -1> = CassNo
end else
SecondEventUnprocCassList<0, -1> = CassNo
end
Next CassIndex
end
SecondEventStartDtm = HBEventStopDtm
If FirstRDS NE '' then
FirstLoadDtm = Xlate('RDS', FirstRDS, 'DATETIME_IN', 'X')
If FirstLoadDtm GT HBEventStopDTM then
SecondEventStartDtm = FirstLoadDtm
end
end
SecondEventKey = RTI_CreateGUID()
SecondEventRec = ''
NewEventDuration = Schedule_Services('GetEventDuration', ThisEventWONo, SecondEventWfrQty)
SecondEventRec<SCHED_DET_NG.REACT_NO$> = ThisEventRec<SCHED_DET_NG.REACT_NO$>
SecondEventRec<SCHED_DET_NG.WO_NO$> = ThisEventRec<SCHED_DET_NG.WO_NO$>
SecondEventRec<SCHED_DET_NG.START_DTM$> = SecondEventStartDTM
SecondEventRec<SCHED_DET_NG.STOP_DTM$> = SecondEventStartDTM + NewEventDuration
SecondEventRec<SCHED_DET_NG.DESC$> = ThisEventRec<SCHED_DET_NG.DESC$>
SecondEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$> = SecondEventWfrQty
SecondEventRec<SCHED_DET_NG.PROCESSED_CASS$> = SecondEventProcCassList
SecondEventRec<SCHED_DET_NG.UNPROCESSED_CASS$> = SecondEventUnprocCassList
Database_Services('WriteDataRow', 'SCHED_DET_NG', SecondEventKey, SecondEventRec)
// Add second split event to event list
NewEventIndex = EventIndex + 2
NewReactEventList = Insert(NewReactEventList, 0, NewEventIndex, 0, SecondEventKey)
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.9'
LogData<3> = 'Second split event details:'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event work order: ':SecondEventRec<SCHED_DET_NG.WO_NO$>
Schedule_Services('LogActivity', ReactNo, LogData)
Logdata<3> = 'Event start dtm: ':OConv(SecondEventRec<SCHED_DET_NG.START_DTM$>, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event stop dtm: ':OConv(SecondEventRec<SCHED_DET_NG.STOP_DTM$>, 'DT2/^H')
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event quantity: ':SecondEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Event complete: ':OConv(SecondEventRec<SCHED_DET_NG.EVENT_COMP$>, "Btrue,false")
Schedule_Services('LogActivity', ReactNo, LogData)
end
// Adjust downstream events
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '6.':vPos
LogData<3> = 'Begin adjusting downstream events.'
Schedule_Services('LogActivity', ReactNo, LogData)
NumSchedEvents = DCount(NewReactEventList, @VM)
For AdjustmentIndex = (EventIndex + 3) to NumSchedEvents
AdjustEventKey = NewReactEventList<0, AdjustmentIndex>
AdjustEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', AdjustEventKey)
AdjustEventWONo = AdjustEventRec<SCHED_DET_NG.WO_NO$>
AdjustEventWfrQty = AdjustEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
PrevEventKey = NewReactEventList<0, AdjustmentIndex - 1>
PrevEventStopDTM = Xlate('SCHED_DET_NG', PrevEventKey, 'STOP_DTM', 'X')
// Set new start and stop DTMs
AdjustEventStartDTM = PrevEventStopDTM
AdjustEventDuration = Schedule_Services('GetEventDuration', AdjustEventWONo, AdjustEventWfrQty)
AdjustEventStopDTM = AdjustEventStartDTM + AdjustEventDuration
AdjustEventRec<SCHED_DET_NG.START_DTM$> = AdjustEventStartDTM
AdjustEventRec<SCHED_DET_NG.STOP_DTM$> = AdjustEventStopDTM
Database_Services('WriteDataRow', 'SCHED_DET_NG', AdjustEventKey, AdjustEventRec, True$, False$, True$)
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '6.':vPos:'.':AdjustmentIndex
LogData<3> = 'Event ':AdjustEventKey:' adjusted.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'New start dtm ':OConv(AdjustEventStartDtm, 'DT2/^H'):'.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'New stop dtm ':OConv(AdjustEventStopDtm, 'DT2/^H'):'.'
Schedule_Services('LogActivity', ReactNo, LogData)
Next AdjustmentIndex
end else
// No time conflict, so just insert hard block event
HBEventIndex = EventIndex + 1
NewReactEventList = Insert(NewReactEventList, 0, HBEventIndex, 0, HBEventKey)
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5.':vPos:'.2.2'
LogData<3> = 'No time conflict found between event ':ThisEventKey:' and ':HBEventKey:'.'
Schedule_Services('LogActivity', ReactNo, LogData)
LogData<3> = 'Inserting hard block without splitting.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
end
end
EventIndex += StepInc
Until ( HBInserted or (EventIndex LT 0) or (EventIndex GT NumSchedEvents) )
Repeat
Next HBEventKey
end else
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '5'
LogData<3> = 'No hard block events to reschedule.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
end
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '6'
LogData<3> = 'Begin cleaning out "zombie events".'
Schedule_Services('LogActivity', ReactNo, LogData)
NumEvents = DCount(NewReactEventList, @VM)
For EventIndex = 1 to NumEvents
EventID = NewReactEventList<0, EventIndex>
EventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', EventID)
EventWONo = EventRec<SCHED_DET_NG.WO_NO$>
EventBlockOut = EventRec<SCHED_DET_NG.BLOCK_OUT$>
If ( (EventWONo EQ '') and (EventBlockOut NE True$) ) then
// This is a zombie, so delete it (there's a bug up above somewhere creating or not
// deleting events as necessary.
NewReactEventList = Delete(NewReactEventList, 0, EventIndex, 0)
Database_Services('DeleteDataRow', 'SCHED_DET_NG', EventID, True$, False$)
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '6.':EventIndex
LogData<3> = 'Zombie event ':EventID:' found and deleted.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
Next EventIndex
ReactNo = OrigReactNo
// Update reactor event list
If ( (NewReactEventList NE '') and (NewReactEventList NE OrigReactEventList) ) then
ReactRec = Database_Services('ReadDataRow', 'REACTOR', ReactNo)
// Clean out duplicate event keys (there's a bug up above somewhere creating them)
NewReactEventList = SRP_Array('Clean', NewReactEventList, 'TrimAndMakeUnique', @VM)
ReactRec<REACTOR_SCHED_EVENTS$> = NewReactEventList
Database_Services('WriteDataRow', 'REACTOR', ReactNo, ReactRec, True$, False$, True$)
LogData = ''
LogData<1> = OConv(Datetime(), 'DT2/^H')
LogData<2> = '7'
LogData<3> = 'Writing new event list for reactor ':ReactNo:'.'
Schedule_Services('LogActivity', ReactNo, LogData)
end
Schedule_Services('SendRefreshMessage')
Unlock hSysLists, ServiceKeyID:'*':ReactNo else Null
end
end service
//----------------------------------------------------------------------------------------------------------------------
// AddSchedEvent
//
// Saves the event to the database.
//----------------------------------------------------------------------------------------------------------------------
Service AddSchedEvent(ReactorNo, WorkOrderNo, StartDTM, StopDTM, Description, WaferQty)
// Create a unique key ID for the new event
NewEventKeyID = RTI_CreateGUID()
NewEventRec = ''
NewEventRec<SCHED_DET_NG.REACT_NO$> = ReactorNo
NewEventRec<SCHED_DET_NG.WO_NO$> = WorkOrderNo
NewEventRec<SCHED_DET_NG.START_DTM$> = StartDTM
NewEventRec<SCHED_DET_NG.DESC$> = Description
NewEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$> = WaferQty
EpiPro = (Xlate('WO_LOG', WorkOrderNo, 'REACT_TYPE', 'X') EQ 'EPP')
CurrDTM = Datetime()
EventProcCassList = ''
EventUnprocCassList = ''
LastLoadedRDS = ''
LastUnloadedRDS = ''
// Determine start cassette and slot number
PrevSchedQty = Schedule_Services('GetScheduledWfrQty', WorkOrderNo)
StartCass = SRP_Math('CEILING', ( (PrevSchedQty + 1) / 25))
If StartCass EQ 0 then StartCass = 1
StartSlot = Mod(PrevSchedQty, 25) + 1
TotalSchedQty = PrevSchedQty + WaferQty
StopCass = SRP_Math('CEILING', ( TotalSchedQty / 25 ) )
StopSlot = Mod(TotalSchedQty, 25)
If StopSlot EQ 0 then StopSlot = 25
// Get list of RDS keys associated with this event if they exist
RDSKeys = ''
If EpiPro then
For CassNo = StartCass to StopCass
WMIKey = WorkOrderNo:'*1*':CassNo
WMIRdsList = Xlate('WM_IN', WMIKey, 'RDS_NO', 'X')
If WMIRdsList NE '' then
Begin Case
Case ( (CassNo EQ StartCass) and (CassNo EQ StopCass) )
StartIndex = StartSlot
StopIndex = StopSlot
Case CassNo EQ StartCass
StartIndex = StartSlot
StopIndex = 25
Case CassNo EQ StopCass
StartIndex = 1
StopIndex = StopSlot
Case Otherwise$
StartIndex = 1
StopIndex = 25
End Case
For SlotIndex = StartIndex to StopIndex
RDSKeys<0, -1> = WMIRdsList<0, SlotIndex>
Next SlotIndex
end else
// No RDS keys exist for this cassette yet, so it's unprocessed
EventUnprocCassList<0, -1> = CassNo
end
Next CassNo
If RDSKeys NE '' then
RDSKeys = SRP_Array('Clean', RDSKeys, 'TrimAndMakeUnique', @VM)
For each RDSKey in RDSKeys using @VM setting vPos
DtmOut = Xlate('RDS', RDSKey, 'DATETIME_OUT', 'X')
WMIKeys = Xlate('RDS', RDSKey, 'WM_IN_KEY', 'X')
For each WMIKey in WMIKeys using @VM
CassNo = Field(WMIKey, '*', 3)
If DtmOut NE '' then
LastUnloadedRDS = RDSKey
Locate CassNo in EventProcCassList using @VM setting Dummy else
EventProcCassList<0, -1> = CassNo
end
end else
Locate CassNo in EventUnprocCassList using @VM setting Dummy else
EventUnprocCassList<0, -1> = CassNo
end
end
Next WMIKey
Next RDSKey
end
EventUnprocCassList = SRP_Array('SortSimpleList', EventUnprocCassList, 'AscendingNumbers', @VM)
// This line ensures EpiPro cassettes are not recorded as both processed and unprocessed.
EventProcCassList = SRP_Array('Join', EventProcCassList, EventUnprocCassList, 'NOT', @VM)
end else
For CassNo = StartCass to StopCass
WOMatKey = WorkOrderNo:'*':CassNo
RDSKey = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
DtmOut = Xlate('RDS', RDSKey, 'DATETIME_OUT', 'X')
If DtmOut NE '' then
LastUnloadedRDS = RDSKey
Locate CassNo in EventProcCassList using @VM setting Dummy else
EventProcCassList<0, -1> = CassNo
end
end else
Locate CassNo in EventUnprocCassList using @VM setting Dummy else
EventUnprocCassList<0, -1> = CassNo
end
end
Next CassNo
end
EventComp = (EventUnprocCassList EQ '')
If EventComp then StopDtm = Xlate('RDS', LastUnloadedRDS, 'DATETIME_OUT', 'X')
// Update event
NewEventRec<SCHED_DET_NG.EVENT_COMP$> = EventComp
NewEventRec<SCHED_DET_NG.PROCESSED_CASS$> = EventProcCassList
NewEventRec<SCHED_DET_NG.UNPROCESSED_CASS$> = EventUnprocCassList
NewEventRec<SCHED_DET_NG.STOP_DTM$> = StopDtm
Database_Services('WriteDataRow', 'SCHED_DET_NG', NewEventKeyID, NewEventRec)
If Error_Services('NoError') then
// Determine where to insert the event in the REACTOR record linked list
// Add the event to the REACTOR record event list
ReactRec = Database_Services('ReadDataRow', 'REACTOR', ReactorNo)
ReactSchedEvents = ReactRec<REACTOR_SCHED_EVENTS$>
CurrNumEvents = DCount(ReactSchedEvents, @VM)
// Add our new event key ID to the reactor event list
NewNumEvents = CurrNumEvents + 1
InsertPos = ''
For EventIndex = 1 to CurrNumEvents Step 1
ThisEventKey = ReactSchedEvents<0, EventIndex>
ThisStartDTM = Xlate('SCHED_DET_NG', ThisEventKey, 'START_DTM', 'X')
IF ThisStartDTM GT StartDTM then
// We found our location
InsertPos = EventIndex
end
Until InsertPos NE ''
Next EventIndex
If InsertPos EQ '' then InsertPos = NewNumEvents
ReactSchedEvents = Insert(ReactSchedEvents, 0, InsertPos, 0, NewEventKeyID)
ReactRec<REACTOR_SCHED_EVENTS$> = ReactSchedEvents
Database_Services('WriteDataRow', 'REACTOR', ReactorNo, ReactRec)
// Check if there is an overlap with the previous event. If so, then set previous event's stop DTM to this event's
// start DTM.
If NewNumEvents GT 1 then
PrevEventKey = ReactSchedEvents<0, (NewNumEvents - 1)>
If PrevEventKey NE '' then
PrevEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', PrevEventKey)
PrevEventStopDTM = PrevEventRec<SCHED_DET_NG.STOP_DTM$>
If PrevEventStopDTM GT StartDTM then
PrevEventStopDTM = StartDTM
PrevEventRec<SCHED_DET_NG.STOP_DTM$> = PrevEventStopDTM
Database_Services('WriteDataRow', 'SCHED_DET_NG', PrevEventKey, PrevEventRec, True$, False$, True$)
end
end
end
// Record schedule change in SCHED_HIST table
Schedule_Services('AddSchedHist', NewEventKeyID, ReactorNo, 'ADD', @User4)
Response = NewEventKeyID
end
end service
Service AddSchedHist(SchedDetNGKey, ReactorNo, Action=ACTIONS, User)
Error = ''
If ( (SchedDetNGKey NE '') and (ReactorNo NE '') and (Action NE '') and (User NE '') ) then
SchedHistRec = ''
SchedHistRec<SCHED_HIST.SCHED_DET_NG_KEY$> = SchedDetNGKey
SchedHistRec<SCHED_HIST.REACTOR$> = ReactorNo
SchedHistRec<SCHED_HIST.ACTION$> = Action
SchedHistRec<SCHED_HIST.USER$> = User
SchedHistKey = NextKey('SCHED_HIST')
If SchedHistKey NE '' then
Database_Services('WriteDataRow', 'SCHED_HIST', SchedHistKey, SchedHistRec)
If Error_Services('HasError') then Error = Error_Services('GetMessage')
end else
Error = 'Error in ':Service:' service. Error retrieving new SCHED_HIST key.'
end
end else
Error = 'Error in ':Service:' service. Null parameter passed in.'
end
If Error NE '' then Error_Services('Add', Error)
end service
//----------------------------------------------------------------------------------------------------------------------
// ModifySchedEvent
//
// Modifies an event's details.
//----------------------------------------------------------------------------------------------------------------------
Service ModifySchedEvent(SchedDetNGKey, ReactorNo, WorkOrderNo, StartDTM, StopDTM, Description, WaferQty, AdjustNextEvent=BOOLEAN)
Response = False$
If SchedDetNGKey NE '' then
EventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetNGKey)
If Error_Services('NoError') then
If AdjustNextEvent EQ '' then AdjustNextEvent = True$
CurrDTM = Datetime()
If ReactorNo NE '' then EventRec<SCHED_DET_NG.REACT_NO$> = ReactorNo
If WorkOrderNo NE '' then EventRec<SCHED_DET_NG.WO_NO$> = WorkOrderNo
If StartDTM NE '' then EventRec<SCHED_DET_NG.START_DTM$> = StartDTM
If StopDTM NE '' then EventRec<SCHED_DET_NG.STOP_DTM$> = StopDTM
If Description NE '' then EventRec<SCHED_DET_NG.DESC$> = Description
OrigWfrQty = EventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
// If the user is just changing the description the wafer quantity may not be passed in.
If WaferQty EQ '' then WaferQty = OrigWfrQty
If OrigWfrQty NE WaferQty then
ReactorNo = EventRec<SCHED_DET_NG.REACT_NO$>
WorkOrderNo = EventRec<SCHED_DET_NG.WO_NO$>
StartDTM = EventRec<SCHED_DET_NG.START_DTM$>
StopDTM = EventRec<SCHED_DET_NG.STOP_DTM$>
EpiPro = ( Xlate('REACTOR', ReactorNo, 'REACT_TYPE', 'X') EQ 'EPP' )
EventProcCassList = ''
EventUnprocCassList = ''
LastLoadedRDS = ''
LastUnloadedRDS = ''
// Determine start cassette and slot number
PrevSchedQty = Schedule_Services('GetScheduledWfrQty', WorkOrderNo, StartDtm)
StartCass = SRP_Math('CEILING', ( (PrevSchedQty + 1) / 25))
If StartCass EQ 0 then StartCass = 1
StartSlot = Mod(PrevSchedQty, 25) + 1
TotalSchedQty = PrevSchedQty + WaferQty
StopCass = SRP_Math('CEILING', ( TotalSchedQty / 25 ) )
StopSlot = Mod(TotalSchedQty, 25)
If StopSlot EQ 0 then StopSlot = 25
// Get list of RDS keys associated with this event if they exist
RDSKeys = ''
If EpiPro then
For CassNo = StartCass to StopCass
WMIKey = WorkOrderNo:'*1*':CassNo
WMIRdsList = Xlate('WM_IN', WMIKey, 'RDS_NO', 'X')
If WMIRdsList NE '' then
Begin Case
Case ( (CassNo EQ StartCass) and (CassNo EQ StopCass) )
StartIndex = StartSlot
StopIndex = StopSlot
Case CassNo EQ StartCass
StartIndex = StartSlot
StopIndex = 25
Case CassNo EQ StopCass
StartIndex = 1
StopIndex = StopSlot
Case Otherwise$
StartIndex = 1
StopIndex = 25
End Case
For SlotIndex = StartIndex to StopIndex
RDSKeys<0, -1> = WMIRdsList<0, SlotIndex>
Next SlotIndex
end else
// No RDS keys exist for this cassette yet, so it's unprocessed
EventUnprocCassList<0, -1> = CassNo
end
Next CassNo
If RDSKeys NE '' then
RDSKeys = SRP_Array('Clean', RDSKeys, 'TrimAndMakeUnique', @VM)
For each RDSKey in RDSKeys using @VM setting vPos
DtmOut = Xlate('RDS', RDSKey, 'DATETIME_OUT', 'X')
WMIKeys = Xlate('RDS', RDSKey, 'WM_IN_KEY', 'X')
For each WMIKey in WMIKeys using @VM
CassNo = Field(WMIKey, '*', 3)
If DtmOut NE '' then
LastUnloadedRDS = RDSKey
Locate CassNo in EventProcCassList using @VM setting Dummy else
EventProcCassList<0, -1> = CassNo
end
end else
Locate CassNo in EventUnprocCassList using @VM setting Dummy else
EventUnprocCassList<0, -1> = CassNo
end
end
Next WMIKey
Next RDSKey
end
EventUnprocCassList = SRP_Array('SortSimpleList', EventUnprocCassList, 'AscendingNumbers', @VM)
// This line ensures EpiPro cassettes are not recorded as both processed and unprocessed.
EventProcCassList = SRP_Array('Join', EventProcCassList, EventUnprocCassList, 'NOT', @VM)
end else
For CassNo = StartCass to StopCass
WOMatKey = WorkOrderNo:'*':CassNo
RDSKey = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
DtmOut = Xlate('RDS', RDSKey, 'DATETIME_OUT', 'X')
If DtmOut NE '' then
LastUnloadedRDS = RDSKey
Locate CassNo in EventProcCassList using @VM setting Dummy else
EventProcCassList<0, -1> = CassNo
end
end else
Locate CassNo in EventUnprocCassList using @VM setting Dummy else
EventUnprocCassList<0, -1> = CassNo
end
end
Next CassNo
end
EventComp = (EventUnprocCassList EQ '')
If EventComp then StopDtm = Xlate('RDS', LastUnloadedRDS, 'DATETIME_OUT', 'X')
// Update event
EventRec<SCHED_DET_NG.EVENT_COMP$> = EventComp
EventRec<SCHED_DET_NG.PROCESSED_CASS$> = EventProcCassList
EventRec<SCHED_DET_NG.UNPROCESSED_CASS$> = EventUnprocCassList
EventRec<SCHED_DET_NG.STOP_DTM$> = StopDtm
EventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$> = WaferQty
Database_Services('WriteDataRow', 'SCHED_DET_NG', SchedDetNGKey, EventRec, True$, False$, True$)
If Error_Services('NoError') then
Schedule_Services('AddSchedHist', SchedDetNGKey, ReactorNo, 'MODIFY', @User4)
Response = True$
end
If AdjustNextEvent then
// Adjust downstream events if they share the same work order. If the above event increased its
// wafer quantity then the next event will be reduced by the delta and vice versa.
ModifyWfrQty = WaferQty - OrigWfrQty
Query = 'SELECT SCHED_DET_NG WITH WO_NO EQ ':WorkOrderNo:' AND WITH START_DTM GT '
Query := Quote(OConv(StartDtm, 'DT/^3H')):' BY START_DTM'
GoSub ClearCursors
Set_Status(0)
RList(Query, TARGET_ACTIVELIST$, '', '', '')
errCode = ''
Done = False$
DownStreamKey = ''
If Get_Status(errCode) then
ErrorMsg = 'Error retrieving event list from the ':Service:' module. Error code':errCode
Error_Services('Add', ErrorMsg)
end else
EOF = False$
ReadNext DownStreamKey else EOF = True$
If ( Not(EOF) and (DownStreamKey NE SchedDetNGKey) ) then
DownStreamRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', DownStreamKey)
OrigDownStreamQty = DownStreamRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
NewDownStreamQty = OrigDownStreamQty - ModifyWfrQty
Schedule_Services('ModifySchedEvent', DownStreamKey, '', '', '', '', '', NewDownStreamQty, False$)
end
GoSub ClearCursors
end
end
end else
// User is likely just adding a comment
Database_Services('WriteDataRow', 'SCHED_DET_NG', SchedDetNGKey, EventRec, True$, False$, True$)
If Error_Services('NoError') then
Schedule_Services('AddSchedHist', SchedDetNGKey, ReactorNo, 'MODIFY', @User4)
Response = True$
end
end
end
end
end service
Service GetBlockEvents(ReactorNo, StartDTM, StopDTM, BlockGroup=BLOCK_GROUP)
ScheduleDetailKeys = ''
If Not(Num(StartDTM)) then StartDTM = Iconv(StartDTM, 'DT')
If Not(Num(StopDTM)) then StopDTM = Iconv(StopDTM, 'DT')
GoSub ClearCursors
Set_Status(0)
Query = ''
If ReactorNo NE '' then
Query = 'SELECT SCHED_DET_NG WITH REACT_NO EQ ':ReactorNo
RList(Query, TARGET_ACTIVELIST$, '', '', '')
end
Begin Case
Case ((StartDTM NE '') and (StopDTM NE ''))
Query = 'SELECT SCHED_DET_NG WITH START_DTM BETWEEN ':Quote(OConv(StartDTM, 'DT/^3H')):' AND ':Quote(OConv(StopDTM, 'DT/^3H')):' OR '
Query := 'WITH STOP_DTM BETWEEN ':Quote(OConv(StartDTM, 'DT/^3H')):' AND ':Quote(OConv(StopDTM, 'DT/^3H'))
RList(Query, TARGET_ACTIVELIST$, '', '', '')
Case ((StartDTM NE '') and (StopDTM EQ ''))
Query = 'SELECT SCHED_DET_NG WITH START_DTM GE ':Quote(OConv(StartDTM, 'DT/^3H'))
RList(Query, TARGET_ACTIVELIST$, '', '', '')
Case ((StartDTM EQ '') and (StopDTM NE ''))
Query = 'SELECT SCHED_DET_NG WITH STOP_DTM LE ':Quote(OConv(StopDTM, 'DT/^3H'))
RList(Query, TARGET_ACTIVELIST$, '', '', '')
Case ((StartDTM EQ '') and (StopDTM EQ ''))
Null
End Case
Begin Case
Case BlockGroup _EQC 'SOFT_BLOCK'
Query = 'SELECT SCHED_DET_NG WITH SOFT_BLOCK EQ ':True$
RList(Query, TARGET_ACTIVELIST$, '', '', '')
Case BlockGroup _EQC 'HARD_BLOCK'
Query = 'SELECT SCHED_DET_NG WITH HARD_BLOCK EQ ':True$
RList(Query, TARGET_ACTIVELIST$, '', '', '')
Case Otherwise$
// Get all block events
Query = 'SELECT SCHED_DET_NG WITH BLOCK_OUT EQ ':True$
RList(Query, TARGET_ACTIVELIST$, '', '', '')
End Case
errCode = ''
If Get_Status(errCode) then
ErrorMsg = 'Error retrieving event list from the ':Service:' module. Error code':errCode
Error_Services('Add', ErrorMsg)
end else
EOF = False$
Loop
ReadNext SchedDetNGKey Else EOF = True$
Until EOF EQ True$
ScheduleDetailKeys<-1> = SchedDetNGKey
Repeat
end
GoSub ClearCursors
Response = ScheduleDetailKeys
end service
Service AddBlockOutEvent(ReactorNo, StartDTM, StopDTM, BlockType, Description, Options)
// Create a unique key ID for the new event
NewEventKeyID = RTI_CreateGUID()
NewEventRec = ''
NewEventRec<SCHED_DET_NG.REACT_NO$> = ReactorNo
NewEventRec<SCHED_DET_NG.START_DTM$> = StartDTM
NewEventRec<SCHED_DET_NG.STOP_DTM$> = StopDTM
NewEventRec<SCHED_DET_NG.DESC$> = Description
NewEventRec<SCHED_DET_NG.BLOCK_OUT$> = True$
NewEventRec<SCHED_DET_NG.BLOCK_OUT_TYPE$> = BlockType
// Determine where to insert the event in the REACTOR record linked list
// Add the event to the REACTOR record linked list
ReactRec = Database_Services('ReadDataRow', 'REACTOR', ReactorNo)
ReactSchedEvents = ReactRec<REACTOR_SCHED_EVENTS$>
CurrNumEvents = DCount(ReactSchedEvents, @VM)
ReactRec = Database_Services('ReadDataRow', 'REACTOR', ReactorNo)
ReactSchedEvents = ReactRec<REACTOR_SCHED_EVENTS$>
CurrNumEvents = DCount(ReactSchedEvents, @VM)
// Add our new event key ID to the reactor event list
NewNumEvents = CurrNumEvents + 1
InsertPos = ''
For EventIndex = 1 to CurrNumEvents Step 1
ThisEventKey = ReactSchedEvents<0, EventIndex>
ThisStartDTM = Xlate('SCHED_DET_NG', ThisEventKey, 'START_DTM', 'X')
IF ThisStartDTM GT StartDTM then
// We found our location
InsertPos = EventIndex
end
Until InsertPos NE ''
Next EventIndex
If InsertPos EQ '' then InsertPos = NewNumEvents
ReactSchedEvents = Insert(ReactSchedEvents, 0, InsertPos, 0, NewEventKeyID)
ReactRec<REACTOR_SCHED_EVENTS$> = ReactSchedEvents
Database_Services('WriteDataRow', 'REACTOR', ReactorNo, ReactRec)
Database_Services('WriteDataRow', 'SCHED_DET_NG', NewEventKeyID, NewEventRec)
// Record schedule change in SCHED_HIST table
Schedule_Services('AddSchedHist', NewEventKeyID, ReactorNo, 'ADD', @User4)
Response = NewEventKeyID
end service
//----------------------------------------------------------------------------------------------------------------------
// GetScheduleDetail
//
// Returns the SCHED_DET_NG row for the indicated Key ID.
//----------------------------------------------------------------------------------------------------------------------
Service GetScheduleDetail(ScheduleKeyID)
ScheduleDetail = ''
TableName = 'SCHED_DET_NG'
If ScheduleKeyID NE '' then
ScheduleDetail = Database_Services('ReadDataRow', Tablename, ScheduleKeyID)
end else
Error_Services('Add', 'ScheduleKeyID argument was missing from the ' : Service : ' service.')
end
Response = ScheduleDetail
end service
//----------------------------------------------------------------------------------------------------------------------
// SetScheduleDetail
//
// Creates or updates the SCHED_DET_NG row for the indicated Key ID.
//----------------------------------------------------------------------------------------------------------------------
Service SetScheduleDetail(ScheduleKeyID, ScheduleDetail)
If (ScheduleKeyID NE '') AND (ScheduleDetail NE '') then
TableName = 'SCHED_DET_NG'
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
//----------------------------------------------------------------------------------------------------------------------
// DeleteScheduleDetail
//
// Deletes the SCHED_DET_NG row for the indicated Key IDs.
//----------------------------------------------------------------------------------------------------------------------
Service DeleteScheduleDetail(ScheduleKeyIDs)
If ScheduleKeyIDs NE '' then
TableName = 'SCHED_DET_NG'
For Each ScheduleKeyID in ScheduleKeyIDs using @FM
ReactNo = Xlate(TableName, ScheduleKeyID, 'REACT_NO', 'X')
ReactRec = Database_Services('ReadDataRow', 'REACTOR', ReactNo)
ReactEvents = ReactRec<REACTOR_SCHED_EVENTS$>
Locate ScheduleKeyID in ReactEvents using @VM setting vPos then
ReactEvents = Delete(ReactEvents, 0, vPos, 0)
ReactRec<REACTOR_SCHED_EVENTS$> = ReactEvents
Database_Services('WriteDataRow', 'REACTOR', ReactNo, ReactRec, True$, False$, True$)
end
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, ReactorNo, EventWafers)
DaysNeeded = ''
If WorkOrderNo NE '' then
WorkOrderDetail = Work_Order_Services('GetWorkOrderLogDetail', WorkOrderNo)
If Error_Services('NoError') then
If EventWafers EQ '' then
// Get estimate for all remaining wafers in the work order.
EventWafers = Work_Order_Services('GetRemainingWafers', WorkOrderNo)
end
EpiPartNo = WorkOrderDetail<WO_LOG_EPI_PART_NO$>
TotalWafers = WorkOrderDetail<WO_LOG_WO_QTY$>
WOReactorType = WorkOrderDetail<WO_LOG_REACT_TYPE$>
WOClosedFlag = WorkOrderDetail<WO_LOG_CLOSE_DATE$> NE ''
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
WafersPerDay = Epi_Part_Services('GetAdjustedWafersPerDayScheduler', EpiPartNo, WOReactorType)
If ( (WafersPerDay NE 0) and (WafersPerDay NE '') ) then
DaysNeeded = (EventWafers / WafersPerDay)
end
end else
GoSub LogError
end
end else
Error_Services('Add', 'WorkOrderNo argument was missing from the ' : Service : ' service.')
GoSub LogError
end
Response = DaysNeeded
end service
Service GetEventAdjustment(SchedDetKey)
DaysNeeded = ''
If SchedDetKey NE '' then
SchedDetRec = Schedule_Services('GetScheduleDetail', SchedDetKey)
BlockOut = SchedDetRec<SCHED_DET_NG.BLOCK_OUT$>
If BlockOut EQ True$ then
// Simply return the original duration of the event (i.e. there is no adjustment)
EventStartDTM = SchedDetRec<SCHED_DET_NG.START_DTM$>
EventStopDTM = SchedDetRec<SCHED_DET_NG.STOP_DTM$>
DaysNeeded = EventStopDTM - EventStartDTM
end else
// Calculate new duration necessary to complete event.
WorkOrderNo = SchedDetRec<SCHED_DET_NG.WO_NO$>
WorkOrderDetail = Work_Order_Services('GetWorkOrderLogDetail', WorkOrderNo)
If Error_Services('NoError') then
CurrDTM = Datetime()
EventRemWfrs = Schedule_Services('GetEventRemainingWfrs', SchedDetKey)
If Error_Services('NoError') then
EpiPartNo = WorkOrderDetail<WO_LOG_EPI_PART_NO$>
TotalWafers = WorkOrderDetail<WO_LOG_WO_QTY$>
WOReactorType = WorkOrderDetail<WO_LOG_REACT_TYPE$>
WOClosedFlag = WorkOrderDetail<WO_LOG_CLOSE_DATE$> NE ''
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
WafersPerDay = Epi_Part_Services('GetAdjustedWafersPerDayScheduler', EpiPartNo, WOReactorType)
If Error_Services('NoError') then
DaysNeeded = (EventRemWfrs / WafersPerDay)
end else
GoSub LogError
end
end else
GoSub LogError
end
end else
GoSub LogError
end
end
end else
Error_Services('Add', 'SchedDetKey argument was missing from the ' : Service : ' service.')
GoSub LogError
end
Response = DaysNeeded
end service
Service GetWafersPerDayAdjusted(WONo)
If WONo NE '' then
WorkOrderDetail = Work_Order_Services('GetWorkOrderLogDetail', WONo)
If Error_Services('NoError') then
If Error_Services('NoError') then
EpiPartNo = WorkOrderDetail<WO_LOG_EPI_PART_NO$>
WOReactorType = WorkOrderDetail<WO_LOG_REACT_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
WafersPerDay = Epi_Part_Services('GetAdjustedWafersPerDayScheduler', EpiPartNo, WOReactorType)
If Error_Services('NoError') then
Response = WafersPerDay
end else
GoSub LogError
end
end else
GoSub LogError
end
end else
GoSub LogError
end
end else
Error_Services('Add', 'WONo argument was missing from the ' : Service : ' service.')
GoSub LogError
end
end service
Service GetWaferEstimate(WONo, TimeWindow)
If ( (WONo NE '') and (TimeWindow NE '') ) then
WafersPerDayAdjusted = Schedule_Services('GetWafersPerDayAdjusted', WONo)
WaferEstimate = WafersPerDayAdjusted * TimeWindow
Response = WaferEstimate
end
end service
Service GetEventDuration(WONo, WaferQty)
If ( (WONo NE '') and (WaferQty NE '') ) then
WafersPerDayAdjusted = Schedule_Services('GetWafersPerDayAdjusted', WONo)
If ( (WafersPerDayAdjusted NE '') and (WafersPerDayAdjusted GT 0) ) then
TimeEstimate = WaferQty / WafersPerDayAdjusted
Response = TimeEstimate
end else
WorkOrderDetail = Work_Order_Services('GetWorkOrderLogDetail', WONo)
If WorkOrderDetail NE '' then
EpiPartNo = WorkOrderDetail<WO_LOG_EPI_PART_NO$>
ReactType = WorkOrderDetail<WO_LOG_REACT_TYPE$>
ErrorMsg = 'Error in ':Service:' service. Wafers per day value not set for EpiPart ':EpiPartNo:' for reactor type 'ReactType:'.'
end else
ErrorMsg = 'Error in ':Service:' service. Wafers per day value not set for work order ':WONo:'.'
end
end
end
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()
HaveLock = Database_Services('GetKeyIDLock', 'APP_INFO', 'AUTO_SCHEDULER_REACTOR')
If HaveLock then
ReactNos = Reactor_Services('GetReactorNumbers')
If Error_Services('NoError') then
LastReactNo = ''
If RowExists('APP_INFO', 'AUTO_SCHEDULER_REACTOR') then
LastReactNo = Database_Services('ReadDataRow', 'APP_INFO', 'AUTO_SCHEDULER_REACTOR')
end
If LastReactNo NE '' then
Locate LastReactNo in ReactNos using @FM setting fPos then
fPos += 1
end
end else
fPos = 1
end
NextReactNo = ReactNos<fPos>
// WriteDataRow service will release the lock.
Database_Services('WriteDataRow', 'APP_INFO', 'AUTO_SCHEDULER_REACTOR', NextReactNo, True$, False$, False$)
Schedule_Services('AdjustScheduleEvents', NextReactNo)
end
end
end service
//----------------------------------------------------------------------------------------------------------------------
// LogActivity
//
// Logs the current activity indicated in the LogData argument. This should be reactor based.
//----------------------------------------------------------------------------------------------------------------------
Service LogActivity(ReactorNo, LogData, EmailNotification=BOOLEAN)
If ReactorNo EQ '' then ReactorNo = 0
LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\Scheduler\' : ReactorNo
LogFileName = Date[7, 4] : '-' : Date[1, 2] : '-' : Date[4, 2] : ' Scheduler Log.csv'
objLog = Logging_Services('NewLog', LogPath, LogFilename, CRLF$, ' ', Headers, ColumnWidths, False$, False$)
If EmailNotification then
Recipients = 'DANIEL_ST'
SentFrom = @USER4
Subject = 'Scheduler Notification'
Message = LogData
Swap @FM with ' ' in Message
AttachWindow = ''
AttachKey = ''
SendToGroup = ''
Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
obj_Notes('Create',Parms)
end
Logging_Services('AppendLog', objLog, LogData, @RM, @FM, False$)
end service
Service GetScheduledWfrQty(WorkOrderNo, EndDtm)
ScheduledQty = ''
TableName = "SCHED_DET_NG"
Open "DICT.":TableName To hTableDict then
ScheduledQty = 0
Column = "WO_NO"
QueryParam = WorkOrderNo
Query = Column:@VM:QueryParam:@FM
If EndDtm NE '' then
If Not(Num(EndDtm)) then EndDtm = IConv(EndDtm, 'DT')
Query := "STOP_DTM":@VM:'<=':EndDtm:@FM
end
Keylist = ""
Option = "S"
Flag = ""
Btree.Extract(Query, TableName, hTableDict, Keylist, Option, Flag)
If Flag EQ 0 then
For each KeyID in Keylist using @VM
ScheduledQty += Xlate('SCHED_DET_NG', KeyID, 'EVENT_TOTAL_WFRS', 'X')
Next KeyID
end
end
Response = ScheduledQty
end service
Service GetScheduledEvents(WorkOrderNo)
Keylist = ""
TableName = "SCHED_DET_NG"
Open "DICT.":TableName To hTableDict then
ScheduledQty = 0
Column = "WO_NO"
QueryParam = WorkOrderNo
Query = Column:@VM:QueryParam:@FM
Option = "S"
Flag = ""
Btree.Extract(Query, TableName, hTableDict, Keylist, Option, Flag)
If Flag EQ 0 then
Error_Services('Add', 'Error in ':Service:' service. Error calling Btree.Extract.')
end
end
Response = KeyList
end service
Service GetScheduledCassettes(WorkOrderNo)
ScheduledCassettes = ''
TableName = "SCHED_DET_NG"
Open "DICT.":TableName To hTableDict then
ScheduledQty = 0
Column = "WO_NO"
QueryParam = WorkOrderNo
Query = Column:@VM:QueryParam:@FM
Keylist = ""
Option = "S"
Flag = ""
Btree.Extract(Query, TableName, hTableDict, Keylist, Option, Flag)
If Flag EQ 0 then
For each KeyID in Keylist using @VM
UnprocessedCass = Xlate('SCHED_DET_NG', KeyID, 'UNPROCESSED_CASS', 'X')
ProcessedCass = Xlate('SCHED_DET_NG', KeyID, 'PROCESSED_CASS', 'X')
ScheduledCassettes = SRP_Array('Join', ScheduledCassettes, UnprocessedCass, 'OR', @VM)
ScheduledCassettes = SRP_Array('Join', ScheduledCassettes, ProcessedCass, 'OR', @VM)
Next KeyID
end
end
ScheduledCassettes = SRP_Array('SortSimpleList', ScheduledCassettes, 'AscendingNumbers', @VM)
Response = ScheduledCassettes
end service
Service GetUnscheduledWfrQty(WorkOrderNo)
If WorkOrderNo NE '' then
ScheduledWfrQty = Schedule_Services('GetScheduledWfrQty', WorkOrderNo)
WorkOrderWfrQty = Xlate('WO_LOG', WorkOrderNo, 'QTY', 'X')
UnscheduledWfrQty = WorkOrderWfrQty - ScheduledWfrQty
Response = UnscheduledWfrQty
end
end service
//----------------------------------------------------------------------------------------------------------------------
// GetAvailableWorkOrders
//
// Returns an array of work orders with wafers available to schedule.
//----------------------------------------------------------------------------------------------------------------------
Service GetAvailableWorkOrders(ReactorType, SusceptorSize, ReactorNo)
WorkOrders = ''
If ( (ReactorType NE '') or (ReactorNo NE '') ) then
If ReactorType EQ '' then ReactorType = Xlate('REACTOR', ReactorNo, 'REACT_TYPE', 'X')
end
If ReactorType _EQC 'ASM+' then ReactorType = 'ASM'
WorkOrderList = Database_Services('ReadDataRow', 'APP_INFO', 'SCHED_WO_LIST')
NumRows = DCount(WorkOrderList, @FM)
For RowIndex = 1 to NumRows
WOLogKeyID = WorkOrderList<RowIndex, 1>
WOLogRow = Database_Services('ReadDataRow', 'WO_LOG', WOLogKeyID)
EpiPartNo = WOLogRow<WO_LOG_EPI_PART_NO$>
ProdVerNo = WOLogRow<WO_LOG_PROD_VER_NO$>
ProdVerRow = Database_Services('ReadDataRow', 'PROD_VER', ProdVerNo)
PSN = ProdVerRow<PROD_VER_PROC_STEP_PSN$>
ProdSpecRow = Database_Services('ReadDataRow', 'PROD_SPEC', PSN)
BlockedReactors = ProdSpecRow<PROD_SPEC_BLOCKED_REACTS$>
AcceptWorkOrder = True$ ; // Assume this work order is acceptable for now.
PSNBlock = False$
If EpiPartNo NE '' then
If SusceptorSize NE '' then
EpiPart = Database_Services('ReadDataRow', 'EPI_PART', EpiPartNo)
SuscSize = EpiPart<EPI_PART_SUB_WAFER_SIZE$>
If SuscSize NE SusceptorSize then
// The susceptor size for this work order does not match the required susceptor size.
AcceptWorkOrder = False$
end
end
If (BlockedReactors NE '') AND (ReactorNo NE '') then
// This work order uses a part spec that is blocked on the current reactor.
Locate ReactorNo in BlockedReactors using @VM setting vPos then PSNBlock = True$
end
RowReactorType = WorkOrderList<RowIndex, 4>
If ( (RowReactorType _NEC ReactorType) and (RowReactorType _NEC ReactorType:'+') ) then
AcceptWorkOrder = False$
end
If AcceptWorkOrder then
Swap False$ with '' in PSNBlock
Swap True$ with 'Yes' in PSNBlock
WorkOrderList<RowIndex, 5> = PSNBlock
WorkOrders = Insert(WorkOrders, -1, 0, 0, WorkOrderList<RowIndex>)
end
end
Next RowIndex
Response = WorkOrders
end service
Service MarkCassProcessed(WONo, CassNo, ProcessedDTM)
If ((WONo NE '') and (CassNo NE '') and (ProcessedDTM NE '') ) then
If Num(ProcessedDTM) then ProcessedDTM = OConv(ProcessedDTM, 'DT2/^H')
// Find the SCHED_DET_NG event record that contains the cassette number
Query = 'SELECT SCHED_DET_NG WITH WO_NO EQ ':WONo:' AND WITH START_DTM LE "':ProcessedDTM:'" '
Query := 'AND WITH UNPROCESSED_CASS EQ ':CassNo:' BY START_DTM'
GoSub ClearCursors
RList(Query, TARGET_ACTIVELIST$, '', '', '')
EOF = False$
Done = False$
Loop
Readnext SchedDetNGKey else EOF = True$
Until EOF = True$
SchedDetNGRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetNGKey)
CassComp = False$
ReactType = Xlate('WO_LOG', WONo, 'REACT_TYPE', 'X')
If ReactType EQ 'EPP' then
// Since EpiPro splits WM_IN cassettes out into RDS runs, we need to determine if the
// cassette has been emptied out or if enough wafers have been consumed such that
// the wafer count equals the split work order event quantity.
StopDtm = SchedDetNGRec<SCHED_DET_NG.STOP_DTM$>
SchedWfrQty = Schedule_Services('GetScheduledWfrQty', WONo, StopDtm)
// Determine the cassette and slot this event should end on. This will be used to identify
// cassettes that should be marked as complete when the event ends in the middle of the cassette.
// That is, the EpiPro work order events should end on an RDS when they are split around block
// out events.
UnprocCassList = SchedDetNGRec<SCHED_DET_NG.UNPROCESSED_CASS$>
ProcCassList = SchedDetNGRec<SCHED_DET_NG.PROCESSED_CASS$>
AllSchedCass = SRP_Array('Join', UnprocCassList, ProcCassList, 'OR', @VM)
AllSchedCass = SRP_Array('SortSimpleList', AllSchedCass, 'AscendingNumbers', @VM)
NumSchedCass = DCount(AllSchedCass, @VM)
EndCassNo = AllSchedCass<0, NumSchedCass>
EndSlotNo = Mod(SchedWfrQty, 25)
If EndSlotNo EQ 0 then EndSlotNo = 25
WMIKey = WONo:'*1*':CassNo
WMIRdsList = Xlate('WM_IN', WMIKey, 'RDS_NO', 'X')
UnloadDtms = Xlate('RDS', WMIRdsList, 'DATETIME_OUT', 'X')
NumUnloadedWfrs = 0
If UnloadDtms NE '' then
For each UnloadDtm in UnloadDtms using @VM
NumUnloadedWfrs += (UnloadDtm NE '')
Next UnloadDtm
end
CassRemWfrs = Xlate('WM_IN', WMIKey, 'REM_WFRS', 'X')
If ( (NumUnloadedWfrs EQ 25) or ( (CassNo EQ EndCassNo) and (NumUnloadedWfrs GE EndSlotNo) ) or (CassRemWfrs EQ 0) ) then
CassComp = True$
end
end else
CassComp = True$
end
UnprocessedCassettes = SchedDetNGRec<SCHED_DET_NG.UNPROCESSED_CASS$>
If CassComp then
Locate CassNo in UnprocessedCassettes using @VM setting vPos then
UnprocessedCassettes = Delete(UnprocessedCassettes, 0, vPos, 0)
ProcessedCassettes = SchedDetNGRec<SCHED_DET_NG.PROCESSED_CASS$>
ProcessedCassettes<0, -1> = CassNo
SchedDetNGRec<SCHED_DET_NG.UNPROCESSED_CASS$> = UnprocessedCassettes
SchedDetNGRec<SCHED_DET_NG.PROCESSED_CASS$> = ProcessedCassettes
Database_Services('WriteDataRow', 'SCHED_DET_NG', SchedDetNGKey, SchedDetNGRec, True$, False$, True$)
Done = True$
end
end
Until Done EQ True$
Repeat
end
end service
Service GetEventCurrCass(SchedDetNGKey)
CurrCassNo = ''
If SchedDetNGKey NE '' then
SchedDetNGRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetNGKey)
CurrRunNo = Schedule_Services('GetEventCurrRun', SchedDetNGKey)
If CurrRunNo NE '' then
ReactNo = SchedDetNGRec<SCHED_DET_NG.REACT_NO$>
WONo = SchedDetNGRec<SCHED_DET_NG.WO_NO$>
ReactType = Xlate('REACTOR', ReactNo, 'REACT_TYPE', 'X')
AllRuns = Xlate('WO_STEP', WONo:'*1', 'RDS_KEY', 'X')
CurrRDSNo = AllRuns<0, CurrRunNo>
If ReactType EQ 'EPP' then
WMIKeys = Xlate('RDS', CurrRDSNo, 'WM_IN_KEY', 'X')
CurrCassNo = WMIKeys[-1, 'B*']
end else
CurrCassNo = Xlate('RDS', CurrRDSNo, 'CASS_NO', 'X')
end
end else
// No runs have been loaded yet, so use the first cassette assigned to the event
UnprocCassList = SchedDetNGRec<SCHED_DET_NG.UNPROCESSED_CASS$>
ProcCassList = SchedDetNGRec<SCHED_DET_NG.PROCESSED_CASS$>
EventCassList = SRP_Array('Join', ProcCassList, UnprocCassList, 'OR', @VM)
EventCassList = SRP_Array('SortSimpleList', EventCassList, 'AscendingNumbers', @VM)
CurrCassNo = EventCassList<0, 1>
end
end else
Error_Services('Add', 'Error in ':Service:' service. Null SchedDetNGKey passed in.')
end
Response = CurrCassNo
end service
Service GetEventCurrRun(SchedDetNGKey)
CurrRunNo = ''
If SchedDetNGKey NE '' then
EventRDSKeys = Schedule_Services('GetEventRDSKeys', SchedDetNGKey)
Swap @VM with @FM in EventRDSKeys
If EventRDSKeys NE '' then
Query = "WITH {DATE_IN} NE ''"
Tablename = 'RDS'
Open Tablename to hTable then
Open 'DICT.RDS' to hDict then
GoSub ClearCursors
Cursor = 0
Make.List(Cursor, EventRDSKeys, hTable, hDict)
Mode = 2 ; // Use existing cursor initialized above
SortList = '#DATETIME_IN'
Select Tablename by SortList using Cursor then
Reduce(Query, SortList, Mode, Tablename, Cursor, Flag)
If Flag then
// Success
ReadNext RDSKey then CurrRunNo = Xlate('RDS', RDSKey, 'RUN_ORDER_NUM', 'X')
GoSub ClearCursors
end
end
end
end
end
end else
Error_Services('Add', 'Error in ':Service:' service. Null SchedDetNGKey passed in.')
end
Response = CurrRunNo
end service
Service GetEventRDSKeys(SchedDetNGKey)
RDSKeys = ''
If SchedDetNGKey NE '' then
SchedEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetNGKey)
If Error_Services('NoError') then
WorkOrderNo = SchedEventRec<SCHED_DET_NG.WO_NO$>
ReactNo = SchedEventRec<SCHED_DET_NG.REACT_NO$>
ReactType = Xlate('REACTOR', ReactNo, 'REACT_TYPE', 'X')
UnprocCass = SchedEventRec<SCHED_DET_NG.UNPROCESSED_CASS$>
ProcCass = SchedEventRec<SCHED_DET_NG.PROCESSED_CASS$>
EventComp = SchedEventRec<SCHED_DET_NG.EVENT_COMP$>
AllCass = SRP_Array('Join', UnprocCass, ProcCass, 'OR', @VM)
AllCass = SRP_Array('SortSimpleList', AllCass, 'AscendingNumbers', @VM)
If ReactType NE 'EPP' then
For each CassNo in AllCass using @VM setting vPos
WOMatKey = WorkOrderNo:'*':CassNo
RDSKeys<0, -1> = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
Next CassNo
end else
// Determine starting slot to exlude runs that are part of previous schedule events
QueryDtm = SchedEventRec<SCHED_DET_NG.START_DTM$>
SchedQty = SchedEventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
EventComp = SchedEventRec<SCHED_DET_NG.EVENT_COMP$>
PrevSchedQty = Schedule_Services('GetScheduledWfrQty', WorkOrderNo, QueryDtm)
StartSlot = Mod(PrevSchedQty, 25) + 1
TotalQty = PrevSchedQty + SchedQty
StopSlot = Mod(TotalQty, 25)
If StopSlot EQ 0 then StopSlot = 25
StartCass = AllCass<0, 1>
StopCass = AllCass[-1, 'B':@VM]
For each CassNo in AllCass using @VM setting vPos
WMInKey = WorkOrderNo:'*1*':CassNo
WMInRDSList = Xlate('WM_IN', WMInKey, 'RDS_NO', 'X')
Begin Case
Case CassNo EQ StartCass
For SlotIndex = StartSlot to 25
RDSKeys<0, -1> = WMInRDSList<0, SlotIndex>
Next SlotIndex
Case CassNo EQ StopCass
For SlotIndex = 1 to StopSlot
RDSKeys<0, -1> = WMInRDSList<0, SlotIndex>
Next SlotIndex
Case ( (CassNo EQ StartCass) and (CassNo EQ StopCass) )
For SlotIndex = StartSlot to StopSlot
RDSKeys<0, -1> = WMInRDSList<0, SlotIndex>
Next SlotIndex
Case Otherwise$
RDSKeys<0, -1> = WMInRDSList
End Case
Next CassNo
end
If Not(EventComp) then
// Add in RDS keys not yet loaded (i.e. not associated with any WM_IN record yet).
Query = "SELECT RDS WITH WO EQ ":WorkOrderNo:" AND WITH DATE_IN EQ '' BY SEQ"
RList(Query, TARGET_ACTIVELIST$, '', '', '')
ErrCode = ''
If Not(Get_Status(ErrCode)) then
EOF = False$
Loop
ReadNext RDSKey else EOF = True$
Until EOF
RDSKeys<0, -1> = RDSKey
Repeat
end
end
RDSKeys = SRP_Array('Clean', RDSKeys, 'TrimAndMakeUnique', @VM)
end
end else
Error_Services('Add', 'Error in ':Service:' service. Null SchedDetNGKey passed in.')
end
Response = RDSKeys
end service
Service GetEventLoadedRDSKeys(SchedDetNGKey)
RDSKeys = ''
If SchedDetNGKey NE '' then
EventRDSKeys = Schedule_Services('GetEventRDSKeys', SchedDetNGKey)
Swap @VM with @FM in EventRDSKeys
If EventRDSKeys NE '' then
Query = "WITH {DATE_IN} NE '' AND WITH {DATE_OUT} EQ ''"
Tablename = 'RDS'
Open Tablename to hTable then
Open 'DICT.RDS' to hDict then
GoSub ClearCursors
Cursor = 0
Make.List(Cursor, EventRDSKeys, hTable, hDict)
Mode = 2 ; // Use existing cursor initialized above
SortList = 'SEQ'
Reduce(Query, SortList, Mode, Tablename, Cursor, Flag)
If Flag then
// Success
EOF = False$
Loop
ReadNext RDSKey else EOF = True$
Until EOF
RDSKeys<0, -1> = RDSKey
Repeat
end
end
end
end
end else
Error_Services('Add', 'Error in ':Service:' service. Null SchedDetNGKey passed in.')
end
Response = RDSKeys
end service
Service GetEventLoadedWfrQty(SchedDetNGKey)
LoadedQty = ''
If SchedDetNGKey NE '' then
ReactNo = Xlate('SCHED_DET_NG', SchedDetNGKey, 'REACT_NO', 'X')
ReactType = Xlate('REACTOR', ReactNo, 'REACT_TYPE', 'X')
LoadedRDS = Schedule_Services('GetEventLoadedRDSKeys', SchedDetNGKey)
If LoadedRDS NE '' then
If ReactType EQ 'EPP' then
LoadedQty = Sum(Xlate('REACT_RUN', LoadedRDS, 'WFRS_EPI_LOAD', 'X'))
end else
LoadedQty = Sum(Xlate('RDS', LoadedRDS, 'WAFERS_IN', 'X'))
end
end
end else
Error_Services('Add', 'Error in ':Service:' service. Null SchedDetNGKey passed in.')
end
Response = LoadedQty
end service
Service NotifySupervisorsIfSameDayChange(NewRec, OrigRec)
If NewRec NE '' then
StartDTM = NewRec<SCHED_DET_NG.START_DTM$>
StopDTM = NewRec<SCHED_DET_NG.STOP_DTM$>
If (StartDTM NE '') and (StopDTM NE '') then
CurrentDTM = SRP_DateTime("Now")
CurrentPlus24DTM = SRP_Datetime("AddHours", CurrentDTM, 24)
IrrelevantStopChange = False$
IrrelevantStartChange = False$
If OrigRec NE '' then
OrigStartDTM = OrigRec<SCHED_DET_NG.START_DTM$>
OrigStopDTM = OrigRec<SCHED_DET_NG.STOP_DTM$>
If (OrigStartDTM NE '') and (OrigStopDTM NE '') then
// Check to make sure not just an extension where original stop was already after 24 hours from now
IrrelevantStopChange = ((OrigStopDTM GE CurrentPlus24DTM) and (StopDTM GE CurrentPlus24DTM)) or (OrigStopDTM EQ StopDTM)
// Check to make sure original start was not already before today and new start still not after now
IrrelevantStartChange = ((OrigStartDTM LT CurrentDTM) and (StartDTM LT CurrentDTM)) or (OrigStartDTM EQ StartDTM)
end
end
If ((IrrelevantStopChange NE True$) or (IrrelevantStartChange NE True$)) and (StartDTM LT StopDTM) then
SchedEventInNext24Hours = False$
GoSub VerifySameDayChange
If SchedEventInNext24Hours EQ True$ then
WorkOrder = NewRec<SCHED_DET_NG.WO_NO$>
ReactorNo = NewRec<SCHED_DET_NG.REACT_NO$>
HotLot = Work_Order_Services("GetHotFlag", WorkOrder)
Description = NewRec<SCHED_DET_NG.DESC$>
Message = ''
If HotLot NE False$ then Message := '[HOT LOT] '
Message := 'A same-day schedule change was made'
If ReactorNo NE '' then
Message := ' on reactor ':ReactorNo:'.'
end else
Message := '.'
end
EmailSubject = ''
If HotLot NE False$ then
EmailSubject = '[HOT LOT] '
end else
EmailSubject = ''
end
EmailSubject := 'Same-day schedule change!'
GoSub CreateScheduleChangeNotification
end
end
end
end else
If OrigRec NE '' then
StartDTM = OrigRec<SCHED_DET_NG.START_DTM$>
StopDTM = OrigRec<SCHED_DET_NG.STOP_DTM$>
If (StartDTM NE '') and (StopDTM NE '') then
CurrentDTM = SRP_DateTime("Now")
CurrentPlus24DTM = SRP_Datetime("AddHours", CurrentDTM, 24)
SchedEventInNext24Hours = False$
GoSub VerifySameDayChange
If SchedEventInNext24Hours EQ True$ then
WorkOrder = OrigRec<SCHED_DET_NG.WO_NO$>
ReactorNo = OrigRec<SCHED_DET_NG.REACT_NO$>
HotLot = Work_Order_Services("GetHotFlag", WorkOrder)
Description = OrigRec<SCHED_DET_NG.DESC$>
Message = 'A scheduled event was cancelled or removed on the same day'
If ReactorNo NE '' then
Message := ' on reactor ':ReactorNo:'.'
end else
Message := '.'
end
EmailSubject = 'Same-day schedule change!'
GoSub CreateScheduleChangeNotification
end
end
end
end
end service
Service SendRefreshMessage()
SendUpdate = True$
LastRefreshDtm = Xlate('APP_INFO', 'AUTO_SCHEDULER_LAST_REFRESH', '', 'X')
If LastRefreshDtm NE '' then
ElapMinutes = SRP_Datetime('MinuteSpan', LastRefreshDtm, Datetime(), True$)
SendUpdate = (ElapMinutes GT 5)
end
If SendUpdate then
Messaging_Services('SendMessage', 'RefreshSchedule', 'Response', @User4, 'All', @User4, 'EventHandler', 'NDW_SCHEDULER' : ',OMNIEVENT,@MESSAGE,@ARGUMENTS')
Database_Services('WriteDataRow', 'APP_INFO', 'AUTO_SCHEDULER_LAST_REFRESH', Datetime(), True$, False$, True$)
end
end service
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ClearCursors:
For counter = 0 to 8
ClearSelect counter
Next counter
return
LogError:
Message = Error_Services('GetMessage')
LogData = ''
LogData<1> = Oconv(Date(), 'D4/') : ' ' : Oconv(Time(), 'MTS') ; // Logging DTM
LogData<2> = Service : ' Error' ; // Service Step
LogData<3> = Str(' ', 12) : Message
Schedule_Services('LogActivity', ReactorNo, LogData)
return
VerifySameDayChange:
If (StartDTM LE CurrentDTM) and (StopDTM GE CurrentPlus24DTM) then
SchedEventInNext24Hours = True$
end
If (StartDTM GE CurrentDTM) and (StopDTM LE CurrentPlus24DTM) then
SchedEventInNext24Hours = True$
end
If (StartDTM GE CurrentDTM) and (StartDTM LT CurrentPlus24DTM) and (StopDTM GE CurrentPlus24DTM) then
SchedEventInNext24Hours = True$
end
If (StartDTM LE CurrentDTM) and (StopDTM LE CurrentPlus24DTM) and (StopDTM GT CurrentDTM) then
SchedEventInNext24Hours = True$
end
return
CreateScheduleChangeNotification:
If WorkOrder NE '' then Message := CRLF$:'Work Order: ':WorkOrder:'.'
ConvertedStart = OConv(StartDTM, 'DT2/^H')
If StartDTM NE '' then Message := CRLF$:'Start Time: ':ConvertedStart:'.'
ConvertedStop = OConv(StopDTM, 'DT2/^H')
If StopDTM NE '' then Message := CRLF$:'End Time: ':ConvertedStop:'.'
If Description NE '' then Message := CRLF$:'Description: ':Description:'.'
IncludeNextShift = False$
//If we are close to shift change we want to add in the next shift.
CurrTime = Time()
if SRP_Time('Hour', CurrTime) EQ 17 OR SRP_Time('Hour', CurrTime) EQ 5 then
IncludeNextShift = True$
end
OnShiftSupervisors = Lsl_Users_Services('GetOnShiftUsersByClass', 'Si Shift Supervisors', IncludeNextShift)
OnShiftLeads = Lsl_Users_Services('GetOnShiftUsersByClass', 'Lead Operator', IncludeNextShift)
Recipients = OnShiftSupervisors:@VM:OnShiftLeads
SentFrom = @User4
AttachWindow = ''
AttachKey = ''
SendToGroup = ''
Parms = Recipients:@RM:SentFrom:@RM:EmailSubject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
obj_Notes('Create',Parms)
return