Function GaN_Services(@Service, @Params) /*********************************************************************************************************************** Name : GaN_Services Description : Handler program for all RDS services. Notes : Application errors should be logged using the Error Services module. There are a few methodological assumptions built into way errors are managed which are important to understand in order to properly work with Error Services: - The term 'top' refers to the originating procedure of a call stack and the term 'bottom' refers to the last routine (or the current routine) within a call stack. Within the OpenInsight Debugger this will appear backwards since the originating procedure always appears at the bottom of the list and the current routine appears at the top of the list. We are using this orientation because it is common to refer to the process of calling other procedures as 'drilling down'. - The reason for defining the orientation of the call stack is because Error_Services allows for multiple error conditions to be appended to an original error. In most cases this will happen when a procedure at the bottom of the stack generates an error condition and then returns to its calling procedure. This higher level procedure can optionally add more information relevant to itself. This continues as the call stack 'bubbles' its way back to the top to where the originating procedure is waiting. - Native OpenInsight commands that handle errors (e.g., Set_Status, Set_FSError, Set_EventStatus) preserve their error state until explicitly cleared. This can hinder the normal execution of code since subsequent procedures (usually SSPs) will fail if a pre-existing error condition exists. Our philosophy is that error conditions should automatically be cleared before a new procedure is executed to avoid this problem. However, the nature of Basic+ does not make this easy to automate for any given stored procedure. Therefore, if a stored procedure wants to conform to our philosophy then it should include a call into the 'Clear' service request at the top of the program. Alternatively this can be done through a common insert (see SERVICE_SETUP for example.) - Service modules will use the SERVICE_SETUP insert and therefore automatically clear out any error conditions that were set before. 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) 01/11/19 djs Original programmer. 07/23/19 djs Added check within UpdateDisposition service to see if an NCR has been added to External wafers before marking DISP complete and removing the wafers from the WIP. Added check within UpdateDisposition service to see if the DISP_COMPLETE flag has been marked for that wafer before marking the DISP stage complete and moving the wafer out of the DISP Location queue. 12/17/19 dpc Removed G_REACT filter from the GetToolList service to allow R69 and R71 to show in Tool filter dropdown. 07/21/20 djs Updated the AutoDisposition service to require retained wafers to be signed (i.e. physically retained by an operator) before the wafer is marked as ready to close. 08/20/20 djs Removed unnecessary inserts and declarations. Created Disposition_Services which contains new services for dispositioning production and engineering wafers. The AutoDisposition and UpdateDisposition services within this service module are now deprecated and should no longer be used. Gan_Services is approaching the maximum executable size. As a result GaN_Services may need to be refactored and split into multiple service modules in the future. ***********************************************************************************************************************/ #pragma precomp SRP_PreCompiler $insert APP_INSERTS $insert SERVICE_SETUP $insert PROD_SPEC_EQUATES $insert TOOL_CLASS_EQUATES $insert REACT_RUN_EQUATES $insert REACTOR_EQUATES $insert PRS_STAGE_EQUATES $insert WO_LOG_EQUATES $insert WO_MAT_EQUATES $insert RLIST_EQUATES $insert TOOL_EQUATES $insert GAN_ETCH_EQUATES $insert WO_WFR_EQUATES $insert TOOL_WFR_EQUATES $insert LOCATION_EQUATES $insert WO_MAT_WFR_EQUATES $insert RUN_STAGE_EQUATES $insert RUN_STAGE_WFR_EQUATES $insert GAN_RUN_INFO_REQUESTS_EQUATES $insert GAN_SCHEDULE_EQUATES $insert GAN_PARAMS_EQUATES $insert RETAINED_WAFERS_EQUATES $insert DISPOSITION_REQUESTS_EQUATES $insert TOOL_ETCH_EQUATES Declare subroutine Error_Services, SRP_JSON, End_Window, Database_Services, Btree.Extract, SQL_Services Declare subroutine Logging_Services, SRP_Array, GaN_Services, Sleepery, WinYield, Yield, Set_Status, Excel_Services Declare subroutine Disposition_Services, Engineering_Services Declare function Database_Services, SRP_JSON, Logging_Services, Environment_Services, Error_Services Declare function SQL_Services, SRP_Array, NextKey, DateTime, GaN_Services, Excel_Services, Max, SRP_Math Declare function GetTickCount LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\Thruput' LogDate = Oconv(Date(), 'D4/') LogTime = Oconv(Time(), 'MTS') LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' ThruPut Log - GaN.csv' Headers = 'Logging DTM':@FM:'RDS Key ID':@FM:'GRWfrQty':@FM:'ScrapQty':@FM:'ProdTWQty':@FM:'DummyQty' objLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$) LoggingDTM = LogDate : ' ' : LogTime ; // Logging DTM LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\GaNWIP' LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' WfrQueueLog.csv' Headers = 'Logging DTM':@FM:'UserID':@FM:'WfrID':@FM:'Param Queue':@FM:'Wafer Queue':@FM:'Error Message' objWIPLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$) LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\GaNWIP' LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' PocketLog.csv' Headers = 'Logging DTM':@FM:'RunID':@FM:'Pockets' objPocketLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$) LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\GaNWIP' LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' UnloadLog.csv' Headers = 'Logging DTM':@FM:'RDSNo':@FM:'Notes' objUnloadLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$) 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 BOOLEAN = True$, False$ Options STAGETYPE = 'Reactor', 'Metrology', 'Disposition' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Services //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //---------------------------------------------------------------------------------------------------------------------- // GetGaNGRProps // // //---------------------------------------------------------------------------------------------------------------------- Service GetGaNGRProps(WOMatKey) If WOMatKey NE '' then ReactorType = Xlate('WO_MAT', WOMatKey, 'REACTOR_TYPE', 'X') // The following NCR and TW counting logic is ran against all RDS, for that WorkOrder, everytime a GaN cassette // is FQA signed. If (ReactorType _EQC 'GAN') then GRWfrQty = 0 ScrapQty = 0 ProdTWQty = 0 DummyQty = 0 EmptyQty = 0 WONo = Field(WOMatKey, '*', 1) WOStepKey = Xlate('WO_MAT', WOMatKey, 'WO_STEP_KEYS', 'X') WOStepRec = Database_Services('ReadDataRow', 'WO_STEP', WOStepKey) WOMatKeys = Xlate('WO_LOG', WONo, 'WO_MAT_KEY', 'X') CurrDTM = Datetime() GRWfrQty = Xlate('WO_MAT_WFR', WOMatKey, 'OUT_WFR_QTY', 'X') // Scan through each wafer in this work order to count the number of WMI, RDS, and WMO NCRs which have // not been reported to SAP yet and report them with this outbound cassette batch ID. For each InboundMatKey in WOMatKeys using @VM CassNo = Field(InboundMatKey, '*', 2) SlotNos = Xlate('WO_MAT', InboundMatKey, 'SLOT_NO', 'X') For each SlotNo in SlotNos using @VM WOWfrKey = WONo:'*':CassNo:'*':SlotNo Database_Services('ActivateRecord', 'WO_WFR', WOWfrKey) OrigRec = @Record If ( ({WMI_NCR_NO} NE '') and ({WMI_NCR_SAP_DTM} EQ '') ) then {WMI_NCR_SAP_DTM} = CurrDTM ScrapQty += 1 end If ( ({RDS_NCR_NO} NE '') and ({RDS_NCR_SAP_DTM} EQ '') ) then {RDS_NCR_SAP_DTM} = CurrDTM ScrapQty += 1 end If ( ({WMO_NCR_NO} NE '') and ({WMO_NCR_SAP_DTM} EQ '') ) then {WMO_NCR_SAP_DTM} = CurrDTM ScrapQty += 1 end If OrigRec NE @Record then Database_Services('WriteDataRow', 'WO_WFR', WOWfrKey, @Record, True$, False$, True$) end Next SlotNo Next InboundMatKey // Scan through all RDS records in this work order to count the number of test wafers which have not // yet been reported to SAP. RDSKeys = WOStepRec For each RDSKey in RDSKeys using @VM ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSKey) GaNRunID = ReactRunRec Scribes = Xlate('REACT_RUN', RDSKey, 'WFR_SCRIBES', 'X') DummyQty = 0 EmptyQty = 0 For each Scribe in Scribes using @VM setting vPos DummyID = GanRunID[1,7]:'D' EmptyID = GaNRunID[1,7]:'E' EmptyFlag = (Indexc(Scribe, 'EMPTY', 1) > 0) If Scribe[1,8] EQ DummyID then DummyQty += 1 end If ( (Scribe[1,8] EQ EmptyID) or (EmptyFlag EQ True$) ) then EmptyQty += 1 end Next Scribe SigProfile = ReactRunRec SigProfileCnt = DCount(SigProfile, @VM) NeedToWrite = False$ InWfrIDs = ReactRunRec For each InWfrID in InWfrIDs using @VM setting vPos If InWfrID NE '' then // Count the number of TWs which have not been reported to SAP yet For SigProfileIndex = 1 to SigProfileCnt Destroyed = False$ Stage = SigProfile<1, SigProfileIndex> WfrKID = InWfrID Convert '*' to '.' in WfrKID WfrStageKey = RDSKey:'*':Stage:'*':WfrKID StageDestTest = XLATE('RUN_STAGE_WFR',WfrStageKey,'DEST_TEST','X') StageCompSig = XLATE('RUN_STAGE_WFR',WfrStageKey,'COMP_BY','X') If ( (StageDestTest EQ True$) AND (StageCompSig NE '') ) then Database_Services('ActivateRecord', 'WO_WFR', InWfrID) If {TW_SAP_DTM} EQ '' then ProdTWQty += 1 {TW_SAP_DTM} = CurrDTM Database_Services('WriteDataRow', 'WO_WFR', InWfrID, @Record, True$, False$, True$) end end until Destroyed EQ True$ next SigProfileIndex end Next InWfrID next RDSKey end else Error_Services('Add', 'WO_MAT ':WOMatKey:' is not of reactor type GAN.') end end else Error_Services('Add', 'Null WO_MAT key supplied to ':Service:'.') end Response = GRWfrQty:@FM:ScrapQty:@FM:ProdTWQty end service //---------------------------------------------------------------------------------------------------------------------- // GetYieldInfo // // //---------------------------------------------------------------------------------------------------------------------- Service GetYieldInfo(RDSNo, WOMatKey) Response = '' If RDSNo NE '' then WOMatKey = Xlate('REACT_RUN', RDSNo, 'WO_MAT_KEY', 'X') If WOMatKey NE '' then WONo = Field(WOMatKey, '*', 1) WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey) If Error_Services('NoError') then If RDSNo EQ '' then RDSNo = WOMatRec GRWfrQty = 0 ScrapQty = 0 ProdTWQty = 0 DummyQty = 0 EmptyQty = 0 ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) GaNRunID = ReactRunRec Scribes = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X') GRWfrQty = DCount(Scribes, @VM) For each Scribe in Scribes using @VM setting vPos DummyID = GanRunID[1,7]:'D' EmptyID = GaNRunID[1,7]:'E' EmptyFlag = (Indexc(Scribe, 'EMPTY', 1) > 0) If Scribe[1,8] EQ DummyID then DummyQty += 1 end If ( (Scribe[1,8] EQ EmptyID) or (EmptyFlag EQ True$) ) then EmptyQty += 1 end Next Scribe GRWfrQty -= EmptyQty // The following NCR and TW counting logic is ran against all RDS, for that WorkOrder, // everytime a GaN cassette is FQA signed. SigProfile = ReactRunRec SigProfileCnt = DCount(SigProfile, @VM) // Count the number of NCRs NCRKeys = Xlate('REACT_RUN', RDSNo, 'NCR_NOS', 'X') If (NCRKeys NE '') then ScrapQty += SUM(XLATE('NCR', NCRKeys, 'REJ_CNT', 'X')) end // Count the number of Test Wafers InWfrIDs = ReactRunRec * If WONo EQ 169733 then ProdTWQty = Sum(ReactRunRec) * end else * For each InWfrID in InWfrIDs using @VM setting vPos * If InWfrID NE '' then * Scribe = Scribes<0, vPos> * ScrapID = GaNRunID[1,7]:'W' * If (Scribe[1, 8] EQ ScrapID) then * ProdTWQty += 1 * end else * For SigProfileIndex = 1 to SigProfileCnt * Destroyed = False$ * Stage = SigProfile<1, SigProfileIndex> * WfrKID = InWfrID * Convert '*' to '.' in WfrKID * WfrStageKey = RDSNo:'*':Stage:'*':WfrKID * * StageDestTest = XLATE('RUN_STAGE_WFR',WfrStageKey,'DEST_TEST','X') * StageCompSig = XLATE('RUN_STAGE_WFR',WfrStageKey,'COMP_BY','X') * StageStatus = Xlate('RUN_STAGE_WFR',WfrStageKey,'STATUS','X') * StageSkipped = StageStatus EQ 'SKIP' * If ( (StageDestTest EQ True$) AND (StageCompSig NE '') and (StageSkipped EQ False$) ) then * ProdTWQty += 1 * Destroyed = True$ * end * until Destroyed EQ True$ * next SigProfileIndex * end * end * Next InWfrID * end * * LogData = '' * LogData<1> = LoggingDTM * LogData<2> = RDSNo * LogData<3> = GRWfrQty * LogData<4> = ScrapQty * LogData<5> = ProdTWQty * LogData<6> = DummyQty * Logging_Services('AppendLog', objLog, LogData, @RM, @FM) Response = GRWfrQty:@FM:ScrapQty:@FM:ProdTWQty:@FM:DummyQty end else Error_Services('Add', 'Error reading WO_MAT record in ':Service:'.') end end else Error_Services('Add', 'Error reading WO_MAT_KEY from REACT_RUN table in ':Service:'.') end end service //---------------------------------------------------------------------------------------------------------------------- // GetRunInfo // // Input: // GaNRunID - [Required] // // Output: // Returns the following information from the Wafer Track database: // 1 - GaN Run/Etch ID Recipe // 2 - GaN Susc Serial Number // 3 - Pocket Numbers // 4 - Wafer Scribes // 5 - Sattelite Serial Numbers // 6 - Part Number // 7 - Reactor Number // Output Format: // Rows are @FM delimited, Columns are @VM delimited // // Note: // This service cannot run directly on the App server due to the fact that the Wafer Track database // is within the EC domain. // //---------------------------------------------------------------------------------------------------------------------- Service GetRunInfo(GaNRunID) If GaNRunID NE '' then Query = 'Declare @RunNumber varchar(50) ' | : "Set @RunNumber = '":GaNRunID:"' " | : 'SELECT ' | : '[Recipe] ' | : ',[Platter S/N] ' | : ',[Pocket_Number] ' | : ',[Wafer_Lot] ' | : ',[Satellite S/N] ' | : ',[Part Number] ' | : 'FROM [G4Wafers_01].[dbo].[Prerun Info] ' | : 'WHERE [Run Number] = @RunNumber ' | : 'ORDER BY [Pocket_Number]' Done = False$ MaxNumAttempts = 5 LoopIndex = 0 Loop LoopIndex += 1 Response = '' GaNRunData = SQL_Services('PostSQLRequest', 'IQSDMS1', Query) If Error_Services('NoError') then Convert @FM to @VM in GaNRunData Convert @RM to @FM in GaNRunData NumRows = DCount(GaNRunData, @FM) For RowIndex = 1 to NumRows GaNRunData = GaNRunID[1, 2] Next RowIndex GaNRunData = SRP_Array('Rotate', GaNRunData, @FM, @VM) Response = GaNRunData PocketNos = Response<3> NumWfrs = DCount(PocketNos, @VM) If NumWfrs GT 4 then Done = True$ If ( (PocketNos EQ 0) or (PocketNos EQ '') ) then // Log error and hardcode the pockets LogData = '' LogData<1> = LoggingDTM LogData<2> = GaNRunID Convert @VM to ' ' in PocketNos LogData<3> = PocketNos Logging_Services('AppendLog', objPocketLog, LogData, @RM, @FM) Scribes = Response<4> For each Scribe in Scribes using @VM setting vPos PocketNos<0, vPos> = Fmt(vPos,"R(0)#2") Next Scribe Response<3> = PocketNos end end else Done = True$ Response = Error_Services('GetMessage') end Until ( (Done EQ True$) or (LoopIndex EQ MaxNumAttempts) ) Repeat end end service //---------------------------------------------------------------------------------------------------------------------- // GetToolList // // Output: // Returns a @FM delimited array of GaN related tools. // //---------------------------------------------------------------------------------------------------------------------- Service GetToolList(ReactorFlag) Response = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_TOOL_LIST') end service //---------------------------------------------------------------------------------------------------------------------- // GetAvailableRunIDs // // Output: // Returns a @FM delimited list of GaN Run IDs, which have not yet been bound to an RDS. // //---------------------------------------------------------------------------------------------------------------------- Service GetAvailableRunIDs(Reactor, WorkOrder) Response = '' AllRunIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_GAN_RUN_IDS') For each RunID in AllRunIDs using @FM setting fPos Convert @Lower.Case to @Upper.Case in RunID If RunID[1, 2] EQ Reactor then If WorkOrder NE '' then GaNSchedWorkOrder = Xlate('GAN_SCHEDULE', RunID, 'WORK_ORDER', 'X') If WorkOrder EQ GaNSchedWorkOrder then Convert @Upper.Case to @Lower.Case in RunID Response<-1> = RunID end end else Convert @Upper.Case to @Lower.Case in RunID Response<-1> = RunID end end Next RunID end service //---------------------------------------------------------------------------------------------------------------------- // GetAvailableEtchIDs // // Output: // Returns a @FM delimited list of GaN Etch IDs, which have not yet been bound to an RDS. // //---------------------------------------------------------------------------------------------------------------------- Service GetAvailableEtchIDs(Reactor) Response = '' AllEtchIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS') For each EtchID in AllEtchIDs using @FM setting fPos If EtchID[1, 2] EQ Reactor then Response<-1> = EtchID end Next EtchID end service //---------------------------------------------------------------------------------------------------------------------- // ConsumeExtEtchID // // Input: ExtEtchID [Required] // // Output: // True$ (1) if successful, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service ConsumeExtEtchID(ExtEtchID) Reponse = False$ ElapTime = 0 If ExtEtchID NE '' then RequestTime = Time() Loop ElapTime = Time() - RequestTime HaveLock = Database_Services('GetKeyIDLock', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS') Until HaveLock or ElapTime GT 5 Repeat If HaveLock then AvailableEtchIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS') Locate ExtEtchID in AvailableEtchIDs using @FM setting fPos then AvailableEtchIDs = Delete(AvailableEtchIDs, fPos, 0, 0) Database_Services('WriteDataRow', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS', AvailableEtchIDs, True$, False$, True$) Reponse = True$ ConsumedEtchIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_ETCH_IDS_CONSUMED') // Add this Etch ID to the consumed Etch ID list to use for filtering the combo box on the REACT_RUN // form. Locate ExtEtchID in ConsumedEtchIDs using @FM setting cPos else ConsumedEtchIDs<-1> = ExtEtchID ConsumedEtchIDs = SRP_Array('SortRows', ConsumedEtchIDs, 'AR1', 'LIST') Database_Services('WriteDataRow', 'APP_INFO', 'PRERUN_ETCH_IDS_CONSUMED', ConsumedEtchIDs, True$, False$, True$) end end Database_Services('ReleaseKeyIDLock', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS') end end end service //---------------------------------------------------------------------------------------------------------------------- // RemoveRunID // // Input: RunID [Required] // // Output: // True$ (1) if successful, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service RemoveRunID(RunID) Reponse = False$ If RunID NE '' then RequestTime = Time() Loop ElapTime = Time() - RequestTime HaveLock = Database_Services('GetKeyIDLock', 'APP_INFO', 'PRERUN_GAN_RUN_IDS', True$) Until HaveLock or ElapTime GT 5 Repeat If HaveLock then AvailableRunIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_GAN_RUN_IDS') Locate RunID in AvailableRunIDs using @FM setting fPos then AvailableRunIDs = Delete(AvailableRunIDs, fPos, 0, 0) Database_Services('WriteDataRow', 'APP_INFO', 'PRERUN_GAN_RUN_IDS', AvailableRunIDs, True$, False$, False$) Response = True$ end Database_Services('ReleaseKeyIDLock', 'APP_INFO', 'PRERUN_GAN_RUN_IDS') ConsumedRunIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_RUN_IDS_CONSUMED') // Add this Run ID to the consumed Run ID list to use for filtering the combo box on the REACT_RUN form. Locate RunID in ConsumedRunIDs using @FM setting cPos else ConsumedRunIDs<-1> = RunID ConsumedRunIDs = SRP_Array('SortSimpleList', ConsumedRunIDs) Database_Services('WriteDataRow', 'APP_INFO', 'PRERUN_RUN_IDS_CONSUMED', ConsumedRunIDs, True$, False$, True$) end end If ElapTime GT 5 then Error_Services('Add', 'Error removing Run ID ':RunID: ' from PRERUN_GAN_RUN_IDS') LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = EtchID Machine = Environment_Services('GetServer') If Machine NE 'MESSA01EC' then EmailAddr = 'dstieber@srpcs.com,Dan.Crisp@infineon.com,4805890050@vtext.com,6613649828@txt.att.net' EmailMsg = 'Error removing Run ID: ':RunID:' from APP_INFO*PRERUN_GAN_RUN_IDS' Logging_Services('AppendLog', ObjLog, LogData, @RM, @FM, False$, EmailAddr, EmailMsg) end else Logging_Services('AppendLog', ObjLog, LogData, @RM, @FM, False$) end end end end service //---------------------------------------------------------------------------------------------------------------------- // UpdateAvailableRunIDs // // //---------------------------------------------------------------------------------------------------------------------- Service UpdateAvailableRunIDs() hSysLists = Database_Services('GetTableHandle', 'SYSLISTS') Lock hSysLists, ServiceKeyID then SearchDTM = OConv( (DateTime() - 2) , 'DT') Response = '' WaferTrackDB = Environment_Services('GetWaferTrackProductionPath') Query = 'Declare @RunDTM varchar(50) ' | : "Set @RunDTM = '":SearchDTM:"' " | : 'SELECT DISTINCT ' | : '[Run Number] ' | : 'FROM [G4Wafers_01].[dbo].[Prerun Info] ' | : "WHERE [Date] >= @RunDTM and ([Run Number] like '%mvfet' or [Run Number] like '%hvfet') " | : "ORDER BY [Run Number] " NewGaNRunIDs = SQL_Services('GetDataRows', 'IQSDMS1', Query, 10) Convert @FM to @VM in NewGaNRunIDs Convert @RM to @FM in NewGanRunIDs CurrGaNRunIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_GAN_RUN_IDS') HaveLock = Database_Services('GetKeyIDLock', 'APP_INFO', 'PRERUN_GAN_RUN_IDS', True$) ConsumedRunIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_RUN_IDS_CONSUMED') NeedToWrite = False$ If HaveLock EQ True$ then // OK to update If (NewGaNRunIDs NE '') and (NewGaNRunIDs NE 0) then // Merge new and current list ensuring unique values For each GaNRunID in NewGaNRunIDs using @FM setting gPos Locate GaNRunID in CurrGaNRunIDs using @FM setting fPos else // Check if etch ID has already been consumed. Only add the etch ID to the available list if // it has not yet been consumed. Locate GaNRunID in ConsumedRunIDs using @FM setting cPos else CurrGaNRunIDs<-1> = GaNRunID end end Next GaNRunID // Sort list to ensure list is sorted CurrGaNRunIDs = SRP_Array('SortRows', CurrGanRunIDs, 'AL1', 'LIST', @FM, '') Database_Services('WriteDataRow', 'APP_INFO', 'PRERUN_GAN_RUN_IDS', CurrGaNRunIDs, True$, False$, True$) end Database_Services('ReleaseKeyIDLock', 'APP_INFO', 'PRERUN_GAN_RUN_IDS') end Unlock hSysLists, ServiceKeyID else Null end end service //---------------------------------------------------------------------------------------------------------------------- // GetReactor // // //---------------------------------------------------------------------------------------------------------------------- Service GetReactor(WorkOrderNo) Reactor = '' If (WorkOrderNo NE '') then If RowExists('WO_LOG', WorkOrderNo) then PVDesc = Xlate('WO_LOG', WorkOrderNo, 'PROD_VER_DESC', 'X') Begin Case Case Indexc(PVDesc, 'G5+', 1) Reactor = 69 Case Indexc(PVDesc, 'G5', 1) Reactor = 71 Case Otherwise$ Error_Services('Add', 'Error in ':Service:'. PROD_VER_DESC does not match any GaN reactors.') End Case end else Error_Services('Add', 'Error in ':Service:'. WO_LOG record ':WorkOrderNo:' does not exist.') end end Response = Reactor end service //---------------------------------------------------------------------------------------------------------------------- // UpdateAvailableEtchIDs // // //---------------------------------------------------------------------------------------------------------------------- Service UpdateAvailableEtchIDs() hSysLists = Database_Services('GetTableHandle', 'SYSLISTS') Lock hSysLists, ServiceKeyID then SearchDTM = OConv( (DateTime() - 1) , 'DT') Response = '' WaferTrackDB = Environment_Services('GetWaferTrackProductionPath') Query = 'Declare @RunDTM varchar(50) ' | : "Set @RunDTM = '":SearchDTM:"' " | : 'SELECT DISTINCT ' | : '[Run Number] ' | : 'FROM [G4Wafers_01].[dbo].[Prerun Info] ' | : "WHERE [Date] > @RunDTM and [Run Number] like '%etch' " | : "ORDER BY [Run Number] " NewGaNEtchIDs = SQL_Services('GetDataRows', 'IQSDMS1', Query) Convert @FM to @VM in NewGaNEtchIDs Convert @RM to @FM in NewGanEtchIDs CurrGaNEtchIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS') HaveLock = Database_Services('GetKeyIDLock', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS', True$) ConsumedEtchIDs = Database_Services('ReadDataRow', 'APP_INFO', 'PRERUN_ETCH_IDS_CONSUMED') NeedToWrite = False$ If HaveLock EQ True$ then // OK to update If (NewGaNEtchIDs NE '') and (NewGaNEtchIDs NE 0) then // Merge new and current list ensuring unique values For each GaNEtchID in NewGaNEtchIDs using @FM setting gPos Locate GaNEtchID in CurrGaNEtchIDs using @FM setting fPos else // Check if etch ID has already been consumed. Only add the etch ID to the available list if // it has not yet been consumed. Locate GaNEtchID in ConsumedEtchIDs using @FM setting cPos else CurrGaNEtchIDs<-1> = GaNEtchID end end Next GaNEtchID // Sort list to ensure list is sorted CurrGaNEtchIDs = SRP_Array('SortRows', CurrGanEtchIDs, 'AL1', 'LIST', @FM, '') Database_Services('WriteDataRow', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS', CurrGaNEtchIDs, True$, False$, True$) end Database_Services('ReleaseKeyIDLock', 'APP_INFO', 'PRERUN_GAN_ETCH_IDS') end Unlock hSysLists, ServiceKeyID else Null end end service //---------------------------------------------------------------------------------------------------------------------- // UpdateGaNScheduleInfo // // //---------------------------------------------------------------------------------------------------------------------- Service UpdateGaNScheduleInfo() hSysLists = Database_Services('GetTableHandle', 'SYSLISTS') Lock hSysLists, ServiceKeyID then GanSchedRec = '' GaNReactors = '' Query = "SELECT REACTOR WITH REACT_TYPE EQ 'GAN'" GoSub ClearCursors Rlist(Query, Target_ActiveList$, '', '', '') EOF = False$ If @RecCount then Loop Until EOF EQ True$ ReadNext ReactorID else EOF = True$ Locate ReactorID in GaNReactors using @FM setting rPos else GaNReactors<-1> = ReactorID end Repeat end GoSub ClearCursors SchedPath = Environment_Services('GetGaNSchedulePath') // GaN Reactor Scheduler Excel Spreadsheet --------------------- For each Reactor in GaNReactors using @FM setting fPos SchedFilename = 'R':Reactor:' Growth Schedule.xls' SchedFilePath = SchedPath:'\':SchedFilename SchedExcelHandle = Excel_Services('OpenDocument', SchedFilePath) If Error_Services('NoError') then SchedWorksheet = 'Schedule' NumExcelRows = Excel_Services('GetNumRows', SchedExcelHandle, SchedWorksheet) For Row = (NumExcelRows - 500) to NumExcelRows ExcelRunID = Excel_Services('GetCellValue', SchedExcelHandle, SchedWorksheet, 'E', Row) ValidRunID = (ExcelRunID[1,2] EQ Reactor) If ValidRunID EQ True$ then GaNSchedKey = ExcelRunID // WT = WaferTrack ExcelWTPart = Excel_Services('GetCellValue', SchedExcelHandle, SchedWorksheet, 'H', Row) ExcelOIPart = Excel_Services('GetCellValue', SchedExcelHandle, SchedWorksheet, 'I', Row) ExcelRecipe = Excel_Services('GetCellValue', SchedExcelHandle, SchedWorksheet, 'L', Row) ExcelWorkOrder = Excel_Services('GetCellValue', SchedExcelHandle, SchedWorkSheet, 'G', Row) If RowExists('GAN_SCHEDULE', GaNSchedKey) then // Row exists, check if recipe has been changed. GaNSchedRec = Database_Services('ReadDataRow', 'GAN_SCHEDULE', GanSchedKey) GaNWTPart = GanSchedRec GaNOIPart = GanSchedRec GaNRecipe = GanSchedRec GaNWorkOrder = GanSchedRec If (GaNWTPart NE ExcelWTPart) or (GaNOIPart NE ExcelOIPart) or (GaNRecipe NE ExcelRecipe) or (GaNWorkOrder NE ExcelWorkOrder) then // GaN Schedule updated, update OpenInsight GanSchedRec = ExcelWTPart GanSchedRec = ExcelOIPart GaNSchedRec = ExcelRecipe GaNSchedRec = ExcelWorkOrder Database_Services('WriteDataRow','GAN_SCHEDULE',GaNSchedKey,GanSchedRec,True$,False$,True$) end end else // Row does not yet exist, so create it. GanSchedRec = ExcelRecipe GanSchedRec = ExcelWTPart GanSchedRec = ExcelOIPart GanSchedRec = ExcelWorkOrder Database_Services('WriteDataRow','GAN_SCHEDULE',GaNSchedKey,GanSchedRec,True$,False$,True$) end end Next Row Excel_Services('CloseDocument', SchedExcelHandle) end Next Reactor Unlock hSysLists, ServiceKeyID else Null end end service //---------------------------------------------------------------------------------------------------------------------- // UpdateDisposition // // This service contains automatic disposition logic for engineering lots. // //---------------------------------------------------------------------------------------------------------------------- Service UpdateDisposition(RDSNo) CurrDTM = DateTime() ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) OrigReactRunRec = ReactRunRec InWfrIDs = ReactRunRec ShipFlags = ReactRunRec RetainFlags = ReactRunRec InternalFlags = ReactRunRec ExternalFlags = ReactRunRec CassDispComp = ReactRunRec WfrsDispReady = ReactRunRec NCRReqFlags = ReactRunRec CharFlags = ReactRunRec NumWfrs = DCount(InWfrIDs, @VM) For WfrIndex = 1 to NumWfrs WfrID = InWfrIDs<0, WfrIndex> WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) OrigWOWfrRec = WOWfrRec ShipFlag = ShipFlags<0, WfrIndex> RetainFlag = RetainFlags<0, WfrIndex> InternalFlag = InternalFlags<0, WfrIndex> ExternalFlag = ExternalFlags<0, WfrIndex> CharFlag = CharFlags<0, WfrIndex> DispReady = WfrsDispReady<0, WfrIndex> NCRNo = WOWfrRec NCRStatus = Xlate('NCR', NCRNo, 'STATUS', 'X') CriticalFailFlag = WOWfrRec NCRReq = NCRReqFlags<0, WfrIndex> DestStatus = GaN_Services('GetDestroyedStatus', WfrID) If ( (CharFlag EQ 'DUMMY') or (CharFlag EQ 'EMPTY') ) then DispReady = True$ WfrsDispReady<0, WfrIndex> = DispReady ReactRunRec = WfrsDispReady WOWfrRec = 'Ready to close' end If DestStatus EQ True$ then // Mark wafer's internal flag. Clear all other flags. If ( (InternalFlag EQ False$) or (InternalFlag EQ '') ) then ShipFlag = False$ RetainFlag = False$ InternalFlag = True$ ExternalFlag = False$ ShipFlags<0, WfrIndex> = ShipFlag RetainFlags<0, WfrIndex> = RetainFlag InternalFlags<0, WfrIndex> = InternalFlag ExternalFlags<0, WfrIndex> = ExternalFlag ReactRunRec = ShipFlags ReactRunRec = RetainFlags ReactRunRec = InternalFlags ReactRunRec = ExternalFlags end end MetStatus = GaN_Services('GetMetrologyStatus', WfrID) DispStatus = GaN_Services('GetDispStatus', WfrID) Begin Case Case CriticalFailFlag EQ True$ // Bow and/or warp critical limit exceeded, so the wafer must be // destroyed to prevent damage to candela tools Reason = 'Bow and/or warp critical limit exceeded' Begin Case Case ( (NCRNo NE '') and (NCRStatus EQ 'C') ) WOWfrRec = 'Ready to close' Case ( (NCRNo NE '') and (NCRStatus NE 'C') ) WOWfrRec = 'NCR must be closed' Case NCRNo EQ '' WOWfrRec = 'Wafer requires an NCR' End Case DispReady = ( (NCRNo NE '') and (NCRStatus EQ 'C') ) WfrsDispReady<0, WfrIndex> = DispReady ReactRunRec = WfrsDispReady Case ( (MetStatus EQ True$) or (DispStatus EQ True$) ) // Check Ship, Interal, External, and Retain conditions to determine if wafer should be removed from the WIP FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID Begin Case Case ShipFlag EQ True$ GPackQ = GaN_Services('GetLocQueueID', 'G_PACK') ShipID = ReactRunRec RunStageWfrKey = RDSNo:'*DISP*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec If StageStatus _EQC 'INIT' then // Mark DISP stage as complete RunStageWfrRec = @USER4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end Begin Case Case (ShipID EQ '') LocQ = GaN_Services('GetWfrQueue', WfrID) WOWfrRec = 'Wafer needs to be placed in an outbound cassette' If LocQ _EQC GPackQ then // Wafer is being removed from an outbound cassette, so place the wafer back into the G_PACK // queue and reset the G_PACK RUN_STAGE_WFR record to 'INIT'. RunStageWfrKey = RDSNo:'*G_PACK*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec If StageStatus _EQC 'COMP' then RunStageWfrRec = '' RunStageWfrRec = '' RunStageWfrRec = 'INIT' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) Gan_Services('MoveWfrToQueue', WfrID, GPackQ) end end else // Wafer has been marked as shipped in the Disposition Report (Excel spreadsheet), but has // not yet been added to an outbound cassette. Remove the wafer from its current location // (which likely is DISP), then place the wafer in the G_PACK location queue and update // the wafer trace (WO_WFR record). Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, GPackQ) // Update wafer trace (WO_WFR record) LocDTM = WOWfrRec NextPos = DCount(LocDTM, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'COMP' WOWfrRec = '' WOWfrRec = GPackQ end Case ( (ShipID NE '') and ( (CassDispComp EQ False$) or (CassDispComp EQ '') ) ) // Wafer is ready for disposition, so mark it as such. Once user marks the cassette // disposition complete, then the wafer will move off the WIP. Move wafer to the G_PACK // location queue if it is not there and update the wafer trace (WO_WFR record). If (NCRReq EQ False$) or ( (NCRReq EQ True$) and (NCRNo NE '') ) then LocQ = Gan_Services('GetWfrQueue', WfrID) Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, GPackQ) // Update wafer trace (WO_WFR record) LocDTM = WOWfrRec NextPos = DCount(LocDTM, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'G_PACK' WOWfrRec = '' WOWfrRec = GPackQ WOWfrRec = 'Ready to close' DispReady = True$ WfrsDispReady<0, WfrIndex> = DispReady ReactRunRec = WfrsDispReady end Case ( (ShipID NE '') and (CassDispComp EQ True$) ) // A ship ID has been added (i.e. the wafer has been placed in an outbound cassette and the // user has marked the cassette disposition as complete, so remove the wafer from the // GaN WIP and mark the G_PACK stage as complete.) RunStageWfrKey = RDSNo:'*G_PACK*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec LocQ = Gan_Services('GetWfrQueue', WfrID) WOWfrRec = 'Run closed' Gan_Services('RemoveWfrFromWIP', WfrID) If StageStatus _EQC 'INIT' then // Mark G_PACK stage as complete RunStageWfrRec = @USER4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end Case Otherwise$ // No action necessary at this time. Null End Case Case RetainFlag EQ True$ RetainSlot = WOWfrRec RetainBox = WOWfrRec RetainSig = WOWfrRec RetainQ = GaN_Services('GetLocQueueID', 'RETAIN') RunStageWfrKey = RDSNo:'*DISP*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec If StageStatus _EQC 'INIT' then // Retain flag has been set and metrology is complete, so mark DISP stage as complete. RunStageWfrRec = @User4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end Begin Case Case ( (RetainSlot EQ '') or (RetainBox EQ '') ) // Wafer needs to go into RETAIN queue until it receives a Retain Slot and Retain Box // If wafer is not at RETAIN, then update wafer trace (WO_WFR record) and move the wafer. WOWfrRec = 'Wafer requires a retain box and retain slot' CurrStage = GaN_Services('GetCurrStage', WfrID) LocQ = GaN_Services('GetWfrQueue', WfrID) Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, RetainQ) If CurrStage NE 'RETAIN' then LocDTM = WOWfrRec NextPos = DCount(LocDTM, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'COMP' WOWfrRec = '' WOWfrRec = RetainQ end Case ( (RetainSlot NE '') and (RetainBox NE '') and (CassDispComp NE True$) ) // Wafer is ready for disposition, so mark it as such. Once user marks the cassette // disposition complete, then the wafer will move off the WIP. Move wafer into RETAIN stage // if it is not currently there. This can occur if an RDS is opened after being closed. If ( ( (NCRReq EQ False$) or ( (NCRReq EQ True$) and (NCRNo NE '') ) ) and (RetainSig NE '') ) then WOWfrRec = 'Ready to close' DispReady = True$ end else WOWfrRec = 'Wafer has not yet been retained' DispReady = False$ end RetainQ = 'GCH*Q_RETAIN' LocQ = Gan_Services('GetWfrQueue', WfrID) Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, RetainQ) LocDTM = WOWfrRec NextPos = DCount(LocDTM, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'RETAIN' WOWfrRec = '' WOWfrRec = RetainQ WfrsDispReady<0, WfrIndex> = DispReady ReactRunRec = WfrsDispReady Case ( ( (RetainSlot NE '') and (RetainBox NE '') and (RetainSig NE '') ) and (CassDispComp EQ True$) ) // Retain slot and retain box are populated and the user has marked disposition complete -> // mark RETAIN stage complete and remove the wafer from the GaN WIP. RunStageWfrKey = RDSNo:'*RETAIN*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec LocQ = Gan_Services('GetWfrQueue', WfrID) WOWfrRec = 'Run closed' Gan_Services('RemoveWfrFromWIP', WfrID) If StageStatus _EQC 'INIT' then RunStageWfrRec = @User4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end Case Otherwise$ // No action necessary at this time. Null End Case Case InternalFlag EQ True$ Begin Case Case ( (CassDispComp EQ False$) or (CassDispComp EQ '') ) // Wafer is ready for disposition, so mark it as such. Once user marks the cassette // disposition complete, then the wafer will move off the WIP. DispReady = True$ WfrsDispReady<0, WfrIndex> = DispReady ReactRunRec = WfrsDispReady WOWfrRec = 'Ready to close' Case CassDispComp EQ True$ // Remove the wafer from the GaN WIP and mark DISP stage as complete. RunStageWfrKey = RDSNo:'*DISP*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec LocQ = GaN_Services('GetWfrQueue', WfrID) WOWfrRec = 'Run closed' Gan_Services('RemoveWfrFromWIP', WfrID) If StageStatus _EQC 'INIT' then // Mark DISP stage as complete RunStageWfrRec = @User4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end End Case Case ExternalFlag EQ True$ Begin Case Case ( (CassDispComp EQ False$) or (CassDispComp EQ '') ) // Check to see if an NCR has been added to this wafer before marking this wafer as ready // to be dispositioned. If ( (NCRReq EQ False$) or ( (NCRReq EQ True$) and (NCRNo NE '') ) ) then // Wafer is ready for disposition, so mark it as such. Once user marks the cassette // disposition complete, then the wafer will move off the WIP. DispReady = True$ WfrsDispReady<0, WfrIndex> = DispReady ReactRunRec = WfrsDispReady WOWfrRec = 'Ready to close' end Case CassDispComp EQ True$ // Remove the wafer from the GaN WIP and mark DISP stage as complete. RunStageWfrKey = RDSNo:'*DISP*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec WOWfrRec = 'Run closed' LocQ = Gan_Services('GetWfrQueue', WfrID) Gan_Services('RemoveWfrFromWIP', WfrID) If StageStatus _EQC 'INIT' then // Mark DISP stage as complete RunStageWfrRec = @User4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end End Case Case Otherwise$ // All flags false. Lot aborted or disposition report not found. Begin Case Case ( (CassDispComp EQ False$) or (CassDispComp EQ '') ) // Move wafer to DISP queue if it is not there DispQ = 'GGR*Q_DISP' CurrQ = Gan_Services('GetWfrQueue', WfrID) If CurrQ _NEC DispQ then Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, DispQ) end // Check to see if an NCR has been added to this wafer before marking this wafer as ready // to be dispositioned. If ( (NCRReq EQ False$) or ( (NCRReq EQ True$) and (NCRNo NE '') ) ) then // Wafer is ready for disposition, so mark it as such. Once user marks the cassette // disposition complete, then the wafer will move off the WIP. DispReady = True$ WfrsDispReady<0, WfrIndex> = DispReady ReactRunRec = WfrsDispReady WOWfrRec = 'Ready to close' end else If NCRReq EQ True$ then WOWfrRec = 'NCR required' end else WOWfrRec = 'Disposition report not found' end end Case CassDispComp EQ True$ // Remove the wafer from the GaN WIP and mark DISP stage as complete. RunStageWfrKey = RDSNo:'*DISP*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec WOWfrRec = 'Run closed' LocQ = GaN_Services('GetWfrQueue', WfrID) Gan_Services('RemoveWfrFromWIP', WfrID) If StageStatus _EQC 'INIT' then // Mark DISP stage as complete RunStageWfrRec = @User4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end End Case End Case Case Otherwise$ // Metrology or Disposition not complete WOWfrRec = 'Metrology incomplete' End Case If OrigWOWfrRec NE WOWfrRec then Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) Next WfrIndex If OrigReactRunRec NE ReactRunRec then Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end service //---------------------------------------------------------------------------------------------------------------------- // GetStageDesc // // Input: // StageID - [Required] // // Output: // Returns the GaN stage description associated with the StageID passed in. // //---------------------------------------------------------------------------------------------------------------------- Service GetStageDesc(StageID) Response = '' Done = False$ GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') For each StageRow in GanStageInfo using @FM setting fPos Locate StageID in StageRow using @VM setting vPos then StageDesc = StageRow<0, 2> Response = StageDesc Done = True$ end Until Done EQ True$ Next StageRow end service //---------------------------------------------------------------------------------------------------------------------- // GetStageID // // Input: // StageDesc - [Required] // // Output: // Returns the GaN stage ID associated with the stage description passed in. // //---------------------------------------------------------------------------------------------------------------------- Service GetStageID(StageDesc, StageQ, ToolClass) Response = '' If ( (StageDesc NE '') or (StageQ NE '') or (ToolClass NE '') )then Done = False$ GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') For each StageRow in GanStageInfo using @FM setting fPos Begin Case Case StageDesc NE '' Locate StageDesc in StageRow using @VM setting vPos then StageID = StageRow<0, 1> Response = StageID Done = True$ end Case StageQ NE '' Locate StageQ in StageRow using @VM setting vPos then StageID = StageRow<0, 1> Response = StageID Done = True$ end Case ToolClass NE '' Locate ToolClass in StageRow using @VM setting vPos then StageID = StageRow<0, 1> Response = StageID Done = True$ end End Case Until Done EQ True$ Next StageRow end end service //---------------------------------------------------------------------------------------------------------------------- // GetStageIndex // // Input: // StageID - [Required] // or // StageDesc - [Required] // // Output: // Returns the GaN stage index associated with the stage ID/description passed in. // //---------------------------------------------------------------------------------------------------------------------- Service GetStageIndex(StageID, StageDesc) Response = '' If (StageID NE '') or (StageDesc NE '') then If StageID NE '' then StageKey = StageID end else StageKey = StageDesc end Done = False$ GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') For each StageRow in GanStageInfo using @FM setting fPos Locate StageKey in StageRow using @VM setting vPos then StageIndex = fPos Response = StageIndex Done = True$ end Until Done EQ True$ Next StageRow end end service //---------------------------------------------------------------------------------------------------------------------- // GetRunType // // Input: // StageID - [Required] // or // StageDesc - [Required] // // Output: // Returns the GaN run type (i.e. CASS or WFR) associated with the given stage description/ID. // //---------------------------------------------------------------------------------------------------------------------- Service GetRunType(StageID, StageDesc) Response = '' Done = False$ GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') Begin Case Case StageID NE '' For each StageRow in GanStageInfo using @FM setting fPos Locate StageID in StageRow using @VM setting vPos then RunType = StageRow<0, 4> Response = RunType Done = True$ end Until Done EQ True$ Next StageRow Case StageDesc NE '' For each StageRow in GanStageInfo using @FM setting fPos Locate StageDesc in StageRow using @VM setting vPos then RunType = StageRow<0, 4> Response = RunType Done = True$ end Until Done EQ True$ Next StageRow End Case end service //---------------------------------------------------------------------------------------------------------------------- // GetStageType // // Input: // StageID - [Required] // or // StageDesc - [Required] // // Output: // Returns the GaN stage type (i.e. Reactor, Metrology, or Disposition) associated with the given stage // description/ID. // //---------------------------------------------------------------------------------------------------------------------- Service GetStageType(StageID, StageDesc) Response = '' Done = False$ GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') Begin Case Case StageID NE '' For each StageRow in GanStageInfo using @FM setting fPos Locate StageID in StageRow using @VM setting vPos then StageType = StageRow<0, 5> Response = StageType Done = True$ end Until Done EQ True$ Next StageRow Case StageDesc NE '' For each StageRow in GanStageInfo using @FM setting fPos Locate StageDesc in StageRow using @VM setting vPos then StageType = StageRow<0, 5> Response = StageType Done = True$ end Until Done EQ True$ Next StageRow End Case end service //---------------------------------------------------------------------------------------------------------------------- // GetCassStages // // Input: // ReactRunID - [Required] // // Output: // Response - @VM delimited array of GaN cassette-level stages associated with the given REACT_RUN ID. // //---------------------------------------------------------------------------------------------------------------------- Service GetCassStages(ReactRunID) Response = '' PSNo = Xlate('REACT_RUN', ReactRunID, 'PS_NO', 'X') If PSNo NE '' then GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') PRSStageKeys = Xlate('PROD_SPEC', PSNo, 'PRS_STAGE_KEY', 'X') CassStages = '' For each PRSStageKey in PRSStageKeys using @VM StageID = Field(PRSStageKey, '*', 2) Done = False$ For each GaNStageRow in GaNStageInfo using @FM setting fPos Locate StageID in GaNStageRow using @VM setting vPos then RunType = GaNStageRow<0, 4> If RunType EQ 'CASS' then CassStages<1, fPos> = StageID Done = True$ end end Until Done EQ True$ Next GaNStageRow Next PRSStageKey Convert @VM to ' ' in CassStages CassStages = Trim(CassStages) Convert ' ' to @VM in CassStages Response = CassStages end end service //---------------------------------------------------------------------------------------------------------------------- // GetWfrStages // // Input: // ReactRunID - [Required] // // Output: // Response - @VM delimited array of GaN wafer-level stages associated with the given REACT_RUN ID. // //---------------------------------------------------------------------------------------------------------------------- Service GetWfrStages(ReactRunID) Response = '' PSNo = Xlate('REACT_RUN', ReactRunID, 'PS_NO', 'X') If PSNo NE '' then GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') PRSStageKeys = Xlate('PROD_SPEC', PSNo, 'PRS_STAGE_KEY', 'X') WfrStages = '' For each PRSStageKey in PRSStageKeys using @VM StageID = Field(PRSStageKey, '*', 2) Done = False$ For each GaNStageRow in GaNStageInfo using @FM setting fPos Locate StageID in GaNStageRow using @VM setting vPos then RunType = GaNStageRow<0, 4> If RunType EQ 'WFR' then WfrStages<1, fPos> = StageID Done = True$ end end Until Done EQ True$ Next GaNStageRow Next PRSStageKey Convert @VM to ' ' in WfrStages WfrStages = Trim(WfrStages) Convert ' ' to @VM in WfrStages Response = WfrStages end end service //---------------------------------------------------------------------------------------------------------------------- // GetGaNStages // // //---------------------------------------------------------------------------------------------------------------------- Service GetGaNStages(StageType=STAGETYPE, PSNo) If StageType NE '' then StageOrderKeys = '' If PSNo NE '' then StageOrderKeys = Xlate('PROD_SPEC', PSNo, 'PRS_STAGE_ROUTE', 'X') If StageOrderKeys EQ '' then // Use default stage configuration because no route is defined in the PSN GaNStageArray = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') For each Row in GaNStageArray using @FM setting RowIndex RunType = Row<0, 5> If RunType EQ StageType then Response<0, -1> = Row<0, 1> Next Row end else StageOrderKeys = SRP_Array('Rotate', StageOrderKeys, @VM, '*') StageOrderList = StageOrderKeys<0, 1> Swap '*' with @VM in StageOrderList For each StageID in StageOrderList using @VM ThisStageType = GaN_Services('GetStageType', StageID) If ThisStageType EQ StageType then Response<0, -1> = StageID Next StageID end end end service //---------------------------------------------------------------------------------------------------------------------- // CreateEtchRun // // //---------------------------------------------------------------------------------------------------------------------- Service CreateEtchRun(Reactor) // Place Etch ID into GaN WIP under LOCATION*GCH*Q_ETCH CurrDTM = DateTime() LocID = 'GCH*Q_ETCH' EtchID = NextKey('GAN_ETCH') ToolID = 'R':Reactor GanEtchRec = '' GanEtchRec = Reactor GanEtchRec = @USER4 GanEtchRec = True$ GanEtchRec = CurrDTM GanEtchRec = @User4 GanEtchRec = 'CREATE' GanEtchRec = ToolID GanEtchRec = LocID Database_Services('WriteDataRow', 'GAN_ETCH', EtchID, GanEtchRec, True$, False$, True$) LocRec = Database_Services('ReadDataRow', 'LOCATION', LocID) LocQ = LocRec DisplayEtchID = Xlate('GAN_ETCH', EtchID, 'DISPLAY_ETCH_ID', 'X') Locate DisplayEtchID in LocQ using @VM setting vPos else LocQ<1,-1> = DisplayEtchID LocRec = LocQ Database_Services('WriteDataRow', 'LOCATION', LocID, LocRec, True$, False$, True$) end Response = EtchID end service //---------------------------------------------------------------------------------------------------------------------- // StartEtchRun // // //---------------------------------------------------------------------------------------------------------------------- Service StartEtchRun(EtchID) CurrDTM = DateTime() GanEtchRec = Database_Services('ReadDataRow', 'GAN_ETCH', EtchID) Reactor = GanEtchRec ToolID = 'R':Reactor GanEtchRec = DateTime() GanEtchRec = CurrDTM GanEtchRec = @User4 GanEtchRec = 'START' GanEtchRec = ToolID Database_Services('WriteDataRow', 'GAN_ETCH', EtchID, GanEtchRec, True$, False$, True$) DisplayEtchID = Xlate('GAN_ETCH', EtchID, 'DISPLAY_ETCH_ID', 'X') // Remove from Location queue LocID = 'GCH*Q_ETCH' LocRec = Database_Services('ReadDataRow', 'LOCATION', LocID) LocQ = LocRec Locate DisplayEtchID in LocQ using @VM setting vPos then LocQ = Delete(LocQ, 1, vPos, 0) LocRec = LocQ Database_Services('WriteDataRow', 'LOCATION', LocID, LocRec, True$, False$, True$) end // Add to Tool queue ToolEtchRec = Database_Services('ReadDataRow', 'TOOL_ETCH', ToolID) ToolQ = ToolEtchRec Locate DisplayEtchID in ToolQ using @VM setting vPos else ToolQ<1,-1> = DisplayEtchID ToolEtchRec = ToolQ Database_Services('WriteDataRow', 'TOOL_ETCH', ToolID, ToolEtchRec, True$, False$, True$) end end service //---------------------------------------------------------------------------------------------------------------------- // StopEtchRun // // Input: // EtchID - [Required] // //---------------------------------------------------------------------------------------------------------------------- Service StopEtchRun(EtchID) CurrDTM = DateTime() Reactor = Xlate('GAN_ETCH', EtchID, GAN_ETCH_REACTOR$, 'X') ToolID = 'R':Reactor GanEtchRec = Database_Services('ReadDataRow', 'GAN_ETCH', EtchID) GanEtchRec = DateTime() GanEtchRec = True$ GanEtchRec = CurrDTM GanEtchRec = @User4 GanEtchRec = 'STOP' GanEtchRec = ToolID Database_Services('WriteDataRow', 'GAN_ETCH', EtchID, GanEtchRec, True$, False$, True$) GaN_Services('RemoveEtchFromWIP', EtchID) end service //---------------------------------------------------------------------------------------------------------------------- // DeleteEtchRun // // //---------------------------------------------------------------------------------------------------------------------- Service DeleteEtchRun(EtchID) GaN_Services('RemoveEtchFromWIP', EtchID) Database_Services('DeleteDataRow', 'GAN_ETCH', EtchID, True$, False$) end service //---------------------------------------------------------------------------------------------------------------------- // MoveCassToQueue // // Input: // WOMatKey - [Required] // QueueID - [Required] // // Output: // Response - // //---------------------------------------------------------------------------------------------------------------------- Service MoveCassToQueue(WOMatKey, QueueID) Success = False$ If WOMatKey NE '' and QueueID NE '' then QType = GaN_Services('GetQueueType', QueueID) If QType EQ 'Location' then QRec = Database_Services('ReadDataRow', 'LOCATION', QueueID) CassQ = QRec Locate WOMatKey in CassQ using @VM setting vPos else CassQ<1, -1> = WOMatKey QRec = CassQ Database_Services('WriteDataRow', 'LOCATION', QueueID, QRec, True$, False$, True$) Success = True$ end end else Error_Services('Add', 'Queue ID of type "Location" not supplied') end end Response = Success end service //---------------------------------------------------------------------------------------------------------------------- // RemoveCassFromQueue // // Input: // WOMatKey - [Required] // QueueID - [Required] // // Output: // Response - // //---------------------------------------------------------------------------------------------------------------------- Service RemoveCassFromQueue(WOMatKey, QueueID) Success = False$ If WOMatKey NE '' and QueueID NE '' then QType = GaN_Services('GetQueueType', QueueID) If QType EQ 'Location' then QRec = Database_Services('ReadDataRow', 'LOCATION', QueueID) CassQ = QRec Locate WOMatKey in CassQ using @VM setting vPos then CassQ = Delete(CassQ, 1, vPos, 0) QRec = CassQ Database_Services('WriteDataRow', 'LOCATION', QueueID, QRec, True$, False$, True$) Success = True$ end end else Error_Services('Add', 'Queue ID of type "Location" not supplied') end end Response = Success end service //---------------------------------------------------------------------------------------------------------------------- // GetCassQueue // // Input: // WOMatKey - [Required] // // Output: // Response - LOCATION or TOOL_WFR queue ID // //---------------------------------------------------------------------------------------------------------------------- Service GetCassQueue(WOMatKey) Done = False$ CassQ = '' GaNStages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStages = SRP_Array('Rotate', GaNStages, @FM, @VM) // Need to scan all LOCATION queues for the cassette LocQs = GaNStages<6> ToolClasses = GaNStages<3> For each LocQKey in LocQs using @VM setting vPos If LocQKey NE '' then LocQRec = Database_Services('ReadDataRow', 'LOCATION', LocQKey) CassettesInQ = LocQRec Locate WOMatKey in CassettesInQ using @VM setting wPos then Done = True$ CassQ = LocQKey end end Until Done EQ True$ Next LocQRec Response = CassQ end service //---------------------------------------------------------------------------------------------------------------------- // RemoveCassFromWIP // // Input: // WOMatKey - [Required] // // Output: // Response - True$ (1) if the WOMatKey was removed from the WIP, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service RemoveCassFromWIP(WOMatKey) Success = False$ If WOMatKey NE '' then CassQueue = GaN_Services('GetCassQueue', WOMatKey) CassInQueue = (CassQueue NE '') If CassInQueue EQ True$ then NumAttempts = 0 Loop Error_Services('Clear') // Location queues exist perpetually, so no need to check if the row exists. QRec = Database_Services('ReadDataRow', 'LOCATION', CassQueue) LocQ = QRec Locate WOMatKey in LocQ using @VM setting vPos then LocQ = Delete(LocQ, 0, vPos, 0) QRec = LocQ Database_Services('WriteDataRow', 'LOCATION', CassQueue, QRec, True$, False$, True$) Success = True$ end else Error_Services('Add', 'Error locating Cass ID ':WOMatKey:' in location queue ':LocQ:'.') end NumAttempts += 1 CassQueue = GaN_Services('GetCassQueue', WOMatKey) CassInQueue = (CassQueue NE '') Until ( (CassInQueue EQ False$) or (NumAttempts GT 5) ) Repeat LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = WOMatKey LogData<4> = CassQueue LogData<5> = CassQueue If Success EQ True$ then LogData<6> = 'Successfully removed Cass ID ':WOMatKey: ' from queue ':CassQueue:'. Number of attempts: ':NumAttempts:'.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end else If Error_Services('HasError') then LogData<6> = Error_Services('GetMessage'):' Number of attempts: ':NumAttempts:'.' end else LogData<6> = 'Error removing Cass ID ':WOMatKey:' from queue ':CassQueue:'. Number of attempts: ':NumAttempts:'.' end Machine = Environment_Services('GetServer') If Machine NE 'MESSA01EC' then EmailAddr = 'dstieber@srpcs.com,6613649828@txt.att.net' EmailMsg = 'Error removing Cass ID ':WOMatKey:' from queue ':CassQueue:'.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$, EmailAddr, EmailMsg) end else Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end end end else // Cass not in queue that was passed in. Log this information. LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = WOMatKey LogData<4> = CassQueue LogData<5> = CassQueue LogData<6> = 'Error in ':Service:' service. Cass not in QueueID ':CassQueue:'.' Machine = Environment_Services('GetServer') If Machine NE 'MESSA01EC' then EmailAddr = 'dstieber@srpcs.com,6613649828@txt.att.net' EmailMsg = 'Error in ':Service:' service. Cass not in QueueID ':CassQueue:'.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$, EmailAddr, EmailMsg) end else Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end end end else // WOMatKey is null LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = WOMatKey LogData<4> = CassQueue LogData<5> = CassQueue LogData<6> = 'Error in ':Service:' service. WOMatKey is null.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end Response = Success end service //---------------------------------------------------------------------------------------------------------------------- // MoveWfrToQueue // // Input: // WOWfrID - [Required] // QueueID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service MoveWfrToQueue(WOWfrID, QueueID) Success = False$ If WOWfrID NE '' and QueueID NE '' then QType = GaN_Services('GetQueueType', QueueID) Begin Case Case QType EQ 'Tool' If RowExists('TOOL_WFR', QueueID) then // Tool Queue already exists, so append wafer to the end of the queue QRec = Database_Services('ReadDataRow', 'TOOL_WFR', QueueID) ToolQ = QRec Locate WOWfrID in ToolQ using @VM setting vPos else ToolQ<1, -1> = WOWfrID QRec = ToolQ Database_Services('WriteDataRow', 'TOOL_WFR', QueueID, QRec, True$, False$, True$) Success = True$ end end else // Tool Queue is empty (i.e. the record does not exists), so create the TOOL_WFR record (i.e. TOOL Queue). QRec = '' QRec = WOWfrID Database_Services('WriteDataRow', 'TOOL_WFR', QueueID, QRec, True$, False$, True$) Success = True$ end Case QType EQ 'Location' // Location queues exist perpetually, so no need to check if the row exists. QRec = Database_Services('ReadDataRow', 'LOCATION', QueueID) LocQ = QRec Locate WOWfrID in LocQ using @VM setting vPos else LocQ<0, -1> = WOWfrID QRec = LocQ Database_Services('WriteDataRow', 'LOCATION', QueueID, QRec, True$, False$, True$) Success = True$ end Case Otherwise$ Error_Services('Add', 'Unrecognized QueueID ':QueueID:' of type ':QType:' supplied.') End Case end Response = Success end service //---------------------------------------------------------------------------------------------------------------------- // RemoveWfrFromWIP // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service RemoveWfrFromWIP(WfrID) Success = False$ WfrQueue = '' If WfrID NE '' then WfrQueue = Gan_Services('GetWfrQueue', WfrID) WfrQueueOrig = WfrQueue WfrInQueue = (WfrQueue NE '') If WfrInQueue EQ True$ then NumAttempts = 0 Loop Error_Services('Clear') QType = GaN_Services('GetQueueType', WfrQueue) Begin Case Case QType EQ 'Tool' If RowExists('TOOL_WFR', WfrQueue) then QRec = Database_Services('ReadDataRow', 'TOOL_WFR', WfrQueue) ToolQ = QRec Locate WfrID in ToolQ using @VM setting vPos then ToolQ = Delete(ToolQ, 0, vPos, 0) QRec = ToolQ Database_Services('WriteDataRow', 'TOOL_WFR', WfrQueue, QRec, True$, False$, True$) Success = True$ end else Error_Services('Add', 'Error locating wafer ID ':WfrID:' in tool queue ':ToolQ:'.') end end else // Tool Queue is empty (i.e. the record does not exists), so return error. Error_Services('Add', 'Cannot remove WO_WFR: ':WfrID:' from the TOOL_WFR queue ':WfrQueue | ' because it is empty.') end Case QType EQ 'Location' // Location queues exist perpetually, so no need to check if the row exists. QRec = Database_Services('ReadDataRow', 'LOCATION', WfrQueue) LocQ = QRec Locate WfrID in LocQ using @VM setting vPos then LocQ = Delete(LocQ, 0, vPos, 0) QRec = LocQ Database_Services('WriteDataRow', 'LOCATION', WfrQueue, QRec, True$, False$, True$) Success = True$ end else Error_Services('Add', 'Error locating wafer ID ':WfrID:' in location queue ':LocQ:'.') end Case Otherwise$ Error_Services('Add', 'Unrecognized QueueID ':WfrQueue:' of type ':QType:' supplied.') End Case NumAttempts += 1 WfrQueue = Gan_Services('GetWfrQueue', WfrID) WfrInQueue = (WfrQueue NE '') Until ( (WfrInQueue EQ False$) or (NumAttempts GT 5) ) Repeat LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = WfrID LogData<4> = '' LogData<5> = WfrQueueOrig If Success EQ True$ then LogData<6> = 'Successfully removed wafer ID ':WfrID: ' from queue ':WfrQueueOrig:'. Number of attempts: ':NumAttempts:'.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end else If Error_Services('HasError') then LogData<6> = Error_Services('GetMessage'):' Number of attempts: ':NumAttempts:'.' end else LogData<6> = 'Error removing wafer ID ':WfrID:' from queue ':WfrQueueOrig:'. Number of attempts: ':NumAttempts:'.' end Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end end else LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = WfrID LogData<4> = '' LogData<5> = '' LogData<6> = 'Error in ':Service:' service. Wafer ID ':WfrID:' not found in any queue.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end end else // WfrID is null. This is likely from routines trying to remove wafers after the RDS has been // closed the wafers are off of the WIP. LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = WfrID LogData<4> = '' LogData<5> = '' LogData<6> = 'Error in ':Service:' service. WfrID is null.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end Response = Success end service Service RemoveEtchFromWIP(EtchID) Success = False$ EtchQueue = '' If EtchID NE '' then DisplayEtchID = Xlate('GAN_ETCH', EtchID, 'DISPLAY_ETCH_ID', 'X') EtchQueue = Gan_Services('GetEtchQueue', DisplayEtchID) EtchQueueOrig = EtchQueue EtchInQueue = (EtchQueue NE '') If EtchInQueue EQ True$ then NumAttempts = 0 Loop QType = GaN_Services('GetQueueType', EtchQueue) Begin Case Case QType EQ 'Tool' If RowExists('TOOL_ETCH', EtchQueue) then QRec = Database_Services('ReadDataRow', 'TOOL_ETCH', EtchQueue) ToolQ = QRec Locate DisplayEtchID in ToolQ using @VM setting vPos then ToolQ = Delete(ToolQ, 0, vPos, 0) QRec = ToolQ Database_Services('WriteDataRow', 'TOOL_ETCH', EtchQueue, QRec, True$, False$, True$) Success = True$ end else Error_Services('Add', 'Error locating etch ID ':DisplayEtchID:' in tool queue ':ToolQ:'.') end end else // Tool Queue is empty (i.e. the record does not exists), so return error. Error_Services('Add', 'Cannot remove etch ID ':DisplayEtchID:' from the TOOL_WFR queue ':EtchQueue | ' because it is empty.') end Case QType EQ 'Location' // Location queues exist perpetually, so no need to check if the row exists. QRec = Database_Services('ReadDataRow', 'LOCATION', EtchQueue) LocQ = QRec Locate DisplayEtchID in LocQ using @VM setting vPos then LocQ = Delete(LocQ, 0, vPos, 0) QRec = LocQ Database_Services('WriteDataRow', 'LOCATION', EtchQueue, QRec, True$, False$, True$) Success = True$ end else Error_Services('Add', 'Error locating etch ID ':DisplayEtchID:' in location queue ':LocQ:'.') end Case Otherwise$ Error_Services('Add', 'Unrecognized QueueID ':EtchQueue:' of type ':QType:' supplied.') End Case NumAttempts += 1 EtchQueue = Gan_Services('GetEtchQueue', DisplayEtchID) EtchInQueue = (EtchQueue NE '') Until ( (EtchInQueue EQ False$) or (NumAttempts GT 5) ) Repeat LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = DisplayEtchID LogData<4> = '' LogData<5> = EtchQueueOrig If Success EQ True$ then LogData<6> = 'Successfully removed etch ID ':DisplayEtchID: ' from queue ':EtchQueueOrig:'. Number of attempts: ':NumAttempts:'.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end else If Error_Services('HasError') then LogData<6> = Error_Services('GetMessage'):' Number of attempts: ':NumAttempts:'.' end else LogData<6> = 'Error removing etch ID ':DisplayEtchID:' from queue ':EtchQueueOrig:'. Number of attempts: ':NumAttempts:'.' end Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end end else LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = DisplayEtchID LogData<4> = '' LogData<5> = '' LogData<6> = 'Error in ':Service:' service. Etch ID ':DisplayEtchID:' not found in any queue.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end end else LogData = '' LogData<1> = LoggingDTM LogData<2> = @USER4 LogData<3> = DisplayEtchID LogData<4> = '' LogData<5> = '' LogData<6> = 'Error in ':Service:' service. DisplayEtchID is null.' Logging_Services('AppendLog', objWIPLog, LogData, @RM, @FM, False$) end Response = Success end service //---------------------------------------------------------------------------------------------------------------------- // GetQueueType // // Input: // QueueID - [Required] // // Output: // Response - Queue type - 'Location' or 'Tool' // //---------------------------------------------------------------------------------------------------------------------- Service GetQueueType(QueueID) QueueType = '' NumFields = Count(QueueID, '*') If NumFields EQ 0 then QueueType = 'Tool' end else QueueType = 'Location' end Response = QueueType end service //---------------------------------------------------------------------------------------------------------------------- // GetLocQueueID // // Input: // StageID - [Required] // // Output: // Response - LOCATION QueueID // //---------------------------------------------------------------------------------------------------------------------- Service GetLocQueueID(StageID) QueueID = '' Done = False$ GaNStages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStages = SRP_Array('Rotate', GaNStages, @FM, @VM) LocQs = GaNStages<6> StageIDs = GaNStages<1> Locate StageID in StageIDs using @VM setting vPos then QueueID = LocQs<0, vPos> end Response = QueueID end service //---------------------------------------------------------------------------------------------------------------------- // GetPreEpiWfrs // // Input: // WONo - [Required] // NumWfrs - [Required] // // Output: // Response - NumWfrs of InWfrIDs if enough inbound material available, else null and error set. // //---------------------------------------------------------------------------------------------------------------------- Service GetPreEpiWfrs(WONo, NumWfrs) Response = '' ConsumedWfrCnt = 0 If (WONo NE '') and (NumWfrs GT 0) then CassNos = Xlate('WO_LOG', WONo, 'WO_MAT_CASS_NO', 'X') CassCnt = DCount(CassNos, @VM) ConsumedWfrCnt = 0 CassIndex = 1 Loop HaveCassettes = (CassIndex LE CassCnt) While HaveCassettes CassNo = CassNos<1,CassIndex> WOMatKey = WONo:'*':CassNo WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey) If (WOMatRec EQ '') or (WOMatRec EQ False$) then WOWfrIDs = Xlate('WO_MAT_WFR', WONo:'*':CassNo, WO_MAT_WFR_IN_WFR_ID$, 'X') SlotCnt = WOMatRec SlotIndex = 1 Loop Finished = (SlotIndex GT SlotCnt) or (ConsumedWfrCnt GE NumWfrs) While Not(Finished) If WOWfrIDs<1,SlotIndex> NE '' then SlotID = WONo:'*':CassNo:'*':SlotIndex Response = Insert(Response, 0, -1, 0, SlotID) ConsumedWfrCnt += 1 end SlotIndex += 1 Repeat end CassIndex += 1 Repeat end If ConsumedWfrCnt NE NumWfrs then Response = '' ErrorMessage = 'Not enough inbound material available.' Error_Services('Add', ErrorMessage) end end service //---------------------------------------------------------------------------------------------------------------------- // UnloadSusceptor // // Input: // RDSNo - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service UnloadSusceptor(RDSNo) Response = False$ If RDSNo NE '' then CurrDTM = DateTime() // Update REACT_RUN record ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) If ReactRunRec NE '' then GaNRunID = ReactRunRec WONo = ReactRunRec WOStep = ReactRunRec Reactor = ReactRunRec InWfrIDs = ReactRunRec CarrSlotNos = ReactRunRec ToolID = 'R':Reactor NextLocQ = 'GCH*Q_RATE' CarrSlotIDs = '' For each CarrSlotNo in CarrSlotNos using @VM setting vPos If CarrSlotNo NE '' then CarrSlotIDs<0, vPos> = RDSNo:'.':CarrSlotNo end Next CarrSlotNo ReactRunRec = CarrSlotIDs ReactRunRec = InWfrIDs Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) If Error_Services('NoError') then // Update WO_WFR record(s) and move WO_WFR(s) from TOOL_WFR queue to G_RATE queue For each InWfrID in InWfrIDs using @VM setting vPos If InWfrID NE '' then WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', InWfrID) If WOWfrRec NE '' then LocDTMs = WOWfrRec NextIndex = DCount(LocDTMs, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'UNLOAD' WOWfrRec = CarrSlotIDs<0, vPos> WOWfrRec = NextLocQ Database_Services('WriteDataRow', 'WO_WFR', InWfrID, WOWfrRec, True$, False$, True$) GaN_Services('RemoveWfrFromWIP', WfrID) GaN_Services('MoveWfrToQueue', InWfrID, NextLocQ) end else Error_Services('Add', 'Failed to read WO_WFR record ':InWfrID:' in ':Service:' service.') end end Next InWfrID // Update RUN_STAGE record (i.e. sign UNLOAD IA and stage complete field) RunStageKey = RDSNo:'*GROWTH' RunStageRec = Database_Services('ReadDataRow', 'RUN_STAGE', RunStageKey) If RunStageRec NE '' then InvActions = RunStageRec IACompBy = RunStageRec IACompDTM = RunStageRec Locate 'UNLOAD' in InvActions using @VM setting vPos then IACompBy<0, vPos> = @User4 IACompDTM<0, vPos> = CurrDTM end RunStageRec = IACompBy RunStageRec = IACompDTM RunStageRec = @User4 RunStageRec = CurrDTM RunStageRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE', RunStageKey, RunStageRec, True$, False$, True$) end else Error_Services('Add', 'Failed to read RUN_STAGE record ':RunStageKey:' in ':Service:' service.') end end else Error_Services('Add', 'Failed to write REACT_RUN record ':RDSNo:' in ':Service:' service.') end end else Error_Services('Add', 'Failed to read REACT_RUN record ':RDSNo:' in ':Service:' service.') end If Error_Services('NoError') then Response = True$ end else ErrorMessage = 'Error in ':Service:' service. ':Error_Services('GetMessage') LogData = '' LogData<1> = LoggingDTM LogData<2> = RDSNo LogData<3> = ErrorMessage Logging_Services('AppendLog', objUnloadLog, LogData, @RM, @FM) end end end service //---------------------------------------------------------------------------------------------------------------------- // GetNextStage // // Input: // WOWfrID - [Required] // // Output: // Response - Next WFR_STAGE that is prescribed and has not yet been completed. // //---------------------------------------------------------------------------------------------------------------------- Service GetNextStage(WOWfrID) Convert '.' to '*' in WOWfrID CurrStage = GaN_Services('GetCurrStage', WOWfrID) ReactRunKey = Xlate('WO_WFR', WOWfrID, 'RDS_NO', 'X') WfrStage = Xlate('REACT_RUN', ReactRunKey, 'WFR_STAGE', 'X') FormatWfrID = WOWfrID Done = False$ Convert '*' to '.' in FormatWfrID Begin Case Case CurrStage EQ 'G_RATE' // Lookup first stage in WFR_STAGE column within associated REACT_RUN record Response = WfrStage<0, 1> Case Otherwise$ For each Stage in WfrStage using @VM setting vPos RunStageWfrKey = ReactRunKey:'*':Stage:'*':FormatWfrID StageStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If StageStatus _EQC 'INIT' then Response = Stage Done = True$ end Until Done EQ True$ Next Stage End Case end service //---------------------------------------------------------------------------------------------------------------------- // GetNextRxStage // // Input: // RDSNo - [Required] // CurrStage - [Required] // // Output: // Response - Next WFR_STAGE that is prescribed in the REACT_RUN record // //---------------------------------------------------------------------------------------------------------------------- Service GetNextRxStage(RDSNo, CurrStage) Response = '' If ( (RDSNo NE '') and (CurrStage NE '') ) then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) StageRoute = ReactRunRec RxStages = ReactRunRec NextStage = '' If StageRoute NE '' then Done = False$ Locate CurrStage in StageRoute using @VM setting vPos then NumStages = DCount(StageRoute, @VM) For StageIndex = (vPos + 1) to NumStages NextStage = StageRoute<0, StageIndex> Locate NextStage in RxStages using @VM setting rPos then Done = True$ end Until Done EQ True$ Next StageIndex Response = NextStage end end else Locate CurrStage in RxStages using @VM setting vPos then NextStage = RxStages<0, vPos + 1> Response = NextStage end end end end service //---------------------------------------------------------------------------------------------------------------------- // GetCurrStage // // Input: // WOWfrID - [Required] // // Output: // Response - Current queue a wafer is in. // //---------------------------------------------------------------------------------------------------------------------- Service GetCurrStage(WOWfrID) Response = '' WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WOWfrID) InvLocs = WOWfrRec ToolLocs = WOWfrRec NumInvLocs = DCount(InvLocs, @VM) NumToolLocs = DCount(ToolLocs, @VM) CurrStep = Max(NumInvLocs, NumToolLocs) LastToolLoc = ToolLocs<0, CurrStep> LastInvLoc = InvLocs<0, CurrStep> Begin Case Case LastToolLoc NE '' and LastInvLoc EQ '' ToolID = LastToolLoc Response = Gan_Services('GetStageFromToolID', ToolID) Case LastToolLoc EQ '' and LastInvLoc NE '' LocQ = LastInvLoc Response = GaN_Services('GetStageID', '', LocQ) Case Otherwise$ // Error End Case end service //---------------------------------------------------------------------------------------------------------------------- // GetStageFromToolID // // Input: // ToolID - [Required] // // Output: // Response - Stage ID associated with the given Tool ID. // //---------------------------------------------------------------------------------------------------------------------- Service GetStageFromToolID(ToolID) Stage = '' If ToolID NE '' then Query = 'SELECT TOOL_CLASS CONTAINING "G_"' GoSub ClearCursors Rlist(Query, Target_ActiveList$, '', '', '') EOF = False$ Done = False$ If @RecCount then Loop While EOF EQ False$ ReadNext ToolClassID else EOF = True$ Tools = Xlate('TOOL_CLASS', ToolClassID, 'TOOL', 'X') If Indexc(Tools, ToolID, 1) GT 0 then LocWH = Xlate('TOOL_CLASS', ToolClassID, 'QIN_WH', 'X') Loc = Xlate('TOOL_CLASS', ToolClassID, 'QIN_LOC', 'X') LocQ = LocWH:'*':Loc Stage = Gan_Services('GetStageID', '', LocQ) Done = True$ end Until Done EQ True$ Repeat end GoSub ClearCursors end Response = Stage end service //---------------------------------------------------------------------------------------------------------------------- // GetNextEtchRun // // Input: // RDSNo - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service GetNextEtchRun(Reactor) Response = '' If Reactor NE '' then EtchIDs = '' EtchQList = '' RowIndex = 1 GoSub ClearCursors Query = 'SELECT GAN_ETCH WITH AVAILABLE EQ ':True$:' AND WITH END_DTM NE "" AND WITH REACTOR EQ ' | :Reactor:' BY REACTOR BY START_DTM' Rlist(Query, Target_ActiveList$, '', '', '') EOF = False$ If @RecCount then ReadNext GaNEtchID then Response = GaNEtchID end end GoSub ClearCursors end service //---------------------------------------------------------------------------------------------------------------------- // ConsumeIntEtchID // // Input: // IntEtchID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service ConsumeIntEtchID(IntEtchID) Response = False$ If IntEtchID NE '' then IntEtchRec = Database_Services('ReadDataRow', 'GAN_ETCH', IntEtchID) If IntEtchRec NE '' then IntEtchRec = False$ Database_Services('WriteDataRow', 'GAN_ETCH', IntEtchID, IntEtchRec, True$, False$, True$) Response = True$ end end end service //---------------------------------------------------------------------------------------------------------------------- // CreateRunStageWfr // // Input: // RDSNo - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service CreateRunStageWfr(RDSNo, Stage, WfrID) Response = False$ PSN = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X') ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) WfrStages = ReactRunRec Locate Stage in WfrStages using @VM setting sPos else // Stage has not yet been added to the REACT_RUN WFR_STAGE field UnRxStage = Gan_Services('GetUnRxStatus', RDSNo, Stage) If UnRxStage EQ True$ then UnRxStages = ReactRunRec GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStageInfo = SRP_Array('Rotate', GaNStageInfo, @FM, @VM) AllStages = GaNStageInfo<1> NewStageIndex = Gan_Services('GetStageIndex', Stage) Found = False$ For PrevStageIndex = (NewStageIndex - 1) to 1 Step -1 PrevStage = AllStages<0, PrevStageIndex> Locate PrevStage in WfrStages using @VM setting vPos then WfrStages = Insert(WfrStages, 0, vPos + 1, 0, Stage) UnRxStages = Insert(UnRxStages, 0, vPos + 1, 0, True$) Found = True$ end Until Found EQ True$ Next PrevStageIndex If Found EQ False$ then WfrStages = Insert(WfrStages, 0, 1, 0, Stage) UnRxStages = Insert(UnRxStages, 0, 1, 0, True$) end ReactRunRec = WfrStages ReactRunRec = UnRxStages Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end end RunStageWfrRec = '' RunStageWfrRec = GaN_Services('GetToolClassIDs', Stage) RunStageWfrRec = GaN_Services('GetInvActions', Stage) RunStageWfrRec = 'INIT' WfrKID = WfrID // WfrID may be passed in using an asterisk as the Delimiter instead of a period // Convert asterisks to periods just to be safe Convert '*' to '.' in WfrKID RunStageWfrKey = RDSNo:'*':Stage:'*':WfrKID If Not(RowExists('RUN_STAGE_WFR', RunStageWfrKey)) then Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end If Error_Services('NoError') then Response = True$ end service //---------------------------------------------------------------------------------------------------------------------- // CreateAllRunStageWfr // // Input: // RDSNo - [Required] // StopStageID - [Optional] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service CreateAllRunStageWfr(RDSNo, StopStageID, InWfrIDs) If (RDSNo NE '') then WfrStages = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X') UnRxStages = Xlate('REACT_RUN', RDSNo, 'UNPRESCRIBED_WFR_STAGE', 'X') If StopStageID EQ '' then StopStageID = WfrStages[-1, 'B':@VM] end // If wafer IDs are not passed in, then the code assumes all prescribed stages are to be // created if they have not yet already been created. If InWfrIDs EQ '' then InWfrIDs = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X') Response = False$ NumStages = DCount(WfrStages, @VM) NumWfrs = DCount(InWfrIDs, @VM) WfrIndex = 1 Loop WfrID = InWfrIDs<0, WfrIndex> If WfrID NE '' then StageIndex = 1 FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID Loop Stage = WfrStages<0, StageIndex> UnRxStage = UnRxStages<0, StageIndex> // Create RUN_STAGE_WFR record RunStageWfrKey = RDSNo:'*':Stage:'*':FormattedWfrID If Not(RowExists('RUN_STAGE_WFR', RunStageWfrKey)) and (UnRxStage EQ '') then GaN_Services('CreateRunStageWfr', RDSNo, Stage, WfrID) end Until Stage EQ StopStageID StageIndex += 1 Repeat end WfrIndex += 1 Until WfrIndex GT NumWfrs Repeat end If Error_Services('NoError') then Response = True$ end service //---------------------------------------------------------------------------------------------------------------------- // DispositionWfr // // Input: // RDSNo - [Required] // WfrID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service DispositionWfr(RDSNo, WfrID, Auto) Response = False$ If (RDSNo NE '') and (WfrID NE '') then ReadyToSplit = Gan_Services('ReadyToSplit', WfrID) If ReadyToSplit EQ True$ then If Auto NE '' then User = 'AUTO' end else User = @User4 end CurrDTM = Datetime() PSNo = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X') ANKO = Xlate('PROD_SPEC', PSNo, 'ANKO', 'X') If ANKO EQ True$ then DispStages = 'DISP':@VM:'RETAIN' end else DispStages = 'DISP':@VM:'G_PACK':@VM:'RETAIN' end For each Stage in DispStages using @VM setting vPos FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':Stage:'*':FormattedWfrID If Not(RowExists('RUN_STAGE_WFR', RunStageWfrKey)) then GaN_Services('CreateRunStageWfr', RDSNo, Stage, WfrID) end Next Stage // Update RUN_STAGE_WFR record (Set Status to 'COMP', Fill in CompBy and CompDTM) FormatWfrID = WfrID Convert '*' to '.' in FormatWfrID RunStageWfrKey = RDSNo:'*SPLIT*':FormatWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) RunStageWfrRec = User RunStageWfrRec = CurrDTM RunStageWfrRec = 'DISP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) // Update the WO_WFR record with the LOC_BY, LOC_DTM, INV_LOC (the next prescribed location queue), // and LOC_EVENT (DISPOSITION) WOWfrKey = WfrID WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WOWfrKey) LocDTMs = WOWfrRec NextStep = DCount(LocDTMs, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = User WOWfrRec = 'DISPOSITION' WOWfrRec = '' CurrStage = Gan_Services('GetCurrStage', WfrID) CurrLocQ = Gan_Services('GetLocQueueID', CurrStage) NextStage = Gan_Services('GetNextStage', WfrID) NextLocQ = Gan_Services('GetLocQueueID', NextStage) WoWfrRec = NextLocQ // Move wafer to the next prescribed location queue Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, NextLocQ) Database_Services('WriteDataRow', 'WO_WFR', WOWfrKey, WOWfrRec, True$, False$, True$) // Update the Disposition DTM for all wafers that have been through or are at disposition ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) InWfrIDs = ReactRunRec For each WOWfrID in InWfrIDs using @VM setting vPos ThisWOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WOWfrID) LocDTMs = ThisWOWfrRec LocQs = ThisWOWfrRec Locate 'GGR*Q_DISP' in LocQs using @VM setting vPos then // Wafer has been dispositioned at some point, so update the disposition DTM LocDTMs<0, vPos> = CurrDTM ThisWOWfrRec = LocDTMs Database_Services('WriteDataRow', 'WO_WFR', WOWfrID, ThisWOWfrRec, True$, False$, True$) end Next WOWfrID end If Error_Services('NoError') then Response = True$ end else ErrorMsg = 'This wafer is not ready to split. One or more previous stages have not been completed.' Error_Services('Add', ErrorMsg) end end service //---------------------------------------------------------------------------------------------------------------------- // StartWaferStage // // //---------------------------------------------------------------------------------------------------------------------- Service StartWaferStage(RDSNo, WfrID, StageID, ToolID) // 1. Find which queue the wafers are currently in. // 2. Move wafers to the new queue. // 3. Update all RUN_STAGE_WFR records. (i.e. update the status from INIT to START) // 4. Update WO_WFR records for wafer trace purposes. (i.e. location DTM, event, queue, etc.) // 5. Refresh the UI to display the new wafer statuses. Response = False$ If (RDSNo NE '') and (WfrID NE '') and (StageID NE '') and (ToolID NE '') then CurrDTM = Datetime() * LocQ = Gan_Services('GetWfrQueue', WfrID) * Gan_Services('RemoveWfrFromWIP', WfrID) * Gan_Services('MoveWfrToQueue', WfrID, ToolID) FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) InvAction = 'START' RunStageWfrRec = 'START' // Sign InvAction, Fill InvAction DTM SpecInvActions = RunStageWfrRec SIACompBy = RunStageWfrRec SIACompDTM = RunStageWfrRec Locate InvAction in SpecInvActions using @VM setting vPos then SIACompBy<0, vPos> = @USER4 SIACompDTM<0, vPos> = CurrDTM end RunStageWfrRec = SIACompBy RunStageWfrRec = SIACompDTM Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) LocDTM = WOWfrRec NextPos = DCount(LocDTM, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'START' WOWfrRec = ToolID WOWfrRec = '' Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end service //---------------------------------------------------------------------------------------------------------------------- // StopWaferStage // // //---------------------------------------------------------------------------------------------------------------------- Service StopWaferStage(RDSNo, WfrID, StageID) // 1. Find which queue the wafers are currently in. // 2. Move wafers to the new queue. // 3. Update all RUN_STAGE_WFR record. (i.e. update the status from INIT to STOP) // 4. Update WO_WFR records for wafer trace purposes. (i.e. location DTM, event, queue, etc.) Response = False$ If (RDSNo NE '') and (WfrID NE '') and (StageID NE '') then CurrDTM = Datetime() FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec If StageStatus _EQC 'START' then ToolQ = Gan_Services('GetWfrQueue', WfrID) * Gan_Services('RemoveWfrFromWIP', WfrID) NextStageID = Gan_Services('GetNextStage', WfrID) NextLocQ = Gan_Services('GetLocQueueID', NextStageID) * Gan_Services('MoveWfrToQueue', WfrID, NextLocQ) InvAction = 'STOP' // Sign InvAction, Fill InvAction DTM, SpecInvActions = RunStageWfrRec SIACompBy = RunStageWfrRec SIACompDTM = RunStageWfrRec CompBy = RunStageWfrRec CompDTM = RunStageWfrRec Locate InvAction in SpecInvActions using @VM setting vPos then SIACompBy<0, vPos> = @USER4 SIACompDTM<0, vPos> = CurrDTM end RunStageWfrRec = SIACompBy RunStageWfrRec = SIACompDTM // Set the status RunStageWfrRec = 'STOP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) LocDTM = WOWfrRec NextPos = DCount(LocDTM, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @User4 WOWfrRec = 'STOP' WOWfrRec = '' WOWfrRec = NextLocQ Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end end service //---------------------------------------------------------------------------------------------------------------------- // CompleteWaferStage // // //---------------------------------------------------------------------------------------------------------------------- Service CompleteWaferStage(RDSNo, WfrID, StageID) Response = False$ If (RDSNo NE '') and (WfrID NE '') and (StageID NE '') then CurrDTM = Datetime() FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec If StageStatus _EQC 'STOP' then // Mark RUN_STAGE_WFR as complete RunStageWfrRec = @User4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ DestStage = GaN_Services('IsDestStage', StageID) If DestStage EQ True$ then // Update EA spreadsheet with all fully characterized wafers (i.e. destroyed wafers) Engineering_Services('PostEARequest', RDSNo, 'CHAR') end end end end end service //---------------------------------------------------------------------------------------------------------------------- // SkipWaferStage // // //---------------------------------------------------------------------------------------------------------------------- Service SkipWaferStage(RDSNo, WfrID, StageID) Response = False$ If (RDSNo NE '') and (WfrID NE '') and (StageID NE '') then CurrDTM = Datetime() NextLocQ = '' CurrStageID = Gan_Services('GetCurrStage', WfrID) FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) // Mark RUN_STAGE_WFR as complete RunStageWfrRec = @USER4 RunStageWfrRec = CurrDTM // Set the wafer status RunStageWfrRec = 'SKIP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) // Are we in the same queue that we are trying to skip? If so, then need to move the wafers to the // next prescribed stage, otherwise, we do not. If CurrStageID _EQC StageID then LocQ = Gan_Services('GetWfrQueue', WfrID) Gan_Services('RemoveWfrFromWIP', WfrID) NextStageID = Gan_Services('GetNextStage', WfrID) NextLocQ = Gan_Services('GetLocQueueID', NextStageID) Gan_Services('MoveWfrToQueue', WfrID, NextLocQ) WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) LocDTM = WOWfrRec NextPos = DCount(LocDTM, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'SKIP' WOWfrRec = '' WOWfrRec = NextLocQ Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) end If Error_Services('NoError') then Response = True$ end end service //---------------------------------------------------------------------------------------------------------------------- // UnskipWaferStage // // //---------------------------------------------------------------------------------------------------------------------- Service UnskipWaferStage(RDSNo, WfrID, StageID) Response = False$ If (RDSNo NE '') and (WfrID NE '') and (StageID NE '') then CurrDTM = Datetime() FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) // Mark RUN_STAGE_WFR as not complete RunStageWfrRec = '' RunStageWfrRec = '' // Set the wafer status RunStageWfrRec = 'INIT' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) // Move wafer to the next scheduled queue Gan_Services('RemoveWfrFromWIP', WfrID) NextStageID = Gan_Services('GetNextStage', WfrID) NextLocQ = Gan_Services('GetLocQueueID', NextStageID) Gan_Services('MoveWfrToQueue', WfrID, NextLocQ) WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) LocDTM = WOWfrRec NextPos = DCount(LocDTM, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'UNSKIP' WOWfrRec = '' WOWfrRec = NextLocQ Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end service //---------------------------------------------------------------------------------------------------------------------- // AddWaferStage // // //---------------------------------------------------------------------------------------------------------------------- Service AddWaferStage(RDSNo, WfrID, StageID, ToolID) Response = False$ If (RDSNo NE '') and (WfrID NE '') and (StageID NE '') and (ToolID NE '') and (ToolID _NEC 'Select') then FormattedWfrID = WfrID Swap '*' with '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID Gan_Services('CreateRunStageWfr', RDSNo, StageID, WfrID) RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) RunStageWfrRec = ToolID Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end service //---------------------------------------------------------------------------------------------------------------------- // RemoveWaferStage // // //---------------------------------------------------------------------------------------------------------------------- Service RemoveWaferStage(RDSNo, WfrID, StageID) Response = False$ If (RDSNo NE '') and (WfrID NE '') and (StageID NE '') then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) WfrStages = ReactRunRec UnRxStages = ReactRunRec // Remove the stage from the REACT_RUN stage list Locate StageID in WfrStages using @VM setting vPos then WfrStages = Delete(WfrStages, 0, vPos, 0) UnRxStages = Delete(UnRxStages, 0, vPos, 0) ReactRunRec = WfrStages ReactRunRec = UnRxStages Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end FormattedWfrID = WfrID Swap '*' with '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID // Are we in the same queue that we are trying to remove? If so, then need to move the wafers to the // next prescribed stage, otherwise, we do not. CurrStageID = Gan_Services('GetCurrStage', WfrID) NextStageID = Gan_Services('GetNextStage', WfrID) NextLocQ = Gan_Services('GetLocQueueID', NextStageID) If CurrStageID _EQC StageID then LocQ = Gan_Services('GetWfrQueue', WfrID) Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, NextLocQ) end // Update the WO_WFR record with the LOC_BY, LOC_DTM, INV_LOC (the next prescribed location queue), // and LOC_EVENT (DISPOSITION) WOWfrKey = WfrID WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WOWfrKey) LocDTMs = WOWfrRec NextStep = DCount(LocDTMs, @VM) + 1 WOWfrRec = Datetime() WOWfrRec = @USER4 WOWfrRec = 'REMOVE' WOWfrRec = '' WOWfrRec = NextLocQ Database_Services('WriteDataRow', 'WO_WFR', WOWfrKey, WOWfrRec, True$, False$, True$) ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) // Delete the RUN_STAGE_WFR record If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then Database_Services('DeleteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, True$, False$) end If Error_Services('NoError') then Response = True$ end end service //---------------------------------------------------------------------------------------------------------------------- // CharacterizeWafer // // //---------------------------------------------------------------------------------------------------------------------- Service CharacterizeWafer(RDSNo, WfrID, Auto, PartialChar) Response = False$ If (RDSNo NE '') and (WfrID NE '') then If PartialChar NE True$ then PartialChar = False$ InWfrIDs = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X') Locate WfrID in InWfrIDs using @VM setting WfrIndex then PSNo = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X') ANKO = Xlate('PROD_SPEC', PSNo, 'ANKO', 'X') ANKOMetPockets = Xlate('PROD_SPEC', PSNo, 'ANKO_MET_POCKETS', 'X') ANKOMetPocket = ANKOMetPockets<0, WfrIndex> If Auto NE '' then User = 'AUTO' end else User = @User4 end ReadyToSplit = Gan_Services('ReadyToSplit', WfrID) If ReadyToSplit EQ True$ then CurrDTM = Datetime() // Create remaining prescribed RUN_STAGE_WFR records for the selected wafer. If PartialChar EQ True$ then Null end else Gan_Services('CreateAllRunStageWfr', RDSNo, '', WfrID) end // If ANKO create XRD and PR RUN_STAGE_WFR records. If ANKOMetPocket EQ True$ then GaN_Services('CreateRunStageWfr', RDSNo, 'JV_XRD', WfrID) GaN_Services('CreateRunStageWfr', RDSNo, 'PR', WfrID) end // Update RUN_STAGE_WFR record (Set Status to 'COMP', Fill in CompBy and CompDTM) FormatWfrID = WfrID Convert '*' to '.' in FormatWfrID RunStageWfrKey = RDSNo:'*SPLIT*':FormatWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) RunStageWfrRec = User RunStageWfrRec = CurrDTM RunStageWfrRec = 'CHAR' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) // Update the WO_WFR record with the LOC_BY, LOC_DTM, INV_LOC (the next prescribed location queue), // and LOC_EVENT (CHARACTERIZE) WOWfrKey = WfrID WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WOWfrKey) LocDTMs = WOWfrRec NextStep = DCount(LocDTMs, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = User WOWfrRec = 'CHARACTERIZE' WOWfrRec = '' CurrStage = Gan_Services('GetCurrStage', WfrID) CurrLocQ = Gan_Services('GetLocQueueID', CurrStage) NextStage = Gan_Services('GetNextStage', WfrID) NextLocQ = Gan_Services('GetLocQueueID', NextStage) WoWfrRec = NextLocQ // Move wafer to the next prescribed location queue Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, NextLocQ) Database_Services('WriteDataRow', 'WO_WFR', WOWfrKey, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ // Mark the wafer as characterized in the REACT_RUN record. ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) InWfrIDs = ReactRunRec Locate WfrID in InWfrIDs using @VM setting wPos then ReactRunRec = True$ // Add this wafer ID to the list of characterized wafers so that we can // determine primary and secondary characterization. Locate WfrID in ReactRunRec using @VM setting dummy else ReactRunRec = WfrID Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end end If PartialChar NE True$ then // Record the run number in the work order so that we can calculate when // the next characterization should take place. WONo = Field(WfrID, '*', 1) WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WONo) RunNo = ReactRunRec LastCharRun = WOLogRec[-1, 'B':@VM] If ( (LastCharRun EQ '') or (RunNo GT LastCharRun) ) then LastCharRun = RunNo WOLogRec = LastCharRun Database_Services('WriteDataRow', 'WO_LOG', WONo, WOLogRec, True$, False$, True$) end end end end else ErrorMsg = 'This wafer is not ready to split. One or more previous stages have not been completed.' Error_Services('Add', ErrorMsg) end end end end service Service CharEquation(RDSNo) return 0 end service //---------------------------------------------------------------------------------------------------------------------- // EligibleToWithdrawWfr // // Input: // RDSNo - [Required] // WfrID - [Required] // // Output: // Response - True$ (1) if eligible, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service EligibleToWithdrawWfr(RDSNo, WfrID) Response = True$ If (RDSNo NE '') and (WfrID NE '') then WfrStages = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X') Response = True$ CharDue = False$ Locate 'SPLIT' in WfrStages using @VM setting StartPos then // Was the wafer characterized or dispositioned? FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID SplitKey = RDSNo:'*SPLIT*':FormattedWfrID SplitStatus = Xlate('RUN_STAGE_WFR', SplitKey, 'STATUS', 'X') // Check to see if characterization is due for this wafer. // If so, this wafer is not eligble to be withdrawn. WONo = Field(WfrID, '*', 1) WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WONo) PrevCharRuns = WOLogRec NumCharRuns = DCount(PrevCharRuns, @VM) LastCharRun = PrevCharRuns<0, NumCharRuns> If LastCharRun EQ '' then LastCharRun = 0 ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) InWfrIDs = ReactRunRec LastWafer = DCount(InWfrIDs, @VM) RunNo = ReactRunRec PSNo = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X') CharFreq = Xlate('PROD_SPEC', PSNo, 'CHAR_FREQ', 'X') If ( (RunNo - LastCharRun) EQ CharFreq ) then // Characterization is due for this run SelectedWafer = GaN_Services('CharEquation', RDSNo) If SelectedWafer EQ 0 then SelectedWafer = LastWafer If SelectedWafer EQ WfrID then // Characterization is due for this wafer CharDue = True$ end end Begin Case Case CharDue EQ True$ Response = False$ Case SplitStatus _EQC 'CHAR' // Wafer was characterized. Ensure no metrology stages have started or been completed. NextStage = StartPos + 1 NumStages = DCount(WfrStages, @VM) For StageIndex = NextStage to NumStages Stage = WfrStages<0, StageIndex> RunStageWfrKey = RDSNo:'*':Stage:'*':FormattedWfrID StageStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If StageStatus _EQC 'START' or StageStatus _EQC 'COMP' then Response = False$ end Until Response EQ False$ Next StageIndex Case SplitStatus _EQC 'DISP' // Wafer was dispositioned. Ensure G_PACK and RETAIN have not been completed. GPackKey = RDSNo:'*G_PACK*':FormattedWfrID GPackStatus = Xlate('RUN_STAGE_WFR', GPackKey, 'STATUS', 'X') RetainSig = Xlate('WO_WFR', WfrID, 'RETAIN_SIG', 'X') If ( (GPackStatus _EQC 'COMP') or (RetainSig NE '') ) then Response = False$ end End Case end end end service //---------------------------------------------------------------------------------------------------------------------- // WithdrawWfr // // Input: // RDSNo - [Required] // WfrID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service WithdrawWfr(RDSNo, WfrID) CurrDTM = DateTime() Response = False$ If (RDSNo NE '') and (WfrID NE '') then WfrStages = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X') FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID Locate 'SPLIT' in WfrStages using @VM setting StartPos then NumStages = DCount(WfrStages, @VM) For StageIndex = StartPos to NumStages Stage = WfrStages<0, StageIndex> FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RunStageWfrKey = RDSNo:'*':Stage:'*':FormattedWfrID If Stage _EQC 'SPLIT' then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) OrigReactRunRec = ReactRunRec // Set RUN_STAGE_WFR record back to INIT status RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) RunStageWfrRec = '' RunStageWfrRec = '' RunStageWfrRec = 'INIT' Database_Services('WriteDataRow','RUN_STAGE_WFR',RunStageWfrKey,RunStageWfrRec,True$,False$,True$) CurrStage = Gan_Services('GetCurrStage', WfrID) CurrLocQ = Gan_Services('GetLocQueueID', CurrStage) ReturnStage = 'SPLIT' NextLocQ = Gan_Services('GetLocQueueID', ReturnStage) Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, NextLocQ) // Update the WO_WFR record with the LOC_BY, LOC_DTM, INV_LOC (the next prescribed location queue), // and LOC_EVENT (DISPOSITION) WOWfrKey = WfrID WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WOWfrKey) LocDTMs = WOWfrRec NextStep = DCount(LocDTMs, @VM) + 1 WOWfrRec = CurrDTM WOWfrRec = @User4 WOWfrRec = 'WITHDRAW' WOWfrRec = '' WOWfrRec = NextLocQ Database_Services('WriteDataRow', 'WO_WFR', WOWfrKey, WOWfrRec, True$, False$, True$) // Mark the wafer as NOT characterized in the REACT_RUN record. InWfrIDs = ReactRunRec CharList = ReactRunRec Locate WfrID in InWfrIDs using @VM setting wPos then ReactRunRec = False$ end Locate WfrID in CharList using @VM setting cPos then CharList = Delete(CharList, 0, cPos, 0) ReactRunRec = CharList end If OrigReactRunRec NE ReactRunRec then Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end // Locate this run in the in the WO_LOG record and remove it WONo = Field(WfrID, '*', 1) WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WONo) RunNo = ReactRunRec PrevCharRuns = WOLogRec Locate RunNo in PrevCharRuns using @VM setting vPos then // Only remove this run number if no other wafers are characterizated in this run WfrCharFlags = ReactRunRec TotalCharWfrs = Sum(WfrCharFlags) If TotalCharWfrs EQ 0 then PrevCharRuns = Delete(PrevCharRuns, 0, vPos, 0) WOLogRec = PrevCharRuns Database_Services('WriteDataRow', 'WO_LOG', WONo, WOLogRec, True$, False$, True$) end end end else // Remove subsequent stages If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then Database_Services('DeleteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, True$, False$) end end Next StageIndex end end If Error_Services('NoError') then Response = True$ end service //---------------------------------------------------------------------------------------------------------------------- // GetWfrQueue // // Input: // WfrID - [Required] // // Output: // Response - LOCATION or TOOL_WFR queue ID // //---------------------------------------------------------------------------------------------------------------------- Service GetWfrQueue(WfrID) Done = False$ WfrQ = '' GaNStages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStages = SRP_Array('Rotate', GaNStages, @FM, @VM) // Need to scan all LOCATION queues for the wafer. LocQs = GaNStages<6> ToolClasses = GaNStages<3> For each LocQKey in LocQs using @VM setting vPos If LocQKey NE '' then LocQRec = Database_Services('ReadDataRow', 'LOCATION', LocQKey) WfrsInQ = LocQRec Locate WfrID in WfrsInQ using @VM setting wPos then Done = True$ WfrQ = LocQKey end end Until Done EQ True$ Next LocQRec If Done EQ False$ then // Wafer not yet found. Scan all TOOL_WFR queues for the wafer. // Get tool class, then look up associated tool IDs. For each ToolClassID in ToolClasses using @VM setting vPos If ToolClassID NE '' then ToolIDs = Xlate('TOOL_CLASS', ToolClassID, 'TOOL', 'X') For each ToolID in ToolIDs using @VM setting tPos If (ToolID NE '') and (RowExists('TOOL_WFR', ToolID)) then ToolQRec = Database_Services('ReadDataRow', 'TOOL_WFR', ToolID) WfrsInQ = ToolQRec Locate WfrID in WfrsInQ using @VM setting wPos then Done = True$ WfrQ = ToolID end end Until Done EQ True$ Next ToolID end Until Done EQ True$ Next ToolClassID end Response = WfrQ end service //---------------------------------------------------------------------------------------------------------------------- // GetEtchQueue // // Input: // EtchID - [Required] // // Output: // Response - LOCATION or TOOL_WFR queue ID // //---------------------------------------------------------------------------------------------------------------------- Service GetEtchQueue(EtchID) Done = False$ EtchQ = '' GaNStages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStages = SRP_Array('Rotate', GaNStages, @FM, @VM) // Need to scan all LOCATION queues for the etch. LocQs = GaNStages<6> ToolClasses = GaNStages<3> For each LocQKey in LocQs using @VM setting vPos If LocQKey NE '' then LocQRec = Database_Services('ReadDataRow', 'LOCATION', LocQKey) ItemsInQ = LocQRec Locate EtchID in ItemsInQ using @VM setting wPos then Done = True$ EtchQ = LocQKey end end Until Done EQ True$ Next LocQRec If Done EQ False$ then // Etch not yet found. Scan all TOOL_WFR queues for the wafer. // Get tool class, then look up associated tool IDs. For each ToolClassID in ToolClasses using @VM setting vPos If ToolClassID NE '' then ToolIDs = Xlate('TOOL_CLASS', ToolClassID, 'TOOL', 'X') For each ToolID in ToolIDs using @VM setting tPos If (ToolID NE '') and (RowExists('TOOL_ETCH', ToolID)) then ToolQRec = Database_Services('ReadDataRow', 'TOOL_ETCH', ToolID) ItemsInQ = ToolQRec Locate EtchID in ItemsInQ using @VM setting wPos then Done = True$ EtchQ = ToolID end end Until Done EQ True$ Next ToolID end Until Done EQ True$ Next ToolClassID end Response = EtchQ end service //---------------------------------------------------------------------------------------------------------------------- // GetToolClassIDs // // Input: // StageID - [Required] // // Output: // Response - Tool class associated with the given stage ID. // //---------------------------------------------------------------------------------------------------------------------- Service GetToolClassIDs(StageID) ToolClasses = '' If StageID NE '' then GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStageInfo = SRP_Array('Rotate', GaNStageInfo, @FM, @VM) StageIDs = GaNStageInfo<1> AllToolClasses = GaNStageInfo<3> Locate StageID in StageIDs using @VM setting vPos then ToolClasses = AllToolClasses<0, vPos> end end Response = ToolClasses end service //---------------------------------------------------------------------------------------------------------------------- // GetToolIDs // // Input: // StageID - [Required] // ToolMode - [Optional] // // Output: // Response - Tool IDs associated with the given stage ID. // //---------------------------------------------------------------------------------------------------------------------- Service GetToolIDs(StageID, ToolMode) ToolIDs = '' If StageID NE '' then ToolClassIDs = Gan_Services('GetToolClassIDs', StageID) If ToolClassIDs NE '' then For each ToolClassID in ToolClassIDs using @SVM setting svPos TempArray = Xlate('TOOL_CLASS', ToolClassID, 'TOOL', 'X') If ToolMode NE '' then // Filter on ToolMode FilteredArray = '' For each ToolID in TempArray using @VM CurrMode = Xlate('TOOL', ToolID, 'CURR_MODE', 'X') If CurrMode EQ ToolMode then FilteredArray<0, -1> = ToolID end Next ToolID TempArray = FilteredArray end ToolIDs = SRP_Array('Join', ToolIDs, TempArray, 'OR', @VM) Next ToolClassID If ToolIDs[-1, 1] EQ @VM then ToolIDs[-1, 1] = '' end end Response = ToolIDs end service //---------------------------------------------------------------------------------------------------------------------- // IsDestStage // // Input: // StageID - [Required] // // Output: // Response - True$ (1) if stage is destructive, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service IsDestStage(StageID) Destructive = False$ ToolClassIDs = Gan_Services('GetToolClassIDs', StageID) For each ToolClassID in ToolClassIDs using @VM setting vPos DestClass = Xlate('TOOL_CLASS', ToolClassID, 'DEST_TEST', 'X') If DestClass EQ True$ then Destructive = True$ Until Destructive EQ True$ Next ToolClassID Response = Destructive end service //---------------------------------------------------------------------------------------------------------------------- // ReadyToSplit // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if WfrID is ready for characterization (i.e. SPLIT) and/or disposition (i.e. DISP), // False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service ReadyToSplit(WfrID) Response = 1 end service Done = False$ ReadyToSplit = True$ If WfrID NE '' then // Convert delimiters in case the key is passed in with the wrong format Convert '.' to '*' in WfrID GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStageInfo = SRP_Array('Rotate', GaNStageInfo, @FM, @VM) StageIDs = GaNStageInfo<1> StageTypes = GaNStageInfo<5> // 1. Check that all reactor stages (RUN_STAGE) are complete // 2. Check that all prescribed metrology stages up to, but not including, SPLIT are complete ReactRunNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', ReactRunNo) RunStages = ReactRunRec WfrStages = ReactRunRec UnRxStages = ReactRunRec For each RunStageKey in RunStages using @VM setting vPos StageID = RunStageKey[-1, 'B*'] Locate StageID in StageIDs using @VM setting vPos then StageType = StageTypes<0, vPos> If StageType _EQC 'REACTOR' then CompBy = Xlate('RUN_STAGE', RunStageKey, 'COMP_BY', 'X') If CompBy EQ '' then ReadyToSplit = False$ end end Until ReadyToSplit EQ False$ Next RunStageKey If ReadyToSplit EQ True$ then For each WfrStage in WfrStages using @VM setting vPos Until WfrStage _EQC 'SPLIT' Skip = False$ StageUnRx = UnRxStages<0, vPos> If StageUnRx EQ True$ then Skip = True$ If Skip EQ False$ then Convert '*' to '.' in WfrID RunStageWfrKey = ReactRunNo:'*':WfrStage:'*':WfrID CompBy = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'COMP_BY', 'X') If CompBy EQ '' then ReadyToSplit = False$ end Until ReadyToSplit EQ False$ Next WfrStage end end Response = ReadyToSplit end service //---------------------------------------------------------------------------------------------------------------------- // ReadyToSplit // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if any WfrID has been characterized or dispositioned, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service IsCassSplit(RDSNo) Done = False$ CassSplit = False$ If RDSNo NE '' then WfrIDs = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X') Stage = 'SPLIT' For each WfrID in WfrIDs using @VM setting vPos Convert '*' to '.' in WfrID RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID SplitStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If SplitStatus _EQC 'CHAR' or SplitStatus _EQC 'DISP' then CassSplit = True$ Done = True$ end Until Done EQ True$ Next WfrID end Response = CassSplit end service //---------------------------------------------------------------------------------------------------------------------- // GetRunSplitStatus // // Input: // RDSNo - [Required] // // Output: // Response - True$ (1) if all wafers in the run have been characterized or dispositioned, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service GetRunSplitStatus(RDSNo) SplitComp = True$ If RDSNo NE '' then WfrIDs = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X') Stage = 'SPLIT' For each WfrID in WfrIDs using @VM setting vPos Convert '*' to '.' in WfrID RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID SplitStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If SplitStatus _EQC 'CHAR' or SplitStatus _EQC 'DISP' then Null end else SplitComp = False$ end Until SplitComp EQ False$ Next WfrID end Response = SplitComp end service //---------------------------------------------------------------------------------------------------------------------- // ReadyToStart // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if any WfrID is ready to start a stage (i.e. it has not already started on another // stage, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service ReadyToStart(RDSNo, WfrID) Done = False$ ReadyToStart = True$ Convert '*' to '.' in WfrID If WfrID NE '' then WfrStages = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X') For each Stage in WfrStages using @VM setting vPos RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID StageStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If StageStatus _EQC 'START' then ReadyToStart = False$ Done = True$ end Until Done EQ True$ Next Stage end Response = ReadyToStart end service //---------------------------------------------------------------------------------------------------------------------- // WfrIsRunning // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if any WfrID is running on a tool, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service WfrIsRunning(WfrID) WfrIsRunning = False$ Convert '.' to '*' in WfrID RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') Convert '*' to '.' in WfrID If WfrID NE '' then WfrStages = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X') For each Stage in WfrStages using @VM setting vPos RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID StageStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If StageStatus _EQC 'START' then WfrIsRunning = True$ end Until WfrIsRunning EQ True$ Next Stage end Response = WfrIsRunning end service //---------------------------------------------------------------------------------------------------------------------- // GetWfrRunTool // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if any WfrID is running on a tool, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service GetWfrRunTool(WfrID) ToolID = '' Convert '.' to '*' in WfrID RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') Convert '*' to '.' in WfrID If WfrID NE '' then WfrStages = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X') For each Stage in WfrStages using @VM setting vPos RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID StageStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If StageStatus _EQC 'START' then ToolID = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'TOOL_ID', 'X') end Until ToolID NE '' Next Stage end Response = ToolID end service //---------------------------------------------------------------------------------------------------------------------- // GetUnRxStatus // // Input: // RDSNo - [Required] // StageID - [Required] // // Output: // Response - True$ (1) if StageID is unprescribed by the PSN, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service GetUnRxStatus(RDSNo, StageID) UnRxStage = False$ If (RDSNo NE '') and (StageID NE '') then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) WfrStages = ReactRunRec UnRxStages = ReactRunRec Locate StageID in WfrStages using @VM setting vPos then UnRxStage = UnRxStages<0, vPos> If UnRxStage EQ '' then UnRxStage = 0 end else // Stage not yet added, so we know it is unprescribed. UnRxStage = True$ end end Response = UnRxStage end service //---------------------------------------------------------------------------------------------------------------------- // GetMetrologyStatus // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if all metrology stages prescribed and unprescribed have been skipped or completed, // False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service GetMetrologyStatus(WfrID) MetStatus = '' If (WfrID NE '') then FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') GRateKey = RDSNo:'*G_RATE' GRateStatus = Xlate('RUN_STAGE', GRateKey, 'STATUS', 'X') If GRateStatus NE 'COMP' then MetStatus = False$ If ( (RDSNo NE '') and (GRateStatus _EQC 'COMP') ) then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) WfrStages = ReactRunRec For each MetStage in WfrStages using @VM setting StageIndex Until MetStage _EQC 'DISP' RunStageWfrKey = RDSNo:'*':MetStage:'*':FormattedWfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then StageStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If StageStatus _EQC 'START' or StageStatus _EQC 'INIT' then MetStatus = False$ end end Until MetStatus EQ False$ Next MetStage If MetStatus EQ '' then MetStatus = True$ end end Response = MetStatus end service //---------------------------------------------------------------------------------------------------------------------- // GetDispositionStatus // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if wafer is currently at or has previously been at the DISP stage, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service GetDispStatus(WfrID) DispStatus = False$ If (WfrID NE '') then FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') If (RDSNo NE '') then DispStageKey = RDSNo:'*DISP*':FormattedWfrID If RowExists('RUN_STAGE_WFR', DispStageKey) then StageStatus = Xlate('RUN_STAGE_WFR', DispStageKey, 'STATUS', 'X') CurrLoc = Gan_Services('GetCurrStage', WfrID) If ( (StageStatus _EQC 'COMP') or (CurrLoc _EQC 'DISP') ) then DispStatus = True$ end end end end Response = DispStatus end service //---------------------------------------------------------------------------------------------------------------------- // GetDestroyedStatus // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if a destructive metrology stage has been completed, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service GetDestroyedStatus(WfrID) DestStatus = False$ If (WfrID NE '') then FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') If (RDSNo NE '') then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) WfrIDs = ReactRunRec ExternalFlags = ReactRunRec Locate WfrID in WfrIDs using @VM setting WaferPos then ExternalFlag = ExternalFlags<0, WaferPos> If ExternalFlag EQ True$ then DestStatus = True$ end If DestStatus EQ False$ then WfrStages = ReactRunRec For each MetStage in WfrStages using @VM setting StageIndex If GaN_Services('IsDestStage', MetStage) then RunStageWfrKey = RDSNo:'*':MetStage:'*':FormattedWfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then StageStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If StageStatus _EQC 'START' or StageStatus _EQC 'COMP' then DestStatus = True$ end end end Until DestStatus EQ True$ Next MetStage end end end Response = DestStatus end service //---------------------------------------------------------------------------------------------------------------------- // GetDestroyedWafers // // Input: // RDSNo - [Required] // // Output: // Response - A list of wafer IDs of destroyed wafers in the cassette. // //---------------------------------------------------------------------------------------------------------------------- Service GetDestroyedWafers(RDSNo) DestroyedWfrs = '' If RDSNo NE '' then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) If Error_Services('NoError') then WfrIDs = ReactRunRec For each WfrID in WfrIDs using @VM setting wPos WfrDestroyed = GaN_Services('GetDestroyedStatus', WfrID) If WfrDestroyed EQ True$ then DestroyedWfrs<0, -1> = WfrID Next WfrID end end Response = DestroyedWfrs end service //---------------------------------------------------------------------------------------------------------------------- // GetGaNWIPJSON // // //---------------------------------------------------------------------------------------------------------------------- Service GetGaNWIPJSON() JSON = '' TableList = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_WIP') TableList = Delete(TableList, 1, 0, 0) hStatusObj = '' hStageArray = '' hStageObject = '' hRDSArray = '' hRDSObject = '' If SRP_JSON(hStatusObj, 'NEW', 'OBJECT') then If SRP_JSON(hStageArray, 'NEW', 'ARRAY') then PreviousStage = '' For Each StatusLine in TableList using @FM Stage = StatusLine<0, 1> If Stage NE '' then // New stage being introduced. Close the previous stage if one exists. If PreviousStage NE '' then SRP_JSON(hStageObject, 'SET', 'RDS Items', hRDSArray) SRP_JSON(hRDSArray, 'RELEASE') SRP_JSON(hStageArray, 'ADD', hStageObject) SRP_JSON(hStageObject, 'RELEASE') end SRP_JSON(hStageObject, 'NEW', 'OBJECT') SRP_JSON(hStageObject, 'SETVALUE', 'stage', Stage) SRP_JSON(hRDSArray, 'NEW', 'ARRAY') PreviousStage = Stage end else RDSNo = StatusLine<0, 2> If RDSNo NE '' then // RDS / Wafer line being added to the current stage. SRP_JSON(hRDSObject, 'NEW', 'OBJECT') SRP_JSON(hRDSObject, 'SETVALUE', 'RDS No', RDSNo, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Run/Etch ID', StatusLine<0, 3>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Part No', StatusLine<0, 4>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Wait Pri', StatusLine<0, 5>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Wait Wfrs', StatusLine<0, 6>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Wait Hrs', StatusLine<0, 7>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Wait Wfr ID', StatusLine<0, 8>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Tool ID', StatusLine<0, 9>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Run Wfrs', StatusLine<0, 10>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Run Hrs', StatusLine<0, 11>, 'STRING') SRP_JSON(hRDSObject, 'SETVALUE', 'Run Wfr ID', StatusLine<0, 12>, 'STRING') SRP_JSON(hRDSArray, 'ADD', hRDSObject) SRP_JSON(hRDSObject, 'RELEASE') end end Next StatusLine // Add the last stage object to the stage array. SRP_JSON(hStageObject, 'SET', 'RDS Items', hRDSArray) SRP_JSON(hRDSArray, 'RELEASE') SRP_JSON(hStageArray, 'ADD', hStageObject) SRP_JSON(hStageObject, 'RELEASE') SRP_JSON(hStatusObj, 'SET', 'stages', hStageArray) SRP_JSON(hStageArray, 'RELEASE') end JSON = SRP_JSON(hStatusObj, 'STRINGIFY', 'FAST') SRP_JSON(hStatusObj, 'RELEASE') end else Error_Services('Add', 'Error creating the GAN WIP Status JSON object.') end Response = JSON end service //---------------------------------------------------------------------------------------------------------------------- // SelectGaNEtchRuns // // //---------------------------------------------------------------------------------------------------------------------- Service SelectGaNEtchRuns(ParentWindow, GaNEtchID, Reactor) // Close Etch Manager if it is already open so that we can launch it in COMMIT mode. EtchManVis = Get_Property('NDW_INTERNAL_ETCH_MANAGER', 'VISIBLE') If EtchManVis EQ True$ then End_Window('NDW_INTERNAL_ETCH_MANAGER') Response = Dialog_Box('NDW_INTERNAL_ETCH_MANAGER', ParentWindow, 'COMMIT':@FM:GaNEtchID:@FM:Reactor) end service //---------------------------------------------------------------------------------------------------------------------- // AddWfrsToOutCass // // Input: // OutCassID - [Required] - // SelInWfrIDs - [Required] - Inbound wafer IDs to be be added to the outbound cassette, OutCassID. // //---------------------------------------------------------------------------------------------------------------------- Service AddWfrsToOutCass(OutCassID, SelInWfrIDs) CurrDTM = Datetime() WOMatWfrKey = OutCassID WOMatWfrRec = Database_Services('ReadDataRow', 'WO_MAT_WFR', WOMatWfrKey) OutWfrIDs = WOMatWfrRec AvailSlots = '' // Count empty (i.e. available) slots to fill NumWfrs = 25 For WfrIndex = 1 to NumWfrs If OutWfrIDs<0, WfrIndex> EQ '' then AvailSlots<0, -1> = OutCassID:'*':WfrIndex Next WaferIndex NumAvailSlots = DCount(AvailSlots, @VM) NumAddWfrs = DCount(SelInWfrIDs, @VM) If NumAddWfrs LE NumAvailSlots then FOR AddWfrIndex = 1 TO NumAddWfrs WfrID = SelInWfrIDs<1, AddWfrIndex> RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) // Place IN_WFR_ID into outbound cassette OutWfrID = AvailSlots<0, AddWfrIndex> OutSlot = Field(OutWfrID, '*', 3, 1) OutWfrIDs<0, OutSlot> = WfrID // Place wafer into REACT_RUN outbound wafer field LOCATE WfrID IN ReactRunRec USING @VM SETTING Pos THEN ReactRunRec = OutWfrID END // Update REACT_RUN table (RDS table) Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) // Update WO_WFR table (wafer trace table) WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) LOCATE WfrID IN ReactRunRec USING @VM SETTING cPos THEN CurrCarrSlot = ReactRunRec END ELSE CurrCarrSlot = '' END EventDTMs = WOWfrRec CurrEventIndex = DCount(EventDTMs, @VM) WOWfrRec = OutWfrID WOWfrRec = CurrCarrSlot Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) NEXT I // Update WO_MAT_WFR table (outbound cassette table) WOMatWfrRec = OutWfrIDs Database_Services('WriteDataRow', 'WO_MAT_WFR', WOMatWfrKey, WOMatWfrRec, True$, False$, True$) end else ErrorMessage = 'Add wafer operation failed. ':NumAddWfrs:' wafer(s) selected, but only ':NumAvailSlots: ' ' | : 'slot(s) are available.' Error_Services('Add', ErrorMessage) end end service //---------------------------------------------------------------------------------------------------------------------- // AddWfrToOutCass // // //---------------------------------------------------------------------------------------------------------------------- Service AddWfrToOutCass(WfrID) If WfrID NE '' then WONo = Field(WfrID, '*', 1) RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo) // Get next outbound slot OutSlotID = WORec If OutSlotID EQ '' then // Initialize the next out slot field OutSlotID = '1*1' end OutCassNo = OutSlotID[1, 'F*'] OutSlotNo = OutSlotID[Col2()+1, 2] OutSlotKey = WONo:'*':OutCassNo:'*':OutSlotNo // Update next outbound slot MaxShipQty = Xlate('WO_LOG', WONo, 'CUST_EPI_PART_SHIP_QTY', 'X') If ( (OutSlotNo) GE MaxShipQty ) then // Is there another outbound cassette available? NextCassNo = OutCassNo + 1 NextCassKey = WONo:'*':NextCassNo If ( RowExists('WO_MAT_WFR', NextCassKey) EQ True$ ) then // We have another outbound cassette Null end else // No more outbound cassettes available, so create one now * 1. Add WO_MAT key to WO_LOG WORec = NextCassKey * 2. Create WO_MAT_WFR record NextWOMatWfrRec = '' * 3. Populate WO_MAT_WFR record with outbound slots ONLY * (i.e. do not populate inbound slot columns as this would be equivalent to releasing new material) For Slot = 1 to MaxShipQty NextWOMatWfrRec = WONo:'*':NextCassNo:'*':Slot Next Slot Database_Services('WriteDataRow', 'WO_MAT_WFR', NextCassKey, NextWOMatWfrRec) end NextOutSlot = NextCassNo:'*':1 end else NextOutSlot = OutCassNo:'*':(OutSlotNo + 1) end WORec = NextOutSlot Database_Services('WriteDataRow', 'WO_LOG', WONo, WORec, True$, False$, True$) // Update WO_MAT_WFR table (outbound cassette table) CurrDTM = Datetime() WOMatWfrKey = WONo:'*':OutCassNo WOMatWfrRec = Database_Services('ReadDataRow', 'WO_MAT_WFR', WOMatWfrKey) OutWfrIDs = WOMatWfrRec OutWfrIDs<0, OutSlotNo> = WfrID WOMatWfrRec = OutWfrIDs Database_Services('WriteDataRow', 'WO_MAT_WFR', WOMatWfrKey, WOMatWfrRec, True$, False$, True$) // Place wafer into REACT_RUN outbound wafer field ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) LOCATE WfrID IN ReactRunRec USING @VM SETTING Pos THEN ReactRunRec = OutSlotKey END Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) // Move wafer into the G_PACK queue PackQ = 'GCH*Q_PACK' CurrQ = Gan_Services('GetWfrQueue', WfrID) Gan_Services('RemoveWfrFromWIP', WfrID) Gan_Services('MoveWfrToQueue', WfrID, PackQ) // Update WO_WFR table (wafer trace table) WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) LOCATE WfrID IN ReactRunRec USING @VM SETTING cPos THEN CurrCarrSlot = ReactRunRec END ELSE CurrCarrSlot = '' END EventDTMs = WOWfrRec NextPos = DCount(EventDTMs, @VM) + 1 WOWfrRec = OutSlotKey WOWfrRec = CurrCarrSlot WOWfrRec = CurrDTM WOWfrRec = @USER4 WOWfrRec = 'COMP' WOWfrRec = '' WOWfrRec = PackQ Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) // Mark DISP complete Convert '*' to '.' in WfrID RunStageWfrKey = RDSNo:'*DISP*':WfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec If StageStatus _EQC 'INIT' then // Mark DISP stage as complete RunStageWfrRec = @USER4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end end end service //---------------------------------------------------------------------------------------------------------------------- // RemoveWfrsFromOutCass // // Input: // OutCassID - [Required] - Outbound GaN cassette ID from which SelInWfrIDs will be removed. // SelInWfrIDs - [Required] - Inbound wafer IDs to be removed from the outbound cassette, OutCassID. // //---------------------------------------------------------------------------------------------------------------------- Service RemoveWfrsFromOutCass(OutCassID, SelInWfrIDs) CurrDTM = Datetime() WOMatWfrKey = OutCassID WOMatWfrRec = Database_Services('ReadDataRow', 'WO_MAT_WFR', WOMatWfrKey) OutWfrIDs = WOMatWfrRec NumRemWfrs = DCount(SelInWfrIDs, @VM) FOR RemWfrIndex = 1 TO NumRemWfrs WfrID = SelInWfrIDs<1, RemWfrIndex> RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) // Remove IN_WFR_ID from outbound cassette Locate WfrID in OutWfrIDs using @VM setting vPos then OutWfrIDs<0, vPos> = '' end // Remove wafer from REACT_RUN outbound wafer field LOCATE WfrID IN ReactRunRec USING @VM SETTING Pos THEN ReactRunRec = '' END // Update REACT_RUN table (RDS table) Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) // Update WO_WFR table (wafer trace table) WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) EventDTMs = WOWfrRec CurrEventIndex = DCount(EventDTMs, @VM) WOWfrRec = '' WOWfrRec = '' Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) NEXT I // Update WO_MAT_WFR table (outbound cassette table) WOMatWfrRec = OutWfrIDs Database_Services('WriteDataRow', 'WO_MAT_WFR', WOMatWfrKey, WOMatWfrRec, True$, False$, True$) end service //---------------------------------------------------------------------------------------------------------------------- // GetOutOfSpecStatus // // //---------------------------------------------------------------------------------------------------------------------- Service GetOutOfSpecStatus(RDSNo, WfrID, Split) If ( (RDSNo NE '') and (WfrID NE '') and (Split NE '') ) then DataOutOfSpec = '' ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) WfrStages = ReactRunRec StageList = '' Begin Case Case Split EQ 'PRE' For each Stage in WfrStages using @VM setting StagePos Until Stage EQ 'SPLIT' StageList<0, -1> = Stage Next Stage Case Split EQ 'POST' Locate 'SPLIT' in WfrStages using @VM setting StartPos then StartPos += 1 NumStages = DCount(WfrStages, @VM) For StageIndex = StartPos to NumStages Stage = WfrStages<0, StageIndex> Until Stage EQ 'DISP' StageList<0, -1> = Stage Next StageIndex end Case Otherwise$ Error_Services('Add', 'Invalid Split value supplied to service ':Service:'.') End Case If Error_Services('NoError') then Convert '*' to '.' in WfrID For each Stage in StageList using @VM RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID OutOfSpecArray = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'PARAM_OUT_OF_SPEC', 'X') DataOutOfSpec = ( Index(OutOfSpecArray, 1, 1) > 0 ) Until DataOutOfSpec EQ True$ Next Stage end Response = DataOutOfSpec end else Error_Services('Add', 'Null RDSNo, WfrID, or Split value supplied to service ':Service:'.') end end service //---------------------------------------------------------------------------------------------------------------------- // SplitSelection // // //---------------------------------------------------------------------------------------------------------------------- Service SplitSelection(RDSNo) // Check if we need to auto-characterize the wafer. If RDSNo NE '' then PSNo = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X') PSRec = Database_Services('ReadDataRow', 'PROD_SPEC', PSNo) ANKO = PSRec ANKOMetPockets = PSRec ANKOCharSel = PSRec TechType = PSRec ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) InWfrIDs = ReactRunRec OutOfSpecArray = ReactRunRec CassNo = ReactRunRec WONo = ReactRunRec GaNCritStages = ReactRunRec GaNParamKeys = ReactRunRec RunNo = CassNo WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WONo) SplitArray = '' CharStatus = Xlate('REACT_RUN', RDSNo, 'CHAR_STATUS', 'X') // Is characterization due for this run? CharReq = (CharStatus _EQC 'Sched' ) or (Sum(ANKOMetPockets) GT 0) NumWfrsOOS = 0 If CharReq EQ True$ then CharWfrIndex = '' If ANKOCharSel EQ 'PRESET' then // 'Preset' char selection CharWfrIndex = PSRec // Default fail safe - set char wafer index to 1 If CharWfrIndex EQ '' then CharWfrIndex = 1 end else // 'Candela Total Defects' char selection algorithm If ANKO EQ True$ then For each Value in OutOfSpecArray using @VM setting vPos ANKOPocket = ANKOMetPockets<0, vPos> If ( (ANKOPocket EQ True$) and (Value EQ True$) ) then NumWfrsOOS += Value end Next Value end else NumWfrsOOS = Sum(OutOfSpecArray) end If ANKO EQ True$ then NumCriticalFailWfrs = Xlate('REACT_RUN', RDSNo, 'NUM_ANKO_CRITICAL_FAIL_WFRS', 'X') end else NumCriticalFailWfrs = Xlate('REACT_RUN', RDSNo, 'NUM_CRITICAL_FAIL_WFRS', 'X') end NumWfrsOOS -= NumCriticalFailWfrs Begin Case Case NumWfrsOOS EQ 0 // Select the wafer with the highest candela total defects value DefectArray = '' WorstWfrIndex = 1 For each WfrID in InWfrIDs using @VM setting WfrIndex ANKOPocket = ANKOMetPockets<0, WfrIndex> If ( (ANKO EQ True$) and (ANKOPocket NE True$) ) then // Don't consider this wafer for split selection Null end else // Consider this wafer for split selection CriticalFailure = Xlate('WO_WFR', WfrID, 'CRITICAL_FAILURE', 'X') If CriticalFailure NE True$ then CANTotalDefects = 0 Convert '*' to '.' in WfrID // Try to use CAN_PST data if it exists, otherwise fallback on CAN_PRE data. Locate 'CAN_PST' in GaNCritStages using @VM setting sPos then GaNParamKey = GanParamKeys<0, sPos> MetNames = Xlate('GAN_PARAMS', GaNParamKey, 'MET_NAME', 'X') Locate 'Candela Total Defects Post' in MetNames using @VM setting mPos then RunStageWfrKey = RDSNo:'*CAN_PST*':WfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then MetValues = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'MET_PARAM_VALUES', 'X') CANTotalDefects = MetValues<0, mPos> If CANTotalDefects NE '' then DefectArray<0, WfrIndex> = CANTotalDefects WorstWfrDefects = DefectArray<0, WorstWfrIndex> If CANTotalDefects GT WorstWfrDefects then WorstWfrIndex = WfrIndex end end end else Locate 'CAN_PRE' in GaNCritStages using @VM setting sPos then GaNParamKey = GanParamKeys<0, sPos> MetNames = Xlate('GAN_PARAMS', GaNParamKey, 'MET_NAME', 'X') Locate 'Candela Total Defects' in MetNames using @VM setting mPos then RunStageWfrKey = RDSNo:'*CAN_PRE*':WfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then MetValues = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'MET_PARAM_VALUES', 'X') CANTotalDefects = MetValues<0, mPos> If CANTotalDefects NE '' then DefectArray<0, WfrIndex> = CANTotalDefects WorstWfrDefects = DefectArray<0, WorstWfrIndex> If CANTotalDefects GT WorstWfrDefects then WorstWfrIndex = WfrIndex end end end end end end end end else Locate 'CAN_PRE' in GaNCritStages using @VM setting sPos then GaNParamKey = GanParamKeys<0, sPos> MetNames = Xlate('GAN_PARAMS', GaNParamKey, 'MET_NAME', 'X') Locate 'Candela Total Defects' in MetNames using @VM setting mPos then RunStageWfrKey = RDSNo:'*CAN_PRE*':WfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then MetValues = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'MET_PARAM_VALUES', 'X') CANTotalDefects = MetValues<0, mPos> If CANTotalDefects NE '' then DefectArray<0, WfrIndex> = CANTotalDefects WorstWfrDefects = DefectArray<0, WorstWfrIndex> If CANTotalDefects GT WorstWfrDefects then WorstWfrIndex = WfrIndex end end end end end end end end Next WfrID CharWfrIndex = WorstWfrIndex Case NumWfrsOOS EQ 1 For each WfrID in InWfrIDs using @VM setting vPos CriticalFailure = Xlate('WO_WFR', WfrID, 'CRITICAL_FAILURE', 'X') If ( (OutOfSpecArray<0, vPos> EQ True$) and (CriticalFailure NE True$) ) then WfrIndex = vPos end Next WfrID ANKOPocket = ANKOMetPockets<0, WfrIndex> If ( (ANKO EQ True$) and (ANKOPocket NE True$) ) then // Don't consider this wafer for split selection Null end else // Consider this wafer for split selection // Use the out of spec wafer that hasn't failed critically For each WfrID in InWfrIDs using @VM setting WfrIndex CriticalFailure = Xlate('WO_WFR', WfrID, 'CRITICAL_FAILURE', 'X') If ( (OutOfSpecArray<0, WfrIndex> EQ True$) and (CriticalFailure NE True$) ) then CharWfrIndex = WfrIndex end Until CharWfrIndex NE '' Next WfrID end Case NumWfrsOOS GT 1 // Select the out of spec wafer with the highest candela total defects value DefectArray = '' WorstWfrIndex = 1 For each WfrID in InWfrIDs using @VM setting WfrIndex ANKOPocket = ANKOMetPockets<0, WfrIndex> If ( (ANKO EQ True$) and (ANKOPocket NE True$) ) then // Don't consider this wafer for split selection Null end else // Consider this wafer for split selection CriticalFailure = Xlate('WO_WFR', WfrID, 'CRITICAL_FAILURE', 'X') If CriticalFailure NE True$ then WfrOOS = OutOfSpecArray<0, WfrIndex> If WfrOOS EQ True$ then CANTotalDefects = 0 Convert '*' to '.' in WfrID // Try to use CAN_PST data if it exists, otherwise fallback on CAN_PRE data. Locate 'CAN_PST' in GaNCritStages using @VM setting sPos then GaNParamKey = GanParamKeys<0, sPos> MetNames = Xlate('GAN_PARAMS', GaNParamKey, 'MET_NAME', 'X') Locate 'Candela Total Defects Post' in MetNames using @VM setting mPos then RunStageWfrKey = RDSNo:'*CAN_PST*':WfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then MetValues = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'MET_PARAM_VALUES', 'X') CANTotalDefects = MetValues<0, mPos> If CANTotalDefects NE '' then DefectArray<0, WfrIndex> = CANTotalDefects WorstWfrDefects = DefectArray<0, WorstWfrIndex> If CANTotalDefects GT WorstWfrDefects then WorstWfrIndex = WfrIndex end end end else Locate 'CAN_PRE' in GaNCritStages using @VM setting sPos then GaNParamKey = GanParamKeys<0, sPos> MetNames = Xlate('GAN_PARAMS', GaNParamKey, 'MET_NAME', 'X') Locate 'Candela Total Defects' in MetNames using @VM setting mPos then RunStageWfrKey = RDSNo:'*CAN_PRE*':WfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then MetValues = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'MET_PARAM_VALUES', 'X') CANTotalDefects = MetValues<0, mPos> If CANTotalDefects NE '' then DefectArray<0, WfrIndex> = CANTotalDefects WorstWfrDefects = DefectArray<0, WorstWfrIndex> If CANTotalDefects GT WorstWfrDefects then WorstWfrIndex = WfrIndex end end end end end end end end else Locate 'CAN_PRE' in GaNCritStages using @VM setting sPos then GaNParamKey = GanParamKeys<0, sPos> MetNames = Xlate('GAN_PARAMS', GaNParamKey, 'MET_NAME', 'X') Locate 'Candela Total Defects' in MetNames using @VM setting mPos then RunStageWfrKey = RDSNo:'*CAN_PRE*':WfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then MetValues = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'MET_PARAM_VALUES', 'X') CANTotalDefects = MetValues<0, mPos> If CANTotalDefects NE '' then DefectArray<0, WfrIndex> = CANTotalDefects WorstWfrDefects = DefectArray<0, WorstWfrIndex> If CANTotalDefects GT WorstWfrDefects then WorstWfrIndex = WfrIndex end end end end end end end end end Next WfrID CharWfrIndex = WorstWfrIndex End Case end For each WfrID in InWfrIDs using @VM setting WfrIndex ANKOPocket = ANKOMetPockets<0, WfrIndex> Begin Case Case ( (WfrIndex EQ CharWfrIndex) and (CharStatus EQ 'Sched') ) SplitArray<0, WfrIndex> = 'CHAR' Case ( ( (TechType EQ 'P' ) ) and (ANKOPocket EQ True$) ) SplitArray<0, WfrIndex> = 'PARTIAL_CHAR' Case Otherwise$ SplitArray<0, WfrIndex> = 'DISP' End Case Next WfrID end else // Disposition all wafers For each WfrID in InWfrIDs using @VM setting WfrIndex SplitArray<0, WfrIndex> = 'DISP' Next WfrID end // Disposition and characterize wafers according to the data from our split array. Auto = True$ For each WfrID in InWfrIDs using @VM setting WfrIndex SplitAction = SplitArray<0, WfrIndex> Begin Case Case SplitAction EQ 'PARTIAL_CHAR' Gan_Services('CharacterizeWafer', RDSNo, WfrID, Auto, True$) Case SplitAction EQ 'CHAR' Gan_Services('CharacterizeWafer', RDSNo, WfrID, Auto, False$) Case SplitAction EQ 'DISP' Gan_Services('DispositionWfr', RDSNo, WfrID, Auto) Case Otherwise$ ErrorMsg = 'Error splitting wafer ':WfrID:' in the ':Service:' service.' Error_Services('Add', ErrorMsg) End Case Next WfrID end end service //---------------------------------------------------------------------------------------------------------------------- // GetCharType // // Input: // WfrID - [Required] // // Output: // Response - 'Light' if light characterization performed on wafer, 'Full' if destructive characterization // performed on wafer, or '' if no characterization perfomed on wafer. // //---------------------------------------------------------------------------------------------------------------------- Service GetCharType(WfrID) CharType = '' If WfrID NE '' then RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') If (RDSNo NE '') then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) InWfrIDs = ReactRunRec CharFlags = ReactRunRec Locate WfrID in InWfrIDs using @VM setting WfrIndex then WfrCharacterized = CharFlags<0, WfrIndex> If WfrCharacterized EQ True$ then WfrDestroyed = Gan_Services('GetDestroyedStatus', WfrID) If WfrDestroyed EQ True$ then CharType = 'Full' end else CharType = 'Light' end end end else Error_Services('Add', 'Error locating WfrID ':WfrID:' in RDS record ':RDSNo:' in service ':Service:'.') end end else Error_Services('Add', 'Error retrieving RDS number from WO_WFR record ':WfrID:' in service ':Service:'.') end end else Error_Services('Add', 'Null WfrID key supplied to service ':Service:'.') end Response = CharType end service //---------------------------------------------------------------------------------------------------------------------- // ReadyToClose // // //---------------------------------------------------------------------------------------------------------------------- Service ReadyToClose(RDSNo) Response = False$ If RDSNo NE '' then ReadyToClose = True$ ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) WfrsDispReady = ReactRunRec For each WfrStatus in WfrsDispReady using @VM If WfrStatus NE True$ then ReadyToClose = False$ end Until ReadyToClose = False$ Next WfrStatus Response = ReadyToClose end end service //---------------------------------------------------------------------------------------------------------------------- // CloseRDS // // //---------------------------------------------------------------------------------------------------------------------- Service CloseRDS(RDSNo) If RDSNo NE '' then // Is the RDS eligible to be closed? CurrDTM = Datetime() ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) WfrsDispReady = ReactRunRec InWfrIDs = ReactRunRec EligibleToClose = True$ ; // Assume ready to close until proven otherwise For each WfrStatus in WfrsDispReady using @VM setting WfrIndex If WfrStatus NE True$ then EligibleToClose = False$ Until EligibleToClose EQ False$ Next WfrStatus If EligibleToClose EQ True$ then // 1. Move eligble wafers to outbound box. ShipFlags = ReactRunRec OutSlotIDs = ReactRunRec WONo = ReactRunRec WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WONo) WOMatKeys = WOLogRec CustNo = WOLogRec EpiPartNo = WOLogRec CustEpiNo = CustNo:'*':EpiPartNo SchedQty = Xlate('CUST_EPI_PART', CustEpiNo, 'CASS_SHIP_QTY', 'X') NumCass = DCount(WOMatKeys, @VM) OutCassQtys = Xlate('WO_MAT_WFR', WOMatKeys, 'OUT_WFR_QTY', 'X') FQASigs = Xlate('WO_MAT', WOMatKeys, 'CASS_FINAL_SIG', 'X') PSNo = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X') ANKO = Xlate('PROD_SPEC', PSNo, 'ANKO', 'X') CurrPartNo = Xlate('PROD_SEPC', PSNo, 'CURR_PART_NO', 'X') For each InWfrID in InWfrIDs using @VM setting WfrIndex NextOutSlotID = '' ShipFlag = ShipFlags<0, WfrIndex> OutSlotID = OutSlotIDs<0, WfrIndex> If ( (ShipFlag EQ True$) and (OutSlotID EQ '') and (ANKO NE True$) ) then * If ( (ShipFlag EQ True$) and (OutSlotID EQ '') and (ANKO NE True$) and (CurrPartNo NE 'U314') ) then Gan_Services('AddWfrToOutCass', InWfrID) end else // Remove the wafer from the WIP. These wafers should be in either DISP or RETAIN CurrStage = GaN_Services('GetCurrStage', InWfrID) CurrQ = GaN_Services('GetWfrQueue', InWfrID) If CurrQ NE '' then Gan_Services('RemoveWfrFromWIP', InWfrID) If CurrStage NE '' then // Mark current stage complete Convert '*' to '.' in InWfrID RunStageWfrKey = RDSNo:'*':CurrStage:'*':InWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) StageStatus = RunStageWfrRec If StageStatus _EQC 'INIT' then // Mark DISP stage as complete RunStageWfrRec = @USER4 RunStageWfrRec = CurrDTM RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end end end Next InWfrID If Error_Services('NoError') then // Get an up-to-date copy of the REACT_RUN record ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) // Mark the RDS "Closed" ReactRunRec = True$ Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) Engineering_Services('ClearEARequests', RDSNo) end end else If Assigned(WfrIndex) then WfrID = InWfrIDs<0, WfrIndex> Error_Services('Add', 'Error closing RDS. Wafer ':WfrID:' has incomplete metrology stages.') end else Error_Services('Add', 'Error closing RDS') end end end end service //---------------------------------------------------------------------------------------------------------------------- // UpdateDataFlags // // //---------------------------------------------------------------------------------------------------------------------- Service UpdateDataFlags(RDSNo) Response = '' If RDSNo NE '' then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) OrigReactRunRec = ReactRunRec InWfrIDs = ReactRunRec CharFlags = ReactRunRec CritStages = ReactRunRec RunScrapped = False$ // Clear failure flags ReactRunRec = RunScrapped ReactRunRec = '' GaNMetStages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_MET_STAGES') For each WfrID in InWfrIDs using @VM setting WaferPos // Assume flags are false for this wafer until proven otherwise. DataValidationDisabled = ReactRunRec DataIsMissing = False$ DataOutOfSpec = False$ For each Stage in GaNMetStages using @FM setting StagePos Abort = False$ If Abort EQ False$ then Locate Stage in CritStages using @VM setting vPos then PostSplit = '' PostSplitFailure = False$ WaferCharacterized = '' PostSplit = (StagePos GT 4) WaferCharacterized = CharFlags<0, WaferPos> Convert '*' to '.' in WfrID RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) OutOfSpecArray = RunStageWfrRec For each Parameter in OutOfSpecArray using @VM setting vPos If DataIsMissing EQ False$ then // Check if we need to set the flag to true Locate '-1' in Parameter using @SVM setting pPos then DataIsMissing = True$ end end Locate '1' in Parameter using @SVM setting pPos then DataOutOfSpec = True$ If PostSplit EQ True$ then PostSplitFailure = True$ end Next Parameter If RunScrapped NE True$ then // Check if we need to set the flag to true If ( (PostSplitFailure EQ True$) and (WaferCharacterized EQ True$) and (DataValidationDisabled NE True$) ) then RunScrapped = True$ ReactRunRec = RunScrapped end end end end end Next Stage ReactRunRec = DataIsMissing ReactRunRec = DataOutOfSpec If ( (DataIsMissing EQ False$) and (DataOutOfSpec EQ False$) ) then ReactRunRec = False$ ReactRunRec = '' ReactRunRec = '' end Next WfrID If OrigReactRunRec NE ReactRunRec then Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end end end service //---------------------------------------------------------------------------------------------------------------------- // GetGaNMetrologyData // // //---------------------------------------------------------------------------------------------------------------------- Service GetGaNMetrologyData(RDSNo, Stage, Tool, WfrID) Response = '' ErrorMsg = '' OICandelaStage = '' Abort = False$ If ( (RDSNo NE '') and (Stage NE '') ) then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) OrigReactRunRec = ReactRunRec PSNo = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X') NCRFlags = ReactRunRec InWfrIDs = ReactRunRec DispFlags = ReactRunRec Grades = ReactRunRec Reasons = ReactRunRec CharFlags = ReactRunRec RunScrapped = ReactRunRec If Abort EQ False$ then TechType = Xlate('PROD_SPEC', PSNo, 'TECH_TYPE', 'X') GaNMetStages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_MET_STAGES') If WfrID NE '' then Scribe = Xlate('WO_WFR', WfrID, 'SCRIBE', 'X') GaNParamKey = '' GaNParamKeys = Xlate('REACT_RUN', RDSNo, 'GAN_PARAM_KEYS', 'X') Found = False$ For each GaNParamKey in GaNParamKeys using @VM setting vPos Loc = Index(GaNParamKey, Stage, 1) If Loc GT 0 then Found = True$ Until Found EQ True$ Next GaNParamKey GaNParamRec = Database_Services('ReadDataRow', 'GAN_PARAMS', GaNParamKey) MetNames = GaNParamRec LSLs = GaNParamRec USLs = GaNParamRec Targets = GaNParamRec RunNo = ReactRunRec Reactor = 'R':ReactRunRec PartNo = Xlate('REACT_RUN', RDSNo, 'EPI_PART_NO', 'X') Query = '' IQSToolID = Xlate('TOOL', Tool, 'IQS_ID', 'X') If Stage NE 'JV_XRD' then Query = "SELECT td.F_NAME, se.F_TSNO, SE.F_SBNO, se.F_VAL " end else Query = "SELECT td.F_NAME, SE.F_SBNO, se.F_TSNO, se.F_VAL " end Query := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se " Query := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT = pl.F_LOT " Query := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST " Query := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART " Query := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS " Begin Case Case Stage EQ 'BV100%' Query := "LEFT JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP " Case ( (Stage NE 'GROWTH') and (Stage NE 'TEMP') ) Query := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP " Query := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC " End Case Query := "WHERE pd.F_NAME = '":PartNo:"' AND " Query := " pr.F_NAME = '":Reactor:"' AND " Query := " pl.F_NAME = '":RunNo:"' AND " If Stage EQ 'JV_XRD' then Query := " (se.F_FLAG = 0 or se.F_FLAG = 2) AND " end else Query := " se.F_FLAG = 0 AND " end Begin Case Case ( (Stage EQ 'WARP') or (Stage EQ 'GROWTH') or (Stage EQ 'TEMP') or (Stage EQ 'BV100%') ) Query := " (" Case ( (Stage EQ 'CAN') or (Stage EQ 'UV') or (Stage EQ 'UV_PRE') or (Stage EQ 'UV_PST') or (Stage EQ 'CAN_PRE') or (Stage EQ 'CAN_PST') ) Query := " dd.F_NAME = '":IQSToolID:"' AND (" Case Otherwise$ Query := " dd.F_NAME = '":Scribe:"' AND (" End Case NumMetNames = DCount(MetNames, @VM) For each MetName in MetNames using @VM setting vPos If vPos EQ NumMetNames then Query := "td.F_NAME = '":MetName:"') " end else Query := "td.F_NAME = '":MetName:"' or " end Next MetName If ( (Stage NE 'GROWTH') and (Stage NE 'TEMP') and (Stage NE 'BV100%') ) then Query := "AND dd.F_NAME IS NOT NULL " end Query := "ORDER BY td.F_NAME" If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query) Convert @FM to @VM in Response Convert @RM to @FM in Response ParamValues = '' For each MetName in MetNames using @VM setting mPos PointAvg = 0 NumPoints = 0 For each Row in Response using @FM setting fPos Locate MetName in Row using @VM setting vPos then DataPointIndex = Response If ( (Index(Row, @SVM, 1) GT 0) and (DataPointIndex EQ 0) ) else // Data point at index 0 for parameters with multiple data points is an average. // Ignore this data point. WaferPos = Response ParamValues = Response end end Next Row If Stage EQ 'JV_XRD' then TheseParamValues = ParamValues NumPoints = DCount(TheseParamValues, @SVM) If NumPoints GT 0 then ParamValues = Sum(TheseParamValues) / NumPoints end Next MetName ChangeDetected = False$ If ( (Stage EQ 'UV') or (Stage EQ 'UV_PRE') or (Stage EQ 'UV_PST') or (Stage EQ 'WARP') or (Stage EQ 'CAN') or (Stage EQ 'CAN_PRE') or (Stage EQ 'CAN_PST') or (Stage EQ 'GROWTH') or (Stage EQ 'TEMP') ) then For each WfrID in InWfrIDs using @VM setting WaferPos ValueSet = ParamValues Delimiter = @VM Convert '*' to '.' in WfrID RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) RunStageWfrRec = ValueSet OutOfSpecArray = '' For each Parameter in MetNames using @VM setting vPos Value = ValueSet<0, vPos> LSL = LSLs<0, vPos> USL = USLs<0, vPos> Target = Targets<0, vPos> MaxDecimals = GaN_Services('GetMaxDecimals', LSL:@VM:USL:@VM:Target) If Value NE '' then Value = SRP_Math('ROUND', Value, MaxDecimals) // Ensures decimal places are retained and zeros are padded. Value = Oconv(Iconv(Value, 'MD':MaxDecimals:'L'), 'MD':MaxDecimals:'L') end ValueSet<0,vPos> = Value // One value per parameter Begin Case Case (LSL NE '') and (Value NE '') OutOfSpecArray<0, vPos> = (Value LT LSL) Case (LSL NE '') and (Value EQ '') OutOfSpecArray<0, vPos> = -1 ; // -1 = data value is missing ErrorMsg = 'Error retrieving metrology data from SPC for RDS ':RDSNo:'.' Case Otherwise$ OutOfSpecArray<0, vPos> = False$ End Case // Need to check if we have already failed If (OutOfSpecArray<0, vPos> NE True$) then Begin Case Case (USL NE '') and (Value NE '') OutOfSpecArray<0, vPos> = (Value GT USL) Case (USL NE '') and (Value EQ '') OutOfSpecArray<0, vPos> = -1 ; // -1 = data value is missing ErrorMsg = 'Error retrieving metrology data from SPC for RDS ':RDSNo:'.' Case Otherwise$ OutOfSpecArray<0, vPos> = False$ End Case end Next Parameter RunStageWfrRec = ValueSet RunStageWfrRec = OutOfSpecArray Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) Next WfrID end else Delimiter = @VM PostSplit = '' WaferCharacterized = '' Locate Stage in GaNMetStages using @FM setting fPos then If fPos LT 4 then PostSplit = False$ end else PostSplit = True$ end end Locate WfrID in InWfrIDs using @VM setting WaferPos then WaferCharacterized = CharFlags<0, WaferPos> end Convert '*' to '.' in WfrID RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) RunStageWfrRec = ParamValues OutOfSpecArray = '' For each Parameter in MetNames using @VM setting vPos Value = ParamValues<0, vPos> LSL = LSLs<0, vPos> USL = USLs<0, vPos> Target = Targets<0, vPos> MaxDecimals = GaN_Services('GetMaxDecimals', LSL:@VM:USL:@VM:Target) If Index(Value, @SVM, 1) GT 0 then // Multiple values per parameter RoundedValues = '' For each SubValue in Value using @SVM setting svPos Begin Case Case (LSL NE '') and (SubValue NE '') OutOfSpecArray<0, vPos, svPos> = (SubValue LT LSL) Case (LSL NE '') and (Subvalue EQ '') OutOfSpecArray<0, vPos, svPos> = -1 ; // -1 = data value is missing ErrorMsg = 'Error retrieving metrology data from SPC for RDS ':RDSNo:'.' Case Otherwise$ OutOfSpecArray<0, vPos, svPos> = False$ End Case // Need to check if we have already failed If (OutOfSpecArray<0, vPos, svPos> NE True$) then Begin Case Case (USL NE '') and (SubValue NE '') OutOfSpecArray<0, vPos, svPos> = (SubValue GT USL) Case (USL NE '') and (Subvalue EQ '') OutOfSpecArray<0, vPos, svPos> = -1 ; // -1 = data value is missing ErrorMsg = 'Error retrieving metrology data from SPC for RDS ':RDSNo:'.' Case Otherwise$ OutOfSpecArray<0, vPos, svPos> = False$ End Case end Next SubValue end else // One value per parameter If Value NE '' then Value = SRP_Math('ROUND', Value, MaxDecimals) // Ensures decimal places are retained and zeros are padded. Value = Oconv(Iconv(Value, 'MD':MaxDecimals:'L'), 'MD':MaxDecimals:'L') end Begin Case Case (LSL NE '') and (Value NE '') OutOfSpecArray<0, vPos> = (Value LT LSL) Case (LSL NE '') and (Value EQ '') OutOfSpecArray<0, vPos> = -1 ; // -1 = data value is missing ErrorMsg = 'Error retrieving metrology data from SPC for RDS ':RDSNo:'.' Case Otherwise$ OutOfSpecArray<0, vPos> = False$ End Case // Need to check if we have already failed If (OutOfSpecArray<0, vPos> NE True$) then Begin Case Case (USL NE '') and (Value NE '') OutOfSpecArray<0, vPos> = (Value GT USL) Case (USL NE '') and (Value EQ '') OutOfSpecArray<0, vPos> = -1 ; // -1 = data value is missing ErrorMsg = 'Error retrieving metrology data from SPC for RDS ':RDSNo:'.' Case Otherwise$ OutOfSpecArray<0, vPos> = False$ End Case end end Next Parameter RunStageWfrRec = ParamValues RunStageWfrRec = OutOfSpecArray Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) end If OrigReactRunRec NE ReactRunRec then Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end If Stage EQ 'WARP' then GaN_Services('CheckForCriticalFailures', RDSNo, Tool) end end If ErrorMsg NE '' then Error_Services('Add', ErrorMsg) end service Service GetPlatoData(RDSNo, ToolID) If ( (RDSNo NE '') and (ToolID NE '') ) then WONo = Xlate('REACT_RUN', RDSNo, 'WO_NO', 'X') EpiPartNo = Xlate('WO_LOG', WONo, 'EPI_PART_NO', 'X') GaNRunID = Xlate('REACT_RUN', RDSNo, 'GAN_RUN_ID', 'X') ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) OrigReactRunRec = ReactRunRec PSNo = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X') NCRFlags = ReactRunRec InWfrIDs = ReactRunRec NumWfrs = DCount(InWfrIDs, @VM) DispFlags = ReactRunRec Grades = ReactRunRec Reasons = ReactRunRec CharFlags = ReactRunRec RunScrapped = ReactRunRec GaNMetStages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_MET_STAGES') GaNParamKey = '' GaNParamKeys = Xlate('REACT_RUN', RDSNo, 'GAN_PARAM_KEYS', 'X') Found = False$ For each GaNParamKey in GaNParamKeys using @VM setting vPos Loc = Index(GaNParamKey, 'PLATO', 1) If Loc GT 0 then Found = True$ Until Found EQ True$ Next GaNParamKey GaNParamRec = Database_Services('ReadDataRow', 'GAN_PARAMS', GaNParamKey) MetNames = GaNParamRec LSLs = GaNParamRec USLs = GaNParamRec Targets = GaNParamRec RunNo = ReactRunRec Reactor = 'R':ReactRunRec IQSToolID = Xlate('TOOL', ToolID, 'IQS_ID', 'X') Query = '' If ( (EpiPartNo NE '') and (GaNRunID NE '') ) then Query = "select " Query := "[Wafer Pocket], " Query := "[Epi Thickness Mean Plato], " Query := "[Plato Thickness Std Dev %] " Query := "from ( " Query := " select se_sgrp, se_tsno, jd_name, pl_name, pd_name, " Query := " max(case when dg_name = 'Wafer ID' then dd_name end) [Wafer ID], " Query := " max(case when dg_name = 'Wafer Pocket' then dd_name end) [Wafer Pocket], " Query := " max(case when dg_name = 'Wafer Scribe' then dd_name end) [Wafer Scribe], " Query := " max(case when td_name = 'Epi Thickness Mean Plato' then se_val end) [Epi Thickness Mean Plato], " Query := " max(case when td_name = 'Plato Thickness Std Dev %' then se_val end) [Plato Thickness Std Dev %] " Query := " from ( " Query := " select se.f_sgrp se_sgrp, " Query := " se.f_tsno se_tsno, " Query := " se.f_val se_val, " Query := " jd.f_name jd_name, " Query := " pl.f_name pl_name, " Query := " pd.f_name pd_name, " Query := " dg.f_name dg_name, " Query := " dd.f_name dd_name, " Query := " td.f_name td_name " Query := " from [IRMNSPC].[dbo].[SGRP_EXT] se " Query := " join [IRMNSPC].[dbo].[SGRP_DSC] sd " Query := " on se.f_sgrp = sd.f_sgrp " Query := " and se.f_tsno = sd.f_tsno " Query := " join [IRMNSPC].[dbo].[JOB_DAT] jd " Query := " on se.f_job = jd.f_job " Query := " join [IRMNSPC].[dbo].[PART_LOT] pl " Query := " on se.f_lot = pl.f_lot " Query := " join [IRMNSPC].[dbo].[PART_DAT] pd " Query := " on se.f_part = pd.f_part " Query := " join [IRMNSPC].[dbo].[DESC_DAT] dd " Query := " on sd.f_dsgp = dd.f_dsgp " Query := " and sd.f_desc = dd.f_desc " Query := " join [IRMNSPC].[dbo].[DESC_GRP] dg " Query := " on sd.F_DSGP = dg.F_DSGP " Query := " join [IRMNSPC].[dbo].[TEST_DAT] td " Query := " on se.F_TEST = td.F_TEST " Query := " where se.F_FLAG = 0 " Query := " and jd.f_name = '":IQSToolID:"' " Query := " and pl.f_name = '":GaNRunID:"' " Query := " and pd.f_name = '":EpiPartNo:"' " Query := " and dg.f_name in ( " Query := " 'Wafer ID', " Query := " 'Wafer Pocket', " Query := " 'Wafer Scribe' " Query := " ) " Query := " and td.f_name in ( " Query := " 'Epi Thickness Mean Plato', " Query := " 'Plato Thickness Std Dev %' " Query := " ) " Query := " ) as tableA " Query := " group by se_sgrp, " Query := " se_tsno, " Query := " jd_name, " Query := " pl_name, " Query := " pd_name " Query := ") as tableB " Query := "where [Wafer Scribe] != '' " Query := " and [Wafer ID] != '00' " Query := "order by se_sgrp desc, " Query := " se_tsno" If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query) Convert @FM to @VM in Response Convert @RM to @FM in Response ParamValues = '' NumRows = DCount(Response, @FM) For RowIndex = NumRows to 1 Step -1 Row = Response Pocket = Row<0, 1> EpiThickMean = Row<0, 2> EpiStdDev = Row<0, 3> ParamValues = EpiThickMean ParamValues = EpiStdDev Next RowIndex ChangeDetected = False$ For each WfrID in InWfrIDs using @VM setting WaferPos ValueSet = ParamValues Delimiter = @VM Convert '*' to '.' in WfrID RunStageWfrKey = RDSNo:'*PLATO*':WfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey) RunStageWfrRec = ValueSet OutOfSpecArray = '' For each Parameter in MetNames using @VM setting vPos Value = ValueSet<0, vPos> LSL = LSLs<0, vPos> USL = USLs<0, vPos> Target = Targets<0, vPos> MaxDecimals = GaN_Services('GetMaxDecimals', LSL:@VM:USL:@VM:Target) If Value NE '' then Value = SRP_Math('ROUND', Value, MaxDecimals) // Ensures decimal places are retained and zeros are padded. Value = Oconv(Iconv(Value, 'MD':MaxDecimals:'L'), 'MD':MaxDecimals:'L') end ValueSet<0,vPos> = Value // One value per parameter Begin Case Case (LSL NE '') and (Value NE '') OutOfSpecArray<0, vPos> = (Value LT LSL) Case (LSL NE '') and (Value EQ '') OutOfSpecArray<0, vPos> = -1 ; // -1 = data value is missing ErrorMsg = 'Error retrieving metrology data from SPC for RDS ':RDSNo:'.' Case Otherwise$ OutOfSpecArray<0, vPos> = False$ End Case // Need to check if we have already failed If (OutOfSpecArray<0, vPos> NE True$) then Begin Case Case (USL NE '') and (Value NE '') OutOfSpecArray<0, vPos> = (Value GT USL) Case (USL NE '') and (Value EQ '') OutOfSpecArray<0, vPos> = -1 ; // -1 = data value is missing ErrorMsg = 'Error retrieving metrology data from SPC for RDS ':RDSNo:'.' Case Otherwise$ OutOfSpecArray<0, vPos> = False$ End Case end Next Parameter RunStageWfrRec = ValueSet RunStageWfrRec = OutOfSpecArray Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$) Next WfrID end end end service //---------------------------------------------------------------------------------------------------------------------- // CheckForCriticalFailures // // //---------------------------------------------------------------------------------------------------------------------- Service CheckForCriticalFailures(RDSNo, Tool) If RDSNo NE '' then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) OrigReactRunRec = ReactRunRec InWfrIDs = ReactRunRec PSNo = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X') TechType = Xlate('PROD_SPEC', PSNo, 'TECH_TYPE', 'X') GaNMetStages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_MET_STAGES') RunNo = ReactRunRec Reactor = 'R':ReactRunRec PartNo = Xlate('REACT_RUN', RDSNo, 'EPI_PART_NO', 'X') IQSToolID = Xlate('TOOL', Tool, 'IQS_ID', 'X') Query = "SELECT td.F_NAME, se.F_TSNO, SE.F_SBNO, se.F_VAL " Query := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se " Query := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT = pl.F_LOT " Query := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST " Query := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART " Query := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS " Query := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP " Query := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC " Query := "WHERE pd.F_NAME = '":PartNo:"' AND " Query := " pr.F_NAME = '":Reactor:"' AND " Query := " pl.F_NAME = '":RunNo:"' AND " Query := " se.F_FLAG = 0 AND " Query := " (" MetNames = 'Bow':@VM:'Warp' NumMetNames = DCount(MetNames, @VM) For each MetName in MetNames using @VM setting vPos If vPos EQ NumMetNames then Query := "td.F_NAME = '":MetName:"') AND " end else Query := "td.F_NAME = '":MetName:"' or " end Next MetName Query := "dd.F_NAME IS NOT NULL " Query := "ORDER BY td.F_NAME" If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query) If ( (Response NE '') and (Response NE 0) ) then Convert @FM to @VM in Response Convert @RM to @FM in Response ParamValues = '' For each MetName in MetNames using @VM setting mPos For each Row in Response using @FM setting fPos Locate MetName in Row using @VM setting vPos then DataPointIndex = Response If ( (Index(Row, @SVM, 1) GT 0) and (DataPointIndex EQ 0) ) else // Data point at index 0 for parameters with multiple data points is an average. // Ignore this data point. WaferPos = Response ParamValues = Response end end Next Row Next MetName For each WfrID in InWfrIDs using @VM setting WaferPos WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) OrigCritFailure = WOWfrRec CriticalFailure = False$ ValueSet = ParamValues Delimiter = @VM OutOfSpecArray = '' For each Parameter in MetNames using @VM setting vPos Value = ValueSet<0, vPos> // Check critical parameters If Parameter EQ 'Bow' then CriticalMin = Xlate('PROD_SPEC', PSNo, 'CRITICAL_BOW_MIN', 'X') CriticalMax = Xlate('PROD_SPEC', PSNo, 'CRITICAL_BOW_MAX', 'X') If ( (CriticalMin NE '') and (CriticalMax NE '') ) then If Value LT CriticalMin or Value GT CriticalMax then CriticalFailure = True$ end end If Parameter EQ 'Warp' then CriticalMax = Xlate('PROD_SPEC', PSNo, 'CRITICAL_WARP_MAX', 'X') If CriticalMax NE '' then If Value GT CriticalMax then CriticalFailure = True$ end end Next Parameter Begin Case Case ( (CriticalFailure EQ True$) and (OrigCritFailure NE True$) ) GaN_Services('AbortWafer', WfrID) Case ( (CriticalFailure EQ False$) and (OrigCritFailure EQ True$) ) GaN_Services('RestoreWafer', WfrID) Case Otherwise$ Null End Case Next WfrID end end end service //---------------------------------------------------------------------------------------------------------------------- // ConsumeInboundMaterial // // //---------------------------------------------------------------------------------------------------------------------- Service ConsumeInboundMaterial(InWfrIDs) If InWfrIDs NE ''then For each InWfrID in InWfrIDs using @VM // Mark inbound material consumed (i.e. move WfrID to PrevWfrID within WO_MAT_WFR record) WONo = Field(InWfrID, '*', 1, 1) InboundCass = Field(InWfrID, '*', 2, 1) SlotNo = Field(InWfrID, '*', 3, 1) WOMatWfrKey = WONo:'*':InboundCass WOMatWfrRec = Database_Services('ReadDataRow', 'WO_MAT_WFR', WOMatWfrKey) ReadyQ = WOMatWfrRec ConsumedQ = WOMatWfrRec ReadyQ<0, SlotNo> = '' ConsumedQ<0, SlotNo> = InWfrID WOMatWfrRec = ReadyQ WOMatWfrRec = ConsumedQ Database_Services('WriteDataRow', 'WO_MAT_WFR', WOMatWfrKey, WOMatWfrRec, True$, False$, True$) Next InWfrID end end service //---------------------------------------------------------------------------------------------------------------------- // GetInvActions // // //---------------------------------------------------------------------------------------------------------------------- Service GetInvActions(StageID) If StageID NE '' then GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStageInfo = SRP_Array('Rotate', GaNStageInfo, @FM, @VM) StageIDs = GaNStageInfo<1> InvActions = GaNStageInfo<7> Locate StageID in StageIDs using @VM setting vPos then Response = InvActions<0, vPos> Convert @SVM to @VM in Response end end end service //---------------------------------------------------------------------------------------------------------------------- // GetSigReq // // //---------------------------------------------------------------------------------------------------------------------- Service GetSigReq(StageID) If StageID NE '' then GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStageInfo = SRP_Array('Rotate', GaNStageInfo, @FM, @VM) StageIDs = GaNStageInfo<1> SigReqs = GaNStageInfo<8> Locate StageID in StageIDs using @VM setting vPos then Response = SigReqs<0, vPos> end end If Response EQ '' then Response = False$ end service //---------------------------------------------------------------------------------------------------------------------- // GetReqStages // // //---------------------------------------------------------------------------------------------------------------------- Service GetReqStages() Response = '' GaNStageInfo = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES') GaNStageInfo = SRP_Array('Rotate', GaNStageInfo, @FM, @VM) StageIDs = GaNStageInfo<1> StageReq = GaNStageInfo<9> For each StageID in StageIDs using @VM setting vPos If StageReq<0, vPos> EQ True$ then Response<0, -1> = StageID Next StageID end service //---------------------------------------------------------------------------------------------------------------------- // AddStageToPSN // // //---------------------------------------------------------------------------------------------------------------------- Service AddStageToPSN(PSNo, StageID) Response = False$ If ( (PSNo NE '') and (StageID NE '') ) then PSRec = Database_Services('ReadDataRow', 'PROD_SPEC', PSNo) PRSStageKeys = PSRec PRSStageKeysPrime = SRP_Array('Rotate', PRSStageKeys, @VM, '*') PRSStages = PRSStageKeysPrime<0, 2> Locate StageID in PRSStages using @VM setting vPos else // Stage not yet in the PSN, so add it now ToolClasses = GaN_Services('GetToolClassIDs', StageID) InvActions = GaN_Services('GetInvActions', StageID) SigReq = GaN_Services('GetSigReq', StageID) PRSStageKey = PSNo:'*':StageID PRSStageRec = '' PRSStageRec = ToolClasses If InvActions NE '' then PRSStageRec = InvActions If SigReq EQ True$ then For each InvAction in InvActions using @VM setting vPos PRSStageRec = True$ Next InvAction end end Database_Services('WriteDataRow', 'PRS_STAGE', PRSStageKey, PRSStageRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end end service //---------------------------------------------------------------------------------------------------------------------- // RemoveStageFromPSN // // //---------------------------------------------------------------------------------------------------------------------- Service RemoveStageFromPSN(PSNo, StageID) Response = False$ If ( (PSNo NE '') and (StageID NE '') ) then PRSStageKey = PSNo:'*':StageID If RowExists('PRS_STAGE', PRSStageKey) then Database_Services('DeleteDataRow', 'PRS_STAGE', PRSStageKey, True$, False$) If Error_Services('NoError') then Response = True$ end end end service //---------------------------------------------------------------------------------------------------------------------- // UpdateRetainedWafers // // //---------------------------------------------------------------------------------------------------------------------- Service UpdateRetainedWafers() hSysLists = Database_Services('GetTableHandle', 'SYSLISTS') Lock hSysLists, ServiceKeyID then UpdateFlag = Database_Services('ReadDataRow', 'APP_INFO', 'UPDATE_RETAINED_WAFERS_FLAG') If UpdateFlag EQ True$ then // Clear update flag UpdateFlag = False$ Database_Services('WriteDataRow', 'APP_INFO', 'UPDATE_RETAINED_WAFERS_FLAG', UpdateFlag, True$, False$, True$) // Update wafer lists hDict = Database_Services('GetTableHandle', 'DICT.RETAINED_WAFERS') If Error_Services('NoError') then Database_Services('WriteDataRow', 'APP_INFO', 'UPDATE_RETAINED_WAFERS_FLAG', False$, True$, False$, True$) WaferSizes = '150 mm 6 in' : @VM : '200 mm 8 in' WaferListKeys = 'RETAINED_WAFERS_6IN' : @VM : 'RETAINED_WAFERS_8IN' // Update 6 and 8 inch wafer lists For each WaferSize in WaferSizes using @VM setting SizePos WaferListKey = WaferListKeys<0, SizePos> HaveLock = Database_Services('GetKeyIDLock', 'APP_INFO', WaferListKey, True$) If HaveLock EQ True$ then // OK to update WaferList = '' Query = 'WAFER_SIZE' : @VM : WaferSize : @FM KeyList = '' Option = '' Flag = '' Btree.Extract(Query, 'RETAINED_WAFERS', hDict, KeyList, Option, Flag) NumKeys = DCount(KeyList, @VM) If Get_Status(ErrCode) else For each WfrID in keylist using @VM setting RowIndex WfrRec = Database_Services('ReadDataRow', 'RETAINED_WAFERS', WfrID) WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = Field(WfrID, '*', 1) WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrRec WaferList = WfrID Next WfrID end Database_Services('WriteDataRow', 'APP_INFO', WaferListKey, WaferList, True$, False$, True$) Database_Services('ReleaseKeyIDLock', 'APP_INFO', WaferListKey) end Next WaferSize end end Unlock hSysLists, ServiceKeyID else Null end end service //---------------------------------------------------------------------------------------------------------------------- // UpdateRetainedWaferDetails // // Input: // WaferID - [Required] // Location - [Optional] // Status - [Optional] // Comments - [Optional] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service UpdateRetainedWaferDetails(InboundWfrID, Location, Status, Comments) Response = False$ If InboundWfrID NE '' then WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', InboundWfrID) If Error_Services('NoError') then WOWfrRec = Location WOWfrRec = Status WOWfrRec = Comments Database_Services('WriteDataRow', 'WO_WFR', InboundWfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end end service //---------------------------------------------------------------------------------------------------------------------- // GetCurrPartParams // // //---------------------------------------------------------------------------------------------------------------------- Service GetCurrPartParams(PartNo) Stages = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_IQS_STAGES') Swap 'JV-XRD' with 'JV_XRD' in Stages ParameterList = '' Query = "SELECT pr.Process_Name, pr.IQS_Name, pp.Parameter " Query := "FROM [ControlPlans].[dbo].[Part_Parameter] pp " Query := "JOIN [ControlPlans].[dbo].[Parameter] pr on pp.Parameter = pr.Parameter " Query := "WHERE pp.Part = '":PartNo:"' AND (" For each Stage in Stages using @VM Query := "pr.Process_Name = '":Stage:"' OR " Next Stage Query[-1, -4] = "" Query := ") ORDER BY pr.Process_Name, pr.IQS_Name" Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query) RowCount = 0 If Response NE 0 then For each Row in Response using @RM setting RowPos RowCount += 1 ProcName = Row<1> MetName = Row<2> ParamName = Row<3> Locate ProcName in Stages using @VM setting fPos then vPos = DCount(ParameterList, @VM) vPos += 1 ParameterList = MetName ParameterList = ParamName end Next Row end * Response = ParameterList // Copy UV to UV_PST ParameterList<7> = ParameterList<3> UVData = ParameterList<7> For each Row in UVData using @VM setting vPos MetName = ParameterList<7, vPos, 1> MetName = MetName:' Post' ParameterList<7, vPos, 1> = MetName Next Row // Copy CAN to CAN_PST ParameterList<8> = ParameterList<6> CANData = ParameterList<8> For each Row in CANData using @VM setting vPos MetName = ParameterList<8, vPos, 1> MetName = MetName:' Post' ParameterList<8, vPos, 1> = MetName Next Row // Manually create BV100% parameter ParameterList<17, 1, 1> = '100% BV Failure %' ParameterList<17, 1, 2> = '100% BV Failure %' // Hard code Plato limits for now * ParameterList<5, 1, 1> = 'Epi Thickness Mean Plato' * ParameterList<5, 1, 2> = 'Epi Thickness Mean Plato' * ParameterList<5, 2, 1> = 'Plato Thickness Std Dev %' * ParameterList<5, 2, 2> = 'Plato Thickness Std Dev %' * // Get GROWTH parameters * Query = 'SELECT DISTINCT pr.Process_Name, pr.IQS_Name, pp.Parameter ' * Query := 'From [ControlPlans].[dbo].[Part_Parameter] pp ' * Query := 'JOIN [ControlPlans].[dbo].[Parameter] pr on pp.Parameter = pr.Parameter ' * Query := "WHERE pr.Process_Name = 'Epi' AND pp.Part = '":PartNo:"'" * Query := 'ORDER BY pr.IQS_Name' * Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query) * * If Response NE 0 then * For each Row in Response using @RM setting RowPos * RowCount += 1 * ProcName = Row<1> * MetName = Row<2> * ParamName = Row<3> * Locate ProcName in Stages using @VM setting fPos then * vPos = DCount(ParameterList, @VM) * vPos += 1 * ParameterList = MetName * ParameterList = ParamName * end * Next Row * end * * // Get TEMP parameters * Query = 'SELECT DISTINCT pr.Process_Name, pr.IQS_Name, pp.Parameter ' * Query := 'From [ControlPlans].[dbo].[Part_Parameter] pp ' * Query := 'JOIN [ControlPlans].[dbo].[Parameter] pr on pp.Parameter = pr.Parameter ' * Query := "WHERE pr.Process_Name = 'Temperature' " * Query := 'ORDER BY pr.IQS_Name' * Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query) * If Response NE 0 then * For each Row in Response using @RM setting RowPos * RowCount += 1 * ProcName = Row<1> * MetName = Row<2> * ParamName = Row<3> * Locate ProcName in Stages using @VM setting fPos then * vPos = DCount(ParameterList, @VM) * vPos += 1 * ParameterList = MetName * ParameterList = ParamName * end * Next Row * end Response = ParameterList end service //---------------------------------------------------------------------------------------------------------------------- // GetCurrLimits // // Input - PartNo // Output - LSL, Target, and USL from the Control Plan SQL DB. //---------------------------------------------------------------------------------------------------------------------- Service GetCurrLimits(PartNo) Query = "SELECT [Parameter] " | : ",[LSL] " | : ",[Target] " | : ",[USL] " | : "FROM [ControlPlans].[dbo].[Part_Parameter] " | : "WHERE [Part] = '":PartNo:"' " | : "ORDER BY [Parameter]" Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query) Convert @FM to @VM in Response Convert @RM to @FM in Response Response = SRP_Array('Rotate', Response, @FM, @VM) // Hard code 100% BV Failure % limits Response<1, -1> = '100% BV Failure %' Response<2, -1> = '' Response<3, -1> = '' Response<4, -1> = 10 // Hard code Plato limits Response<1, -1> = 'Epi Thickness Mean Plato' Response<2, -1> = 0 ; // LSL Response<3, -1> = 1.5 ; // Target Response<4, -1> = 3 ; // USL // Hard code Plato limits Response<1, -1> = 'Plato Thickness Std Dev %' Response<2, -1> = -1 ; // LSL Response<3, -1> = 0 ; // Target Response<4, -1> = 1 ; // USL end service //---------------------------------------------------------------------------------------------------------------------- // GetMaxDecimals // // Input - @VM delimited array of values containing decimals // Output - Maximum number of decimal places found between all values in the array. //---------------------------------------------------------------------------------------------------------------------- Service GetMaxDecimals(Array) MaxDecimals = 0 If Array NE '' then For each Value in Array using @VM DecimalFound = Index(Value, '.', 1) If DecimalFound then NumDecimalPlaces = Len(Value[-1, 'B.']) If NumDecimalPlaces GT MaxDecimals then MaxDecimals = NumDecimalPlaces end Next Value end Response = MaxDecimals end service //---------------------------------------------------------------------------------------------------------------------- // ClearRetainInfo // // Input: // InboundWfrID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service ClearRetainInfo(InboundWfrID) Response = False$ If InboundWfrID NE '' then // InboundWfrID may have been passed in with '.' instead of '*' Convert '.' to '*' in InboundWfrID WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', InboundWfrID) If Error_Services('NoError') then RetainBox = WOWfrRec RetainSlot = WOWfrRec If ( (RetainBox NE '') and (RetainSlot NE '') ) then WOWfrRec = '' WOWfrRec = '' Database_Services('WriteDataRow', 'WO_WFR', InboundWfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then RDSNo = Xlate('WO_WFR', InboundWfrID, 'RDS_NO', 'X') If RDSNo NE '' then RetainStageKey = RDSNo:'*RETAIN*':InboundWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RetainStageKey) RunStageWfrRec = '' RunStageWfrRec = '' RunStageWfrRec = 'INIT' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RetainKey, RunStageWfrRec, True$, False$, True$) // Move wafer into the DISP queue, if it isn't already there CurrQ = GaN_Services('GetWfrQueue', InboundWfrID) DispQ = GaN_Services('GetLocQueueID', 'DISP') If CurrQ NE DispQ then Gan_Services('RemoveWfrFromWIP', WfrID) GaN_Services('MoveWfrToQueue', InboundWfrID, DispQ) end ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) InWfrIDs = ReactRunRec Locate InboundWfrID in InWfrIDs using @VM setting vPos then ReactRunRec = False$ Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end Response = True$ end end end end end end service //---------------------------------------------------------------------------------------------------------------------- // RetainWafer // // Input: // WaferID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service RetainWafer(InboundWfrID) Response = False$ If InboundWfrID NE '' then WfrSize = Xlate('WO_WFR', InboundWfrID, 'WFR_SIZE', 'X') WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', InboundWfrID) If Error_Services('NoError') then OrigRetainBox = WOWfrRec OrigRetainSlot = WOWfrRec If ( (OrigRetainBox EQ '') and (OrigRetainSlot EQ '') ) then // Wafer not yet retained, so proceed RetainKey = Gan_Services('GetNextRetainKey', WfrSize) If RetainKey NE '' then RetainBox = RetainKey<1> RetainSlot = RetainKey<2> WOWfrRec = RetainBox WOWfrRec = RetainSlot Database_Services('WriteDataRow', 'WO_WFR', InboundWfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then // Move wafer to RETAIN queue // Move wafer into the RETAIN queue, if it isn't already there CurrQ = GaN_Services('GetWfrQueue', InboundWfrID) RetainQ = GaN_Services('GetLocQueueID', 'RETAIN') If CurrQ NE RetainQ then GaN_Services('RemoveWfrFromWIP', WfrID) GaN_Services('MoveWfrToQueue', InboundWfrID, RetainQ) end If Error_Services('NoError') then RDSNo = Xlate('WO_WFR', InboundWfrID, 'RDS_NO', 'X') If RDSNo NE '' then Convert '*' to '.' in InboundWfrID RetainStageKey = RDSNo:'*RETAIN*':InboundWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RetainStageKey) StageStatus = RunStageWfrRec If StageStatus NE 'COMP' then RunStageWfrRec = 'AUTO' RunStageWfrRec = Datetime() RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RetainStageKey, RunStageWfrRec, True$, False$, True$) end DispStageKey = RDSNo:'*DISP*':InboundWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', DispStageKey) StageStatus = RunStageWfrRec If StageStatus NE 'COMP' then RunStageWfrRec = 'AUTO' RunStageWfrRec = Datetime() RunStageWfrRec = 'COMP' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', DispStageKey, RunStageWfrRec, True$, False$, True$) end ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) InWfrIDs = ReactRunRec Convert '.' to '*' in InboundWfrID Locate InboundWfrID in InWfrIDs using @VM setting vPos then // This flag indicates the wafer is a retained wafer. // This write to the REACT_RUN record will also trigger an MFS to copy the // recipe and part number to the RETAINED_WAFERS record. ReactRunRec = True$ Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end end If Error_Services('NoError') then Response = True$ end end end else Error_Services('Add', 'Error retrieving retain key in the ':Service:' service.') end end end end end service //---------------------------------------------------------------------------------------------------------------------- // SignRetainedWafer // // Input: // WaferID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service SignRetainedWafer(InboundWfrID) Response = False$ If InboundWfrID NE '' then WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', InboundWfrID) If Error_Services('NoError') then WOWfrRec = @User4 WOWfrRec = Date() WOWfrRec = 'Cleanroom' Database_Services('WriteDataRow', 'WO_WFR', InboundWfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Disposition_Services('AutoDispositionWfr', InboundWfrID) If Error_Services('NoError') then Response = True$ end end end end service //---------------------------------------------------------------------------------------------------------------------- // SignDestroyedWafer // // Input: // WaferID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise // //---------------------------------------------------------------------------------------------------------------------- Service SignDestroyedWafer(InboundWfrID) Response = False$ If InboundWfrID NE '' then WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', InboundWfrID) If Error_Services('NoError') then WOWfrRec = @User4 WOWfrRec = Date() WOWfrRec = 'Wafer Destroyed' Database_Services('WriteDataRow', 'WO_WFR', InboundWfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end end service //---------------------------------------------------------------------------------------------------------------------- // GetNextRetainKey // // Input: // WfrSize - [Required] // // Output: // Response - Next retain key for that wafer size. // //---------------------------------------------------------------------------------------------------------------------- Service GetNextRetainKey(WfrSize) Response = '' If WfrSize NE '' then Begin Case Case WfrSize EQ '150 mm 6 in' RetainKeyID = 'RETAIN_KEY_6IN' Case WfrSize EQ '200 mm 8 in' RetainKeyID = 'RETAIN_KEY_8IN' Case Otherwise$ Error_Services('Add', 'Null or incompatible wafer size in ':Service:' service.') End Case If Error_Services('NoError') then CurrRetainKey = Database_Services('ReadDataRow', 'APP_INFO', RetainKeyID) CurrRetainBox = CurrRetainKey<1> CurrRetainSlot = CurrRetainKey<2> // Update Retain Key (i.e. Box and Slot) NewRetainBox = CurrRetainBox If CurrRetainSlot GT 1 then NewRetainSlot = CurrRetainSlot - 1 end else NewRetainSlot = 25 NewBoxIndex = CurrRetainBox[6, 3] NewBoxIndex += 1 NewBoxIndex = Fmt(NewBoxIndex,"R(0)#3") NewRetainBox[6, 3] = NewBoxIndex end NewRetainKey = '' NewRetainKey<1> = NewRetainBox NewRetainKey<2> = NewRetainSlot Database_Services('WriteDataRow', 'APP_INFO', RetainKeyID, NewRetainKey, True$, False$, True$) If Error_Services('NoError') then Response = CurrRetainKey end end end service //---------------------------------------------------------------------------------------------------------------------- // GetAvailableTools // // Input: // StageID - [Required] // // Output: // Response - All tools for a given stage that are currently running in PROD mode. // //---------------------------------------------------------------------------------------------------------------------- Service GetAvailableTools(StageID) Response = '' If StageID NE '' then ToolClassIDs = GaN_Services('GetToolClassIDs', StageID) TempArray = '' For each ToolClassID in ToolClassIDs using @SVM setting svPos ToolIDs = Xlate('TOOL_CLASS', ToolClassID, 'TOOL', 'X') ProdTools = '' For each ToolID in ToolIDs using @VM setting vPos CurrMode = Xlate('TOOL', ToolID, 'CURR_MODE', 'X') If CurrMode EQ 'PROD' then ProdTools<0, -1> = ToolID Next ToolID TempArray = SRP_Array('Join', TempArray, ProdTools, 'OR', @VM) Next ToolClassID Response = TempArray end end service //---------------------------------------------------------------------------------------------------------------------- // GetWaferStatusList // // Input: // RDSNo - [Required] // // Output: // Response - A list of wafers in a given RDS and their current status. // //---------------------------------------------------------------------------------------------------------------------- Service GetWaferStatusList(RDSNo) Response = '' If RDSNo NE '' then WfrStatusList = '' InWfrIDs = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X') For each WfrID in InWfrIDs using @VM setting WfrIndex WfrStatusList = WfrID WfrStatusList = Xlate('WO_WFR', WfrID, 'STATUS', 'X') Next WfrID Response = WfrStatusList end end service //---------------------------------------------------------------------------------------------------------------------- // AbortWafer // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service AbortWafer(WfrID) Response = False$ If WfrID NE '' then // WfrID may have been passed in with . instead of * Convert '.' to '*' in WfrID WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') WfrStages = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X') Locate 'WARP' in WfrStages using @VM setting StageIndex then // Skip all remaining stages, mark wafer as dispositioned at the SPLIT stage Loop StageIndex += 1 StageID = WfrStages<0, StageIndex> Until StageID EQ 'SPLIT' GaN_Services('SkipWaferStage', RDSNo, WfrID, StageID) Repeat GaN_Services('DispositionWfr', RDSNo, WfrID, True$) end // Set critical failure flag (AutoDisposition will set the grades based on this flag) WOWfrRec = True$ Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end service //---------------------------------------------------------------------------------------------------------------------- // RestoreWafer // // Input: // WfrID - [Required] // // Output: // Response - True$ (1) if successful, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service RestoreWafer(WfrID) Response = False$ If WfrID NE '' then // WfrID may have been passed in with . instead of * Convert '.' to '*' in WfrID RDSNo = Xlate('WO_WFR', WfrID, 'RDS_NO', 'X') GaN_Services('WithdrawWfr', RDSNo, WfrID) WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) WfrStages = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X') Convert '*' to '.' in WfrID For each StageID in WfrStages using @VM setting StageIndex Until StageID EQ 'SPLIT' RunStageWfrKey = RDSNo:'*':StageID:'*':WfrID StageStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X') If StageStatus EQ 'SKIP' then GaN_Services('UnskipWaferStage', RDSNo, WfrID, StageID) Next StageID Convert '.' to '*' in WfrID // Set critical failure flag (AutoDisposition will set the grades based on this flag) WOWfrRec = False$ Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) If Error_Services('NoError') then Response = True$ end end service //---------------------------------------------------------------------------------------------------------------------- // PostDispositionRequest // // Input: // RDSNo - [Required] // //---------------------------------------------------------------------------------------------------------------------- Service PostDispositionRequest(RDSNo) Response = '' If RDSNo NE '' then RequestDate = Date() RequestTime = Time() RequestKeyID = RDSNo:'*':RequestDate :'*':RequestTime RequestRow = '' Database_Services('WriteDataRow', 'DISPOSITION_REQUESTS', RequestKeyID, RequestRow, False$, False$, False$) If Error_Services('NoError') then TimeExpired = False$ Start = GetTickCount() Loop RequestRow = Database_Services('ReadDataRow', 'DISPOSITION_REQUESTS', RequestKeyID) ResponseDate = RequestRow Response = RequestRow // Time will expire after 30 seconds. If GetTickCount() - Start GE 60000 then TimeExpired = True$ Until (ResponseDate NE '') OR TimeExpired Sleepery(10) WinYield() Yield();Yield();Yield();Yield();Yield();Yield();Yield();Yield() Repeat If TimeExpired then Error_Services('Add', 'Disposition request timed out in the ' : Service : ' service.') end Database_Services('DeleteDataRow', 'DISPOSITION_REQUESTS', RequestKeyID, True$) end end else Error_Services('Add', 'Null parameter passed into service call. All parameters are required.') end end service //---------------------------------------------------------------------------------------------------------------------- // ProcessDispositionRequests // //---------------------------------------------------------------------------------------------------------------------- Service ProcessDispositionRequests() Tablename = 'DISPOSITION_REQUESTS' hValidationRequests = Database_Services('GetTableHandle', Tablename) If Error_Services('NoError') then Sentence = 'SELECT ':Tablename:' WITH RESPONSE_DATE EQ "" BY REQUEST_DATE BY REQUEST_TIME' Set_Status(0) RList(Sentence, TARGET_ACTIVELIST$, '', '', '') EOF = False$ Loop ReadNext RequestKeyID else EOF = True$ Until EOF Lock hValidationRequests, RequestKeyID then RequestRow = Database_Services('ReadDataRow', 'DISPOSITION_REQUESTS', RequestKeyID) RDSNo = Field(RequestKeyID, '*', 1) GaN_Services('ProcessDispositionRequest', RDSNo) If Error_Services('NoError') then RequestRow = True$ end else RequestRow = Error_Services('GetMessage') end RequestRow = Date() RequestRow = Time() Database_Services('WriteDataRow', 'DISPOSITION_REQUESTS', RequestKeyID, RequestRow, True$, False$, True$) Unlock hValidationRequests, RequestKeyID else Error_Services('Add', 'Error unlocking ':RequestKeyID:', ':Tablename:' within ':Service) end end Repeat end else ErrorMsg = Error_Services('GetMessage') end end service //---------------------------------------------------------------------------------------------------------------------- // ProcessDispositionRequest // // Input: // RDSNo - [Required] // //---------------------------------------------------------------------------------------------------------------------- Service ProcessDispositionRequest(RDSNo) If RDSNo NE '' then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) If Error_Services('NoError') then OrigReactRunRec = ReactRunRec Reactor = ReactRunRec Scribes = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X') NumWfrs = DCount(Scribes, @VM) GanRunID = ReactRunRec GaNRecipe = ReactRunRec WfrIDs = ReactRunRec CassDispComp = ReactRunRec WONo = ReactRunRec WaferTrackPart = Xlate('WO_LOG', WONo, 'WAFER_TRACK_PART', 'X') If CassDispComp NE True$ then ReportFound = False$ // Search for GaN Disposition Report Excel Spreadsheet -------------------- DispFilename = GanRunID:'_':WaferTrackPart:'_Disposition_Report.xlsm' ReportPath = '\\messv02ecc1.ec.local\EC_Characterization_GaN\Disposition Reports\' InitDir ReportPath:'\*.*' RootFileList = DirList() DispFilePath = ReportPath:'\':DispFilename If Indexc(RootFileList, DispFileName, 1) then ReportFound = True$ end else // Report not found in root "working" directory, so search the archives ArchivePath = ReportPath:'\R':Reactor:' Reports\*.*' InitDir ArchivePath ArchiveFileList = DirList() If Indexc(ArchiveFileList, DispFileName, 1) then ReportFound = True$ DispFilePath = ReportPath:'\R':Reactor:' Reports\':DispFilename end else // Disposition report not found. Set status line to inform user Set_Property(@Window:'.STATUS_LINE', 'BACKCOLOR', YELLOW$) Set_Property(@Window:'.STATUS_LINE', 'TEXT', 'Disposition report "':DispFileName:'" not found!') end end If ReportFound EQ True$ then Set_Property(@Window:'.STATUS_LINE', 'BACKCOLOR', GREEN$) DispExcelHandle = Excel_Services('OpenDocument', DispFilePath) DispWorksheet = 'Section E-Lot Summary Report' ExcelScribes = '' ExcelGrades = '' ExcelShipFlags = '' ExcelRetainFlags = '' ExcelReasons = '' ExcelNCRReqFlags = '' For WfrIndex = 1 to NumWfrs WfrID = WfrIDs<0, WfrIndex> WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) FormattedWfrID = WfrID Convert '*' to '.' in FormattedWfrID CellRow = WfrIndex + 6 ExcelScribe = Excel_Services('GetCellValue', DispExcelHandle, DispWorksheet, 'D', CellRow) ExcelScribes<0, WfrIndex> = ExcelScribe MetComp = Gan_Services('GetMetrologyStatus', WfrID) DispComp = Gan_Services('GetDispStatus', WfrID) If ( (MetComp EQ True$) or (DispComp EQ True$) ) then ExcelGrade = Excel_Services('GetCellValue', DispExcelHandle, DispWorksheet, 'O', CellRow) ExcelReason = Excel_Services('GetCellValue', DispExcelHandle, DispWorksheet, 'P', CellRow) ExcelRetainFlag = Excel_Services('GetCellValue', DispExcelHandle, DispWorksheet, 'R', CellRow) ExcelGrades<0, WfrIndex> = ExcelGrade ExcelShipFlag = (ExcelRetainFlag _EQC 'IFX-VIH') ExcelShipFlags<0, WfrIndex> = ExcelShipFlag ExcelRetainFlag = (ExcelRetainFlag _EQC 'Retained') ExcelRetainFlags<0, WfrIndex> = ExcelRetainFlag ExcelReasons<0, WfrIndex> = ExcelReason ExcelNCRReqFlags<0, WfrIndex> = (ExcelReason _EQC 'Lot Aborted') or (ExcelReason _EQC 'Non-rotation') or (ExcelReason _EQC 'Broken') WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID) RetainSlot = WOWfrRec RetainBox = WOWfrRec WOWfrGrade = WOWfrRec If WOWfrGrade NE ExcelGrade then WOWfrRec = ExcelGrade Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$) end OrigShipFlag = ReactRunRec If ( (ExcelRetainFlag EQ False$) and (RetainBox NE '') and (RetainSlot NE '') ) then // Reset RETAIN stage back to INIT because the disposition report has been updated GaN_Services('ClearRetainInfo', WfrID) end If ( (ExcelRetainFlag EQ True$ ) and (RetainBox EQ '') and (RetainSlot EQ '') ) then GaN_Services('RetainWafer', WfrID) end If ( (ExcelShipFlag EQ False$) and (OrigShipFlag EQ True$) ) then // Reset G_PACK stage back to INIT because the disposition report has been updated GPackKey = RDSNo:'*G_PACK*':FormattedWfrID RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', GPackKey) RunStageWfrRec = '' RunStageWfrRec = '' RunStageWfrRec = 'INIT' Database_Services('WriteDataRow', 'RUN_STAGE_WFR', GPackKey, RunStageWfrRec, True$, False$, True$) end end Next WfrIndex ReactRunRec = ExcelShipFlags ReactRunRec = ExcelRetainFlags ReactRunRec = ExcelNCRReqFlags ReactRunRec = ExcelReasons InternalFlags = ReactRunRec ExternalFlags = ReactRunRec For WfrIndex = 1 to NumWfrs RetainFlag = ExcelRetainFlags<0, WfrIndex> ShipFlag = ExcelShipFlags<0, WfrIndex> If (RetainFlag EQ True$) or (ShipFlag EQ True$) then InternalFlags<0, WfrIndex> = False$ ExternalFlags<0, WfrIndex> = False$ end Next WfrIndex ReactRunRec = InternalFlags ReactRunRec = ExternalFlags If OrigReactRunRec NE ReactRunRec then Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end Excel_Services('CloseDocument', DispExcelHandle) end else Error_Services('Add', 'Dispostion report not found!') end end else Error_Services('Add', 'Cannot process disposition report due to run being closed.') end end end else Error_Services('Add', 'Null RDSNo passed into service ':Service:'.') end end service Service VerifyWaferScribes(RDSNo, ToolID) ErrorMsg = '' If ( (RDSNo NE '') and (ToolID NE '') ) then GaNRunID = Xlate('REACT_RUN', RDSNo, 'GAN_RUN_ID', 'X') WfrTrackPart = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X') IQSToolID = Xlate('TOOL', ToolID, 'IQS_ID', 'X') Done = False$ If ( (GaNRunID NE '') and (WfrTrackPart NE '') and (IQSToolID NE '') ) then Begin Case Case ( (ToolID EQ 'CAN02') or (ToolID EQ 'PLATO_02') ) ColName = 'Wafer Pocket' Case ToolID EQ 'XRD04' ColName = 'Wafer ID' Case Otherwise$ Done = True$ End Case If Done EQ False$ then Query = "select [":ColName:"], [Wafer Scribe] " Query := "from ( " Query := "select sd_sgrp, sd_tsno, jd_name, pl_name, pd_name, " Query := "max(case when dg_name = '":ColName:"' then dd_name end) [":ColName:"], " Query := "max(case when dg_name = 'Wafer Scribe' then dd_name end) [Wafer Scribe] " Query := "from ( " Query := "select sd.f_sgrp sd_sgrp, jd.f_name jd_name, pl.f_name pl_name, pd.f_name pd_name, sd.f_tsno sd_tsno, dg.f_name dg_name, dd.f_name dd_name " Query := "from [IRMNSPC].[dbo].[SGRP_DSC] sd " Query := "join [IRMNSPC].[dbo].[SGRP_EXT] se " Query := "on sd.f_sgrp = se.f_sgrp " Query := "join [IRMNSPC].[dbo].[JOB_DAT] jd " Query := "on se.f_job = jd.f_job " Query := "join [IRMNSPC].[dbo].[PART_LOT] pl " Query := "on se.f_lot = pl.f_lot " Query := "join [IRMNSPC].[dbo].[PART_DAT] pd " Query := "on se.f_part = pd.f_part " Query := "join [IRMNSPC].[dbo].[DESC_DAT] dd " Query := "on sd.f_dsgp = dd.f_dsgp " Query := "and sd.f_desc = dd.f_desc " Query := "join [IRMNSPC].[dbo].[DESC_GRP] dg " Query := "on sd.F_DSGP = dg.F_DSGP " Query := "where se.F_FLAG = 0 " Query := "and jd.f_name = '":IQSToolID:"' " Query := "and pl.f_name = '":GaNRunID:"' " Query := "and dg.f_name in ('":ColName:"', 'Wafer Scribe') " Query := ") as tableA " Query := "group by sd_sgrp, sd_tsno, jd_name, pl_name, pd_name " Query := ") as tableB " Query := "where [Wafer Scribe] != '' and [":ColName:"] != '00'" Query := "order by sd_sgrp desc, sd_tsno" Response = Sql_Services('PostSQLRequest', 'IQSDMS1', Query) If Response NE '' then Convert @FM to @VM in Response Convert @RM to @FM in Response WaferData = SRP_Array('Rotate', Response, @FM, @VM) // Verify wafer scribes with what is stored in OpenInsight. // If a discrepancy is found, set a flag to prevent users from closing the RDS. ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) ScribeErrors = '' RRScribes = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X') ScribeErrors = Xlate('REACT_RUN', RDSNo, 'SCRIBE_ERROR', 'X') WaferIDs = WaferData<1> WaferScribes = WaferData<2> For each WaferID in WaferIDs using @VM setting vPos RRScribe = RRScribes<0, WaferID> ThisScribeError = (RRScribe NE WaferScribes<0, vPos>) PrevScribeError = ScribeErrors<0, WaferID> ScribeErrors<0, WaferID> = (ThisScribeError OR PrevScribeError) Next WaferID ReactRunRec = ScribeErrors Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) end end end else ErrorMsg = 'Error in ':Service:' service. Null GaNRunID, WfrTrackPart, or IQSToolID.' end end else ErrorMsg = 'Error in ':Service:' service. Null RDSNo or tool ID passed into service.' end end service Service ClearScribeErrors(RDSNo) ErrorMsg = '' If RDSNo NE '' then ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo) If Error_Services('NoError') then NumWfrs = DCount(ReactRunRec, @VM) For WfrIndex = 1 to NumWfrs ReactRunRec = False$ Next WfrIndex Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$) If Error_Services('HasError') then ErrorMsg = Error_Services('GetMessage') end end else ErrorMsg = Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. Null RDSNo passed in.' end If ErrorMsg NE '' then Error_Services('Add', ErrorMsg) Response = False$ end else Response = True$ end end service //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Internal GoSubs //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ClearCursors: For counter = 0 to 8 ClearSelect counter Next counter return