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 Size = ReactorRec SecondChamber = ReactorRec 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 Location = ReactorRec 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 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 WorkOrderNo = SchedDetRec StartDTM = SchedDetRec StopDTM = SchedDetRec ScheduleEventSummary = Schedule_Services('GetScheduleEventSummary', SchedDetKeyID, False$) ScheduleEvents := ReactorNo : @VM ScheduleEvents := SchedDetKeyID : @VM ; //WorkOrderNo : '*' : ScheduleEventSummary : '*' : ReactorNo : '*' : ScheduleDate : @VM ScheduleEvents := ScheduleEventSummary : @VM ScheduleEvents := ScheduleEventSummary : @VM ScheduleEvents := ScheduleEventSummary : ' L=' : Lum : @VM ScheduleEvents := ScheduleEventSummary : @VM ScheduleEvents := ScheduleEventSummary : @VM ScheduleEvents := ScheduleEventSummary : @VM ScheduleEvents := @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM ScheduleEvents := ScheduleEventSummary : @SVM : ScheduleEventSummary : @SVM : ScheduleEventSummary : @SVM : ScheduleEventSummary 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 WorkOrderNo = SchedDetRec StartDTM = SchedDetRec StopDTM = SchedDetRec ScheduleEventSummary = Schedule_Services('GetScheduleEventSummary', SchedDetKeyID, False$) SchedEvent = ReactorNo : @VM SchedEvent := SchedDetKeyID : @VM SchedEvent := ScheduleEventSummary : @VM SchedEvent := ScheduleEventSummary : @VM SchedEvent := ScheduleEventSummary : ' L=' : Lum : @VM SchedEvent := ScheduleEventSummary : @VM SchedEvent := ScheduleEventSummary : @VM SchedEvent := ScheduleEventSummary : @VM SchedEvent := @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM : @VM SchedEvent := ScheduleEventSummary : @SVM : ScheduleEventSummary : @SVM : ScheduleEventSummary : @SVM : ScheduleEventSummary 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 WorkOrderNo = SchedDetRec StartDTM = SchedDetRec StopDTM = SchedDetRec Desc = SchedDetRec Modified = SchedDetRec BlockOut = SchedDetRec BlockOutType = SchedDetRec BackColor = SchedDetRec ForeColor = SchedDetRec EventWfrQty = SchedDetRec ReactorDetails = Schedule_Services('GetReactorDetails', ReactorNo) Type = ReactorDetails Size = ReactorDetails 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 EpiPartNo = WOLogRow ProdVerNo = WOLogRow TotalWafers = SchedDetRec WOClosedFlag = SchedDetRec HotLotFlag = WOLogRow 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 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 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 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 ReactRec = Database_Services('ReadDataRow', 'REACTOR', ReactNo) ReactSchedEvents = ReactRec 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 = 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 ReactorNo = SchedDetRec EventStartDTM = SchedDetRec 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 EventStopDTM = SchedDetRec WONo = SchedDetRec EventStartDTM = SchedDetRec 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 ReactType = Xlate('WO_LOG', WONo, 'REACT_TYPE', 'X') SchedQty = SchedDetRec UnprocessedCassettes = SchedDetRec NumUnprocessedCassettes = DCount(UnprocessedCassettes, @VM) If ReactType NE 'EPP' then WfrsRemaining = 25 * NumUnprocessedCassettes end else If UnprocessedCassettes NE '' then QueryEndDtm = SchedDetRec 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 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 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 CurrentEventWoMatRecord = Database_Services('ReadDataRow', 'WO_MAT', CurrentEventWoNo:'*1') CurrentEventRdsNo = CurrentEventWoMatRecord CurrentEventRdsRecord = Database_Services('ReadDataRow', 'RDS', CurrentEventRdsNo) CurrentEventPsn = CurrentEventRdsRecord 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 NextEventWoMatRecord = Database_Services('ReadDataRow', 'WO_MAT', NextEventWoNo:'*1') NextEventRdsNo = NextEventWoMatRecord NextEventRdsRecord = Database_Services('ReadDataRow', 'RDS', NextEventRdsNo) NextEventPsn = NextEventRdsRecord 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 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 = 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 NextEventUnprocCass = NextEventRec NextEventProcCass = NextEventRec NextNextEventKey = NonHBList ThisEventRec = Database_Services('ReadDataRow', 'SCHED_DET_NG', ThisEventKey) ThisEventWfrQty = ThisEventRec ThisEventUnprocCass = ThisEventRec ThisEventProcCass = ThisEventRec // 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 = NewEventWfrQty ThisEventRec = ThisEventUnprocCass ThisEventRec = ThisEventProcCass ThisEventRec = 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 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 Schedule_Services('LogActivity', ReactNo, LogData) Logdata<3> = 'Event start dtm: ':OConv(EventRec, 'DT2/^H') Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event stop dtm: ':OConv(EventRec, 'DT2/^H') Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event quantity: ':EventRec Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event complete: ':OConv(EventRec, "Btrue,false") Schedule_Services('LogActivity', ReactNo, LogData) EventAdjustment = Schedule_Services('GetEventAdjustment', EventKey) UnprocessedCass = EventRec ProcessedCass = EventRec AllCassList = SRP_Array('Join', UnprocessedCass, ProcessedCass, 'OR', @VM) AllCassList = SRP_Array('SortSimpleList', AllCassList, 'AscendingNumbers', @VM) EventComp = (UnprocessedCass EQ '') If Not(EventRec) 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 = EventComp WONo = EventRec ReactNo = EventRec EventWfrs = EventRec 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 // 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 PrevEventOrigStopDTM = PrevEventRec PrevEventOrigQty = PrevEventRec PrevEventWONo = PrevEventRec PrevEventBlockOut = PrevEventRec PrevEventDuration = PrevEventOrigStopDTM - PrevEventOrigStartDTM // Ensure previous event is marked as complete If PrevEventRec 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 = True$ If Not(PrevEventBlockOut) then // Set previous event's stop datetime to the last processed cassette/run's unload datetime PrevEventProcCass = PrevEventRec PrevEventUnprocCass = PrevEventRec 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 = 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 PrevEventComp = PrevEventRec 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 = True$ EventWONo = EventRec EventProcCass = EventRec 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 = EventStartDTM EventRec = 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 Schedule_Services('LogActivity', ReactNo, LogData) Logdata<3> = 'Event start dtm: ':OConv(EventRec, 'DT2/^H') Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event stop dtm: ':OConv(EventRec, 'DT2/^H') Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event quantity: ':EventRec Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event complete: ':OConv(EventRec, "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 EventStopDTM = EventRec 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 EventStartDTM = PrevEventStopDTM EventStopDTM = EventStartDTM + EventDuration EventRec = EventStartDTM EventRec = 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 Schedule_Services('LogActivity', ReactNo, LogData) Logdata<3> = 'Event start dtm: ':OConv(EventRec, 'DT2/^H') Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event stop dtm: ':OConv(EventRec, 'DT2/^H') Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event quantity: ':EventRec Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event complete: ':OConv(EventRec, "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 HBEventStopDTM = HBEventRec // 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 ReactType = Xlate('WO_LOG', ThisEventWONo, 'REACT_TYPE', 'X') ThisEventStartDTM = ThisEventRec ThisEventStopDTM = ThisEventRec ThisEventTotalWfrQty = ThisEventRec 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 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 = 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 = FirstEventProcCassList ThisEventRec = FirstEventUnprocCassList ThisEventRec = ThisEventStopDTM ThisEventRec = 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 = ThisEventRec SecondEventRec = ThisEventRec SecondEventRec = SecondEventStartDTM SecondEventRec = SecondEventStartDTM + NewEventDuration SecondEventRec = ThisEventRec SecondEventRec = SecondEventWfrQty SecondEventRec = SecondEventProcCassList SecondEventRec = 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 Schedule_Services('LogActivity', ReactNo, LogData) Logdata<3> = 'Event start dtm: ':OConv(SecondEventRec, 'DT2/^H') Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event stop dtm: ':OConv(SecondEventRec, 'DT2/^H') Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event quantity: ':SecondEventRec Schedule_Services('LogActivity', ReactNo, LogData) LogData<3> = 'Event complete: ':OConv(SecondEventRec, "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 AdjustEventWfrQty = AdjustEventRec 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 = AdjustEventStartDTM AdjustEventRec = 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 EventBlockOut = EventRec 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 = 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 = ReactorNo NewEventRec = WorkOrderNo NewEventRec = StartDTM NewEventRec = Description NewEventRec = 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 = EventComp NewEventRec = EventProcCassList NewEventRec = EventUnprocCassList NewEventRec = 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 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 = 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 If PrevEventStopDTM GT StartDTM then PrevEventStopDTM = StartDTM PrevEventRec = 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 = SchedDetNGKey SchedHistRec = ReactorNo SchedHistRec = Action SchedHistRec = 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 = ReactorNo If WorkOrderNo NE '' then EventRec = WorkOrderNo If StartDTM NE '' then EventRec = StartDTM If StopDTM NE '' then EventRec = StopDTM If Description NE '' then EventRec = Description OrigWfrQty = EventRec // 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 WorkOrderNo = EventRec StartDTM = EventRec StopDTM = EventRec 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 = EventComp EventRec = EventProcCassList EventRec = EventUnprocCassList EventRec = StopDtm EventRec = 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 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 = ReactorNo NewEventRec = StartDTM NewEventRec = StopDTM NewEventRec = Description NewEventRec = True$ NewEventRec = 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 CurrNumEvents = DCount(ReactSchedEvents, @VM) ReactRec = Database_Services('ReadDataRow', 'REACTOR', ReactorNo) ReactSchedEvents = ReactRec 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 = 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 Locate ScheduleKeyID in ReactEvents using @VM setting vPos then ReactEvents = Delete(ReactEvents, 0, vPos, 0) ReactRec = 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 TotalWafers = WorkOrderDetail WOReactorType = WorkOrderDetail WOClosedFlag = WorkOrderDetail 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 If BlockOut EQ True$ then // Simply return the original duration of the event (i.e. there is no adjustment) EventStartDTM = SchedDetRec EventStopDTM = SchedDetRec DaysNeeded = EventStopDTM - EventStartDTM end else // Calculate new duration necessary to complete event. WorkOrderNo = SchedDetRec 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 TotalWafers = WorkOrderDetail WOReactorType = WorkOrderDetail WOClosedFlag = WorkOrderDetail 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 WOReactorType = WorkOrderDetail 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 ReactType = WorkOrderDetail 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 // 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 WOLogRow = Database_Services('ReadDataRow', 'WO_LOG', WOLogKeyID) EpiPartNo = WOLogRow ProdVerNo = WOLogRow ProdVerRow = Database_Services('ReadDataRow', 'PROD_VER', ProdVerNo) PSN = ProdVerRow ProdSpecRow = Database_Services('ReadDataRow', 'PROD_SPEC', PSN) BlockedReactors = ProdSpecRow 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 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 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 = PSNBlock WorkOrders = Insert(WorkOrders, -1, 0, 0, WorkOrderList) 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 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 ProcCassList = SchedDetNGRec 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 If CassComp then Locate CassNo in UnprocessedCassettes using @VM setting vPos then UnprocessedCassettes = Delete(UnprocessedCassettes, 0, vPos, 0) ProcessedCassettes = SchedDetNGRec ProcessedCassettes<0, -1> = CassNo SchedDetNGRec = UnprocessedCassettes SchedDetNGRec = 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 WONo = SchedDetNGRec 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 ProcCassList = SchedDetNGRec 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 ReactNo = SchedEventRec ReactType = Xlate('REACTOR', ReactNo, 'REACT_TYPE', 'X') UnprocCass = SchedEventRec ProcCass = SchedEventRec EventComp = SchedEventRec 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 SchedQty = SchedEventRec EventComp = SchedEventRec 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 StopDTM = NewRec 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 OrigStopDTM = OrigRec 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 ReactorNo = NewRec HotLot = Work_Order_Services("GetHotFlag", WorkOrder) Description = NewRec 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 StopDTM = OrigRec 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 ReactorNo = OrigRec HotLot = Work_Order_Services("GetHotFlag", WorkOrder) Description = OrigRec 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