3564 lines
150 KiB
Plaintext
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
|
|
|
|
|