6481 lines
314 KiB
Plaintext
6481 lines
314 KiB
Plaintext
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<WO_STEP_RDS_KEY$>
|
|
For each RDSKey in RDSKeys using @VM
|
|
ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSKey)
|
|
GaNRunID = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
|
|
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<REACT_RUN_WFR_STAGE$>
|
|
SigProfileCnt = DCount(SigProfile, @VM)
|
|
NeedToWrite = False$
|
|
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
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<WO_MAT_RDS_NO$>
|
|
GRWfrQty = 0
|
|
ScrapQty = 0
|
|
ProdTWQty = 0
|
|
DummyQty = 0
|
|
EmptyQty = 0
|
|
|
|
ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
|
|
GaNRunID = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
|
|
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<REACT_RUN_WFR_STAGE$>
|
|
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<REACT_RUN_IN_WFR_ID$>
|
|
* If WONo EQ 169733 then
|
|
ProdTWQty = Sum(ReactRunRec<REACT_RUN_TEST_WAFER$>)
|
|
* 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<RowIndex, 7> = 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<GAN_SCHEDULE_WAFER_TRACK_PART$>
|
|
GaNOIPart = GanSchedRec<GAN_SCHEDULE_OPENINSIGHT_PART$>
|
|
GaNRecipe = GanSchedRec<GAN_SCHEDULE_RECIPE$>
|
|
GaNWorkOrder = GanSchedRec<GAN_SCHEDULE_WORK_ORDER$>
|
|
If (GaNWTPart NE ExcelWTPart) or (GaNOIPart NE ExcelOIPart) or (GaNRecipe NE ExcelRecipe) or (GaNWorkOrder NE ExcelWorkOrder) then
|
|
// GaN Schedule updated, update OpenInsight
|
|
GanSchedRec<GAN_SCHEDULE_WAFER_TRACK_PART$> = ExcelWTPart
|
|
GanSchedRec<GAN_SCHEDULE_OPENINSIGHT_PART$> = ExcelOIPart
|
|
GaNSchedRec<GAN_SCHEDULE_RECIPE$> = ExcelRecipe
|
|
GaNSchedRec<GAN_SCHEDULE_WORK_ORDER$> = ExcelWorkOrder
|
|
Database_Services('WriteDataRow','GAN_SCHEDULE',GaNSchedKey,GanSchedRec,True$,False$,True$)
|
|
end
|
|
end else
|
|
// Row does not yet exist, so create it.
|
|
GanSchedRec<GAN_SCHEDULE_RECIPE$> = ExcelRecipe
|
|
GanSchedRec<GAN_SCHEDULE_WAFER_TRACK_PART$> = ExcelWTPart
|
|
GanSchedRec<GAN_SCHEDULE_OPENINSIGHT_PART$> = ExcelOIPart
|
|
GanSchedRec<GAN_SCHEDULE_WORK_ORDER$> = 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<REACT_RUN_IN_WFR_ID$>
|
|
ShipFlags = ReactRunRec<REACT_RUN_CARR_WFR_SHIP$>
|
|
RetainFlags = ReactRunRec<REACT_RUN_GAN_RET_WFR$>
|
|
InternalFlags = ReactRunRec<REACT_RUN_INTERNAL$>
|
|
ExternalFlags = ReactRunRec<REACT_RUN_EXTERNAL$>
|
|
CassDispComp = ReactRunRec<REACT_RUN_DISP_COMPLETE$>
|
|
WfrsDispReady = ReactRunRec<REACT_RUN_WFRS_DISP_READY$>
|
|
NCRReqFlags = ReactRunRec<REACT_RUN_NCR_REQ$>
|
|
CharFlags = ReactRunRec<REACT_RUN_IN_WFR_CHAR$>
|
|
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<WO_WFR_RDS_NCR_NO$>
|
|
NCRStatus = Xlate('NCR', NCRNo, 'STATUS', 'X')
|
|
CriticalFailFlag = WOWfrRec<WO_WFR_CRITICAL_FAILURE$>
|
|
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<REACT_RUN_WFRS_DISP_READY$> = WfrsDispReady
|
|
WOWfrRec<WO_WFR_STATUS$> = '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<REACT_RUN_CARR_WFR_SHIP$> = ShipFlags
|
|
ReactRunRec<REACT_RUN_GAN_RET_WFR$> = RetainFlags
|
|
ReactRunRec<REACT_RUN_INTERNAL$> = InternalFlags
|
|
ReactRunRec<REACT_RUN_EXTERNAL$> = 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<WO_WFR_STATUS$> = 'Ready to close'
|
|
Case ( (NCRNo NE '') and (NCRStatus NE 'C') )
|
|
WOWfrRec<WO_WFR_STATUS$> = 'NCR must be closed'
|
|
Case NCRNo EQ ''
|
|
WOWfrRec<WO_WFR_STATUS$> = 'Wafer requires an NCR'
|
|
End Case
|
|
DispReady = ( (NCRNo NE '') and (NCRStatus EQ 'C') )
|
|
WfrsDispReady<0, WfrIndex> = DispReady
|
|
ReactRunRec<REACT_RUN_WFRS_DISP_READY$> = 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<REACT_RUN_OUT_SLOT_ID$, WfrIndex>
|
|
RunStageWfrKey = RDSNo:'*DISP*':FormattedWfrID
|
|
RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey)
|
|
StageStatus = RunStageWfrRec<RUN_STAGE_WFR_STATUS$>
|
|
If StageStatus _EQC 'INIT' then
|
|
// Mark DISP stage as complete
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @USER4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = 'COMP'
|
|
Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$)
|
|
end
|
|
Begin Case
|
|
Case (ShipID EQ '')
|
|
LocQ = GaN_Services('GetWfrQueue', WfrID)
|
|
WOWfrRec<WO_WFR_STATUS$> = '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<RUN_STAGE_WFR_STATUS$>
|
|
If StageStatus _EQC 'COMP' then
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(LocDTM, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'COMP'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = 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<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(LocDTM, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'G_PACK'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = GPackQ
|
|
WOWfrRec<WO_WFR_STATUS$> = 'Ready to close'
|
|
DispReady = True$
|
|
WfrsDispReady<0, WfrIndex> = DispReady
|
|
ReactRunRec<REACT_RUN_WFRS_DISP_READY$> = 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<RUN_STAGE_WFR_STATUS$>
|
|
LocQ = Gan_Services('GetWfrQueue', WfrID)
|
|
WOWfrRec<WO_WFR_STATUS$> = 'Run closed'
|
|
Gan_Services('RemoveWfrFromWIP', WfrID)
|
|
If StageStatus _EQC 'INIT' then
|
|
// Mark G_PACK stage as complete
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @USER4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_RETAIN_SLOT$>
|
|
RetainBox = WOWfrRec<WO_WFR_RETAIN_BOX$>
|
|
RetainSig = WOWfrRec<WO_WFR_RETAIN_SIG$>
|
|
RetainQ = GaN_Services('GetLocQueueID', 'RETAIN')
|
|
RunStageWfrKey = RDSNo:'*DISP*':FormattedWfrID
|
|
RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey)
|
|
StageStatus = RunStageWfrRec<RUN_STAGE_WFR_STATUS$>
|
|
If StageStatus _EQC 'INIT' then
|
|
// Retain flag has been set and metrology is complete, so mark DISP stage as complete.
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @User4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_STATUS$> = '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<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(LocDTM, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'COMP'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = 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<WO_WFR_STATUS$> = 'Ready to close'
|
|
DispReady = True$
|
|
end else
|
|
WOWfrRec<WO_WFR_STATUS$> = '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<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(LocDTM, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'RETAIN'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = RetainQ
|
|
WfrsDispReady<0, WfrIndex> = DispReady
|
|
ReactRunRec<REACT_RUN_WFRS_DISP_READY$> = 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<RUN_STAGE_WFR_STATUS$>
|
|
LocQ = Gan_Services('GetWfrQueue', WfrID)
|
|
WOWfrRec<WO_WFR_STATUS$> = 'Run closed'
|
|
Gan_Services('RemoveWfrFromWIP', WfrID)
|
|
If StageStatus _EQC 'INIT' then
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @User4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<REACT_RUN_WFRS_DISP_READY$> = WfrsDispReady
|
|
WOWfrRec<WO_WFR_STATUS$> = '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<RUN_STAGE_WFR_STATUS$>
|
|
LocQ = GaN_Services('GetWfrQueue', WfrID)
|
|
WOWfrRec<WO_WFR_STATUS$> = 'Run closed'
|
|
Gan_Services('RemoveWfrFromWIP', WfrID)
|
|
If StageStatus _EQC 'INIT' then
|
|
// Mark DISP stage as complete
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @User4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<REACT_RUN_WFRS_DISP_READY$> = WfrsDispReady
|
|
WOWfrRec<WO_WFR_STATUS$> = '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<RUN_STAGE_WFR_STATUS$>
|
|
WOWfrRec<WO_WFR_STATUS$> = 'Run closed'
|
|
LocQ = Gan_Services('GetWfrQueue', WfrID)
|
|
Gan_Services('RemoveWfrFromWIP', WfrID)
|
|
If StageStatus _EQC 'INIT' then
|
|
// Mark DISP stage as complete
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @User4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<REACT_RUN_WFRS_DISP_READY$> = WfrsDispReady
|
|
WOWfrRec<WO_WFR_STATUS$> = 'Ready to close'
|
|
end else
|
|
If NCRReq EQ True$ then
|
|
WOWfrRec<WO_WFR_STATUS$> = 'NCR required'
|
|
end else
|
|
WOWfrRec<WO_WFR_STATUS$> = '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<RUN_STAGE_WFR_STATUS$>
|
|
WOWfrRec<WO_WFR_STATUS$> = 'Run closed'
|
|
LocQ = GaN_Services('GetWfrQueue', WfrID)
|
|
Gan_Services('RemoveWfrFromWIP', WfrID)
|
|
If StageStatus _EQC 'INIT' then
|
|
// Mark DISP stage as complete
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @User4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_STATUS$> = '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<GAN_ETCH_REACTOR$> = Reactor
|
|
GanEtchRec<GAN_ETCH_USER$> = @USER4
|
|
GanEtchRec<GAN_ETCH_AVAILABLE$> = True$
|
|
GanEtchRec<GAN_ETCH_LOC_DTM$> = CurrDTM
|
|
GanEtchRec<GAN_ETCH_LOC_BY$> = @User4
|
|
GanEtchRec<GAN_ETCH_LOC_EVENT$> = 'CREATE'
|
|
GanEtchRec<GAN_ETCH_TOOL_ID$> = ToolID
|
|
GanEtchRec<GAN_ETCH_INV_LOC$> = LocID
|
|
Database_Services('WriteDataRow', 'GAN_ETCH', EtchID, GanEtchRec, True$, False$, True$)
|
|
LocRec = Database_Services('ReadDataRow', 'LOCATION', LocID)
|
|
LocQ = LocRec<LOCATION_ETCH_ID$>
|
|
DisplayEtchID = Xlate('GAN_ETCH', EtchID, 'DISPLAY_ETCH_ID', 'X')
|
|
Locate DisplayEtchID in LocQ using @VM setting vPos else
|
|
LocQ<1,-1> = DisplayEtchID
|
|
LocRec<LOCATION_ETCH_ID$> = 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<GAN_ETCH_REACTOR$>
|
|
ToolID = 'R':Reactor
|
|
GanEtchRec<GAN_ETCH_START_DTM$> = DateTime()
|
|
GanEtchRec<GAN_ETCH_LOC_DTM$, -1> = CurrDTM
|
|
GanEtchRec<GAN_ETCH_LOC_BY$, -1> = @User4
|
|
GanEtchRec<GAN_ETCH_LOC_EVENT$, -1> = 'START'
|
|
GanEtchRec<GAN_ETCH_TOOL_ID$, -1> = 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<LOCATION_WFR_ID$>
|
|
Locate DisplayEtchID in LocQ using @VM setting vPos then
|
|
LocQ = Delete(LocQ, 1, vPos, 0)
|
|
LocRec<LOCATION_ETCH_ID$> = LocQ
|
|
Database_Services('WriteDataRow', 'LOCATION', LocID, LocRec, True$, False$, True$)
|
|
end
|
|
// Add to Tool queue
|
|
ToolEtchRec = Database_Services('ReadDataRow', 'TOOL_ETCH', ToolID)
|
|
ToolQ = ToolEtchRec<TOOL_ETCH.ETCH_ID$>
|
|
Locate DisplayEtchID in ToolQ using @VM setting vPos else
|
|
ToolQ<1,-1> = DisplayEtchID
|
|
ToolEtchRec<TOOL_ETCH.ETCH_ID$> = 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<GAN_ETCH_END_DTM$> = DateTime()
|
|
GanEtchRec<GAN_ETCH_AVAILABLE$> = True$
|
|
GanEtchRec<GAN_ETCH_LOC_DTM$, -1> = CurrDTM
|
|
GanEtchRec<GAN_ETCH_LOC_BY$, -1> = @User4
|
|
GanEtchRec<GAN_ETCH_LOC_EVENT$, -1> = 'STOP'
|
|
GanEtchRec<GAN_ETCH_TOOL_ID$, -1> = 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<LOCATION_CASS_ID$>
|
|
Locate WOMatKey in CassQ using @VM setting vPos else
|
|
CassQ<1, -1> = WOMatKey
|
|
QRec<LOCATION_CASS_ID$> = 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<LOCATION_CASS_ID$>
|
|
Locate WOMatKey in CassQ using @VM setting vPos then
|
|
CassQ = Delete(CassQ, 1, vPos, 0)
|
|
QRec<LOCATION_CASS_ID$> = 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<LOCATION_CASS_ID$>
|
|
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<LOCATION_CASS_ID$>
|
|
Locate WOMatKey in LocQ using @VM setting vPos then
|
|
LocQ = Delete(LocQ, 0, vPos, 0)
|
|
QRec<LOCATION_CASS_ID$> = 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<TOOL_WFR_WFR_ID$>
|
|
Locate WOWfrID in ToolQ using @VM setting vPos else
|
|
ToolQ<1, -1> = WOWfrID
|
|
QRec<TOOL_WFR_WFR_ID$> = 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<TOOL_WFR_WFR_ID$> = 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<LOCATION_WFR_ID$>
|
|
Locate WOWfrID in LocQ using @VM setting vPos else
|
|
LocQ<0, -1> = WOWfrID
|
|
QRec<LOCATION_WFR_ID$> = 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<TOOL_WFR_WFR_ID$>
|
|
Locate WfrID in ToolQ using @VM setting vPos then
|
|
ToolQ = Delete(ToolQ, 0, vPos, 0)
|
|
QRec<TOOL_WFR_WFR_ID$> = 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<LOCATION_WFR_ID$>
|
|
Locate WfrID in LocQ using @VM setting vPos then
|
|
LocQ = Delete(LocQ, 0, vPos, 0)
|
|
QRec<LOCATION_WFR_ID$> = 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<TOOL_ETCH.ETCH_ID$>
|
|
Locate DisplayEtchID in ToolQ using @VM setting vPos then
|
|
ToolQ = Delete(ToolQ, 0, vPos, 0)
|
|
QRec<TOOL_ETCH.ETCH_ID$> = 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<LOCATION_ETCH_ID$>
|
|
Locate DisplayEtchID in LocQ using @VM setting vPos then
|
|
LocQ = Delete(LocQ, 0, vPos, 0)
|
|
QRec<LOCATION_ETCH_ID$> = 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<WO_MAT_HOLD$> EQ '') or (WOMatRec<WO_MAT_HOLD$> EQ False$) then
|
|
WOWfrIDs = Xlate('WO_MAT_WFR', WONo:'*':CassNo, WO_MAT_WFR_IN_WFR_ID$, 'X')
|
|
SlotCnt = WOMatRec<WO_MAT_WAFER_QTY$>
|
|
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<REACT_RUN_GAN_RUN_ID$>
|
|
WONo = ReactRunRec<REACT_RUN_WO_NO$>
|
|
WOStep = ReactRunRec<REACT_RUN_WO_STEP$>
|
|
Reactor = ReactRunRec<REACT_RUN_REACTOR$>
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
CarrSlotNos = ReactRunRec<REACT_RUN_CARR_SLOT_NO$>
|
|
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<REACT_RUN_CARR_SLOT_ID$> = CarrSlotIDs
|
|
ReactRunRec<REACT_RUN_CARR_WFR_ID$> = 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<WO_WFR_LOC_DTM$>
|
|
NextIndex = DCount(LocDTMs, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextIndex> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextIndex> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextIndex> = 'UNLOAD'
|
|
WOWfrRec<WO_WFR_CARR_SLOT$, NextIndex> = CarrSlotIDs<0, vPos>
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextIndex> = 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<RUN_STAGE_SPEC_INV_ACTION$>
|
|
IACompBy = RunStageRec<RUN_STAGE_SIA_COMP_BY$>
|
|
IACompDTM = RunStageRec<RUN_STAGE_SIA_COMP_DTM$>
|
|
Locate 'UNLOAD' in InvActions using @VM setting vPos then
|
|
IACompBy<0, vPos> = @User4
|
|
IACompDTM<0, vPos> = CurrDTM
|
|
end
|
|
RunStageRec<RUN_STAGE_SIA_COMP_BY$> = IACompBy
|
|
RunStageRec<RUN_STAGE_SIA_COMP_DTM$> = IACompDTM
|
|
RunStageRec<RUN_STAGE_COMP_BY$> = @User4
|
|
RunStageRec<RUN_STAGE_COMP_DTM$> = CurrDTM
|
|
RunStageRec<RUN_STAGE_STATUS$> = '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<REACT_RUN_GAN_STAGE_ROUTE$>
|
|
RxStages = ReactRunRec<REACT_RUN_WFR_STAGE$>
|
|
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<WO_WFR_INV_LOC$>
|
|
ToolLocs = WOWfrRec<WO_WFR_TOOL_ID$>
|
|
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<GAN_ETCH_AVAILABLE$> = 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<REACT_RUN_WFR_STAGE$>
|
|
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<REACT_RUN_UNPRESCRIBED_WFR_STAGE$>
|
|
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<REACT_RUN_WFR_STAGE$> = WfrStages
|
|
ReactRunRec<REACT_RUN_UNPRESCRIBED_WFR_STAGE$> = UnRxStages
|
|
Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
|
|
end
|
|
end
|
|
RunStageWfrRec = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_SPEC_TOOL_CLASS$> = GaN_Services('GetToolClassIDs', Stage)
|
|
RunStageWfrRec<RUN_STAGE_WFR_SPEC_INV_ACTION$> = GaN_Services('GetInvActions', Stage)
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<RUN_STAGE_WFR_COMP_BY$> = User
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_LOC_DTM$>
|
|
NextStep = DCount(LocDTMs, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextStep> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextStep> = User
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextStep> = 'DISPOSITION'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextStep> = ''
|
|
CurrStage = Gan_Services('GetCurrStage', WfrID)
|
|
CurrLocQ = Gan_Services('GetLocQueueID', CurrStage)
|
|
NextStage = Gan_Services('GetNextStage', WfrID)
|
|
NextLocQ = Gan_Services('GetLocQueueID', NextStage)
|
|
WoWfrRec<WO_WFR_INV_LOC$, NextStep> = 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<REACT_RUN_IN_WFR_ID$>
|
|
For each WOWfrID in InWfrIDs using @VM setting vPos
|
|
ThisWOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WOWfrID)
|
|
LocDTMs = ThisWOWfrRec<WO_WFR_LOC_DTM$>
|
|
LocQs = ThisWOWfrRec<WO_WFR_INV_LOC$>
|
|
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<WO_WFR_LOC_DTM$> = 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<RUN_STAGE_WFR_STATUS$> = 'START'
|
|
// Sign InvAction, Fill InvAction DTM
|
|
SpecInvActions = RunStageWfrRec<RUN_STAGE_WFR_SPEC_INV_ACTION$>
|
|
SIACompBy = RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_BY$>
|
|
SIACompDTM = RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_DTM$>
|
|
Locate InvAction in SpecInvActions using @VM setting vPos then
|
|
SIACompBy<0, vPos> = @USER4
|
|
SIACompDTM<0, vPos> = CurrDTM
|
|
end
|
|
RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_BY$> = SIACompBy
|
|
RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_DTM$> = SIACompDTM
|
|
Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$)
|
|
WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID)
|
|
LocDTM = WOWfrRec<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(LocDTM, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'START'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ToolID
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = ''
|
|
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<RUN_STAGE_WFR_STATUS$>
|
|
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<RUN_STAGE_WFR_SPEC_INV_ACTION$>
|
|
SIACompBy = RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_BY$>
|
|
SIACompDTM = RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_DTM$>
|
|
CompBy = RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$>
|
|
CompDTM = RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$>
|
|
Locate InvAction in SpecInvActions using @VM setting vPos then
|
|
SIACompBy<0, vPos> = @USER4
|
|
SIACompDTM<0, vPos> = CurrDTM
|
|
end
|
|
RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_BY$> = SIACompBy
|
|
RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_DTM$> = SIACompDTM
|
|
// Set the status
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = 'STOP'
|
|
Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$)
|
|
|
|
WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID)
|
|
LocDTM = WOWfrRec<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(LocDTM, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @User4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'STOP'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = 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<RUN_STAGE_WFR_STATUS$>
|
|
If StageStatus _EQC 'STOP' then
|
|
// Mark RUN_STAGE_WFR as complete
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @User4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<RUN_STAGE_WFR_COMP_BY$> = @USER4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
// Set the wafer status
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(LocDTM, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'SKIP'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = 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<RUN_STAGE_WFR_COMP_BY$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = ''
|
|
// Set the wafer status
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(LocDTM, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'UNSKIP'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = 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<RUN_STAGE_WFR_TOOL_ID$> = 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<REACT_RUN_WFR_STAGE$>
|
|
UnRxStages = ReactRunRec<REACT_RUN_UNPRESCRIBED_WFR_STAGE$>
|
|
// 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<REACT_RUN_WFR_STAGE$> = WfrStages
|
|
ReactRunRec<REACT_RUN_UNPRESCRIBED_WFR_STAGE$> = 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<WO_WFR_LOC_DTM$>
|
|
NextStep = DCount(LocDTMs, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextStep> = Datetime()
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextStep> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextStep> = 'REMOVE'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextStep> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextStep> = 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<RUN_STAGE_WFR_COMP_BY$> = User
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_LOC_DTM$>
|
|
NextStep = DCount(LocDTMs, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextStep> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextStep> = User
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextStep> = 'CHARACTERIZE'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextStep> = ''
|
|
CurrStage = Gan_Services('GetCurrStage', WfrID)
|
|
CurrLocQ = Gan_Services('GetLocQueueID', CurrStage)
|
|
NextStage = Gan_Services('GetNextStage', WfrID)
|
|
NextLocQ = Gan_Services('GetLocQueueID', NextStage)
|
|
WoWfrRec<WO_WFR_INV_LOC$, NextStep> = 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<REACT_RUN_IN_WFR_ID$>
|
|
Locate WfrID in InWfrIDs using @VM setting wPos then
|
|
ReactRunRec<REACT_RUN_CHAR_WFR_FLAG$, wPos> = True$
|
|
// Add this wafer ID to the list of characterized wafers so that we can
|
|
// determine primary and secondary characterization.
|
|
Locate WfrID in ReactRunRec<REACT_RUN_CHAR_LIST$> using @VM setting dummy else
|
|
ReactRunRec<REACT_RUN_CHAR_LIST$, -1> = 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<REACT_RUN_CASS_NO$>
|
|
LastCharRun = WOLogRec<WO_LOG_LAST_CHAR_RUN$>[-1, 'B':@VM]
|
|
If ( (LastCharRun EQ '') or (RunNo GT LastCharRun) ) then
|
|
LastCharRun = RunNo
|
|
WOLogRec<WO_LOG_LAST_CHAR_RUN$, -1> = 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<WO_LOG_LAST_CHAR_RUN$>
|
|
NumCharRuns = DCount(PrevCharRuns, @VM)
|
|
LastCharRun = PrevCharRuns<0, NumCharRuns>
|
|
If LastCharRun EQ '' then LastCharRun = 0
|
|
ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
LastWafer = DCount(InWfrIDs, @VM)
|
|
RunNo = ReactRunRec<REACT_RUN_CASS_NO$>
|
|
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<RUN_STAGE_WFR_COMP_BY$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_WFR_LOC_DTM$>
|
|
NextStep = DCount(LocDTMs, @VM) + 1
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextStep> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextStep> = @User4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextStep> = 'WITHDRAW'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextStep> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextStep> = NextLocQ
|
|
Database_Services('WriteDataRow', 'WO_WFR', WOWfrKey, WOWfrRec, True$, False$, True$)
|
|
// Mark the wafer as NOT characterized in the REACT_RUN record.
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
CharList = ReactRunRec<REACT_RUN_CHAR_LIST$>
|
|
Locate WfrID in InWfrIDs using @VM setting wPos then
|
|
ReactRunRec<REACT_RUN_CHAR_WFR_FLAG$, wPos> = False$
|
|
end
|
|
Locate WfrID in CharList using @VM setting cPos then
|
|
CharList = Delete(CharList, 0, cPos, 0)
|
|
ReactRunRec<REACT_RUN_CHAR_LIST$> = 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<REACT_RUN_CASS_NO$>
|
|
PrevCharRuns = WOLogRec<WO_LOG_LAST_CHAR_RUN$>
|
|
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<REACT_RUN_CHAR_WFR_FLAG$>
|
|
TotalCharWfrs = Sum(WfrCharFlags)
|
|
If TotalCharWfrs EQ 0 then
|
|
PrevCharRuns = Delete(PrevCharRuns, 0, vPos, 0)
|
|
WOLogRec<WO_LOG_LAST_CHAR_RUN$> = 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<LOCATION_WFR_ID$>
|
|
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<TOOL_WFR_WFR_ID$>
|
|
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<LOCATION_ETCH_ID$>
|
|
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<TOOL_ETCH.ETCH_ID$>
|
|
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<REACT_RUN_RUN_STAGE_KEY$>
|
|
WfrStages = ReactRunRec<REACT_RUN_WFR_STAGE$>
|
|
UnRxStages = ReactRunRec<REACT_RUN_UNPRESCRIBED_WFR_STAGE$>
|
|
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<REACT_RUN_WFR_STAGE$>
|
|
UnRxStages = ReactRunRec<REACT_RUN_UNPRESCRIBED_WFR_STAGE$>
|
|
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<REACT_RUN_WFR_STAGE$>
|
|
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<REACT_RUN_IN_WFR_ID$>
|
|
ExternalFlags = ReactRunRec<REACT_RUN_EXTERNAL$>
|
|
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<REACT_RUN_WFR_STAGE$>
|
|
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<REACT_RUN_IN_WFR_ID$>
|
|
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<WO_MAT_WFR_OUT_WFR_ID$>
|
|
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<REACT_RUN_IN_WFR_ID$> USING @VM SETTING Pos THEN
|
|
ReactRunRec<REACT_RUN_OUT_SLOT_ID$, Pos> = 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<REACT_RUN_CARR_WFR_ID$> USING @VM SETTING cPos THEN
|
|
CurrCarrSlot = ReactRunRec<REACT_RUN_CARR_SLOT_ID$, cPos>
|
|
END ELSE
|
|
CurrCarrSlot = ''
|
|
END
|
|
EventDTMs = WOWfrRec<WO_WFR_LOC_DTM$>
|
|
CurrEventIndex = DCount(EventDTMs, @VM)
|
|
WOWfrRec<WO_WFR_SLOT_ID$, CurrEventIndex> = OutWfrID
|
|
WOWfrRec<WO_WFR_SLOT_ID$, CurrEventIndex> = CurrCarrSlot
|
|
Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$)
|
|
NEXT I
|
|
|
|
// Update WO_MAT_WFR table (outbound cassette table)
|
|
WOMatWfrRec<WO_MAT_WFR_OUT_WFR_ID$> = 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<WO_LOG_NEXT_OUT_SLOT$>
|
|
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<WO_LOG_WO_MAT_KEY$, -1> = 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<WO_MAT_WFR_OUT_PREV_WFR_ID$, -1> = 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<WO_LOG_NEXT_OUT_SLOT$> = 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<WO_MAT_WFR_OUT_WFR_ID$>
|
|
OutWfrIDs<0, OutSlotNo> = WfrID
|
|
WOMatWfrRec<WO_MAT_WFR_OUT_WFR_ID$> = 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<REACT_RUN_IN_WFR_ID$> USING @VM SETTING Pos THEN
|
|
ReactRunRec<REACT_RUN_OUT_SLOT_ID$, Pos> = 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<REACT_RUN_CARR_WFR_ID$> USING @VM SETTING cPos THEN
|
|
CurrCarrSlot = ReactRunRec<REACT_RUN_CARR_SLOT_ID$, cPos>
|
|
END ELSE
|
|
CurrCarrSlot = ''
|
|
END
|
|
EventDTMs = WOWfrRec<WO_WFR_LOC_DTM$>
|
|
NextPos = DCount(EventDTMs, @VM) + 1
|
|
WOWfrRec<WO_WFR_SLOT_ID$, NextPos> = OutSlotKey
|
|
WOWfrRec<WO_WFR_CARR_SLOT$, NextPos> = CurrCarrSlot
|
|
WOWfrRec<WO_WFR_LOC_DTM$, NextPos> = CurrDTM
|
|
WOWfrRec<WO_WFR_LOC_BY$, NextPos> = @USER4
|
|
WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'COMP'
|
|
WOWfrRec<WO_WFR_TOOL_ID$, NextPos> = ''
|
|
WOWfrRec<WO_WFR_INV_LOC$, NextPos> = 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<RUN_STAGE_WFR_STATUS$>
|
|
If StageStatus _EQC 'INIT' then
|
|
// Mark DISP stage as complete
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @USER4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<WO_MAT_WFR_OUT_WFR_ID$>
|
|
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<REACT_RUN_IN_WFR_ID$> USING @VM SETTING Pos THEN
|
|
ReactRunRec<REACT_RUN_OUT_SLOT_ID$, Pos> = ''
|
|
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<WO_WFR_LOC_DTM$>
|
|
CurrEventIndex = DCount(EventDTMs, @VM)
|
|
WOWfrRec<WO_WFR_SLOT_ID$, CurrEventIndex> = ''
|
|
WOWfrRec<WO_WFR_SLOT_ID$, CurrEventIndex> = ''
|
|
Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$)
|
|
NEXT I
|
|
|
|
// Update WO_MAT_WFR table (outbound cassette table)
|
|
WOMatWfrRec<WO_MAT_WFR_OUT_WFR_ID$> = 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<REACT_RUN_WFR_STAGE$>
|
|
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<PROD_SPEC_ANKO$>
|
|
ANKOMetPockets = PSRec<PROD_SPEC_ANKO_MET_POCKETS$>
|
|
ANKOCharSel = PSRec<PROD_SPEC_ANKO_WAFER_SELECTION$>
|
|
TechType = PSRec<PROD_SPEC_TECH_TYPE$>
|
|
ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
OutOfSpecArray = ReactRunRec<REACT_RUN_DATA_OUT_OF_SPEC$>
|
|
CassNo = ReactRunRec<REACT_RUN_CASS_NO$>
|
|
WONo = ReactRunRec<REACT_RUN_WO_NO$>
|
|
GaNCritStages = ReactRunRec<REACT_RUN_GAN_CRITICAL_STAGES$>
|
|
GaNParamKeys = ReactRunRec<REACT_RUN_GAN_PARAM_KEYS$>
|
|
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<PROD_SPEC_ANKO_FULL_CHAR_POCKET$>
|
|
// 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<REACT_RUN_IN_WFR_ID$>
|
|
CharFlags = ReactRunRec<REACT_RUN_CHAR_WFR_FLAG$>
|
|
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<REACT_RUN_WFRS_DISP_READY$>
|
|
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<REACT_RUN_WFRS_DISP_READY$>
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
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<REACT_RUN_CARR_WFR_SHIP$>
|
|
OutSlotIDs = ReactRunRec<REACT_RUN_OUT_SLOT_ID$>
|
|
WONo = ReactRunRec<REACT_RUN_WO_NO$>
|
|
WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
|
|
WOMatKeys = WOLogRec<WO_LOG_WO_MAT_KEY$>
|
|
CustNo = WOLogRec<WO_LOG_CUST_NO$>
|
|
EpiPartNo = WOLogRec<WO_LOG_EPI_PART_NO$>
|
|
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<RUN_STAGE_WFR_STATUS$>
|
|
If StageStatus _EQC 'INIT' then
|
|
// Mark DISP stage as complete
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = @USER4
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = CurrDTM
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<REACT_RUN_DISP_COMPLETE$> = 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<REACT_RUN_IN_WFR_ID$>
|
|
CharFlags = ReactRunRec<REACT_RUN_CHAR_WFR_FLAG$>
|
|
CritStages = ReactRunRec<REACT_RUN_GAN_CRITICAL_STAGES$>
|
|
RunScrapped = False$
|
|
// Clear failure flags
|
|
ReactRunRec<REACT_RUN_RUN_SCRAPPED$> = RunScrapped
|
|
ReactRunRec<REACT_RUN_DATA_MISSING$> = ''
|
|
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<REACT_RUN_MISSING_DATA_VALID_DISABLED$, WaferPos>
|
|
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<RUN_STAGE_WFR_PARAM_OUT_OF_SPEC$>
|
|
|
|
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<REACT_RUN_RUN_SCRAPPED$> = RunScrapped
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Next Stage
|
|
|
|
ReactRunRec<REACT_RUN_DATA_MISSING$, WaferPos> = DataIsMissing
|
|
ReactRunRec<REACT_RUN_DATA_OUT_OF_SPEC$, WaferPos> = DataOutOfSpec
|
|
If ( (DataIsMissing EQ False$) and (DataOutOfSpec EQ False$) ) then
|
|
ReactRunRec<REACT_RUN_MISSING_DATA_VALID_DISABLED$, WaferPos> = False$
|
|
ReactRunRec<REACT_RUN_MISSING_DATA_VALID_USER$, WaferPos> = ''
|
|
ReactRunRec<REACT_RUN_MISSING_DATA_VALID_DTM$, WaferPos> = ''
|
|
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<REACT_RUN_NCR_REQ$>
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
DispFlags = ReactRunRec<REACT_RUN_WFRS_DISP_READY$>
|
|
Grades = ReactRunRec<REACT_RUN_CARR_WFR_GRADE$>
|
|
Reasons = ReactRunRec<REACT_RUN_DISP_REASON$>
|
|
CharFlags = ReactRunRec<REACT_RUN_CHAR_WFR_FLAG$>
|
|
RunScrapped = ReactRunRec<REACT_RUN_RUN_SCRAPPED$>
|
|
|
|
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<GAN_PARAMS.MET_NAME$>
|
|
LSLs = GaNParamRec<GAN_PARAMS.LSL$>
|
|
USLs = GaNParamRec<GAN_PARAMS.USL$>
|
|
Targets = GaNParamRec<GAN_PARAMS.TARGET$>
|
|
RunNo = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
|
|
Reactor = 'R':ReactRunRec<REACT_RUN_REACTOR$>
|
|
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<fPos, 3>
|
|
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<fPos, 2>
|
|
ParamValues<WaferPos, mPos, DataPointIndex> = Response<fPos, 4>
|
|
end
|
|
end
|
|
Next Row
|
|
|
|
If Stage EQ 'JV_XRD' then
|
|
TheseParamValues = ParamValues<WaferPos, mPos>
|
|
NumPoints = DCount(TheseParamValues, @SVM)
|
|
If NumPoints GT 0 then ParamValues<WaferPos, mPos> = 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<WaferPos>
|
|
Delimiter = @VM
|
|
Convert '*' to '.' in WfrID
|
|
RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID
|
|
RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey)
|
|
RunStageWfrRec<RUN_STAGE_WFR_MET_PARAM_VALUES$> = 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<RUN_STAGE_WFR_MET_PARAM_VALUES$> = ValueSet
|
|
RunStageWfrRec<RUN_STAGE_WFR_PARAM_OUT_OF_SPEC$> = 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<RUN_STAGE_WFR_MET_PARAM_VALUES$> = 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<RUN_STAGE_WFR_MET_PARAM_VALUES$> = ParamValues
|
|
RunStageWfrRec<RUN_STAGE_WFR_PARAM_OUT_OF_SPEC$> = 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<REACT_RUN_NCR_REQ$>
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
NumWfrs = DCount(InWfrIDs, @VM)
|
|
DispFlags = ReactRunRec<REACT_RUN_WFRS_DISP_READY$>
|
|
Grades = ReactRunRec<REACT_RUN_CARR_WFR_GRADE$>
|
|
Reasons = ReactRunRec<REACT_RUN_DISP_REASON$>
|
|
CharFlags = ReactRunRec<REACT_RUN_CHAR_WFR_FLAG$>
|
|
RunScrapped = ReactRunRec<REACT_RUN_RUN_SCRAPPED$>
|
|
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<GAN_PARAMS.MET_NAME$>
|
|
LSLs = GaNParamRec<GAN_PARAMS.LSL$>
|
|
USLs = GaNParamRec<GAN_PARAMS.USL$>
|
|
Targets = GaNParamRec<GAN_PARAMS.TARGET$>
|
|
RunNo = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
|
|
Reactor = 'R':ReactRunRec<REACT_RUN_REACTOR$>
|
|
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] != '<blank>' "
|
|
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<RowIndex>
|
|
Pocket = Row<0, 1>
|
|
EpiThickMean = Row<0, 2>
|
|
EpiStdDev = Row<0, 3>
|
|
ParamValues<Pocket, 1> = EpiThickMean
|
|
ParamValues<Pocket, 2> = EpiStdDev
|
|
Next RowIndex
|
|
|
|
ChangeDetected = False$
|
|
|
|
For each WfrID in InWfrIDs using @VM setting WaferPos
|
|
ValueSet = ParamValues<WaferPos>
|
|
Delimiter = @VM
|
|
Convert '*' to '.' in WfrID
|
|
RunStageWfrKey = RDSNo:'*PLATO*':WfrID
|
|
RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey)
|
|
RunStageWfrRec<RUN_STAGE_WFR_MET_PARAM_VALUES$> = 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<RUN_STAGE_WFR_MET_PARAM_VALUES$> = ValueSet
|
|
RunStageWfrRec<RUN_STAGE_WFR_PARAM_OUT_OF_SPEC$> = 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<REACT_RUN_IN_WFR_ID$>
|
|
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<REACT_RUN_GAN_RUN_ID$>
|
|
Reactor = 'R':ReactRunRec<REACT_RUN_REACTOR$>
|
|
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<fPos, 3>
|
|
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<fPos, 2>
|
|
ParamValues<WaferPos, mPos, DataPointIndex> = Response<fPos, 4>
|
|
end
|
|
end
|
|
|
|
Next Row
|
|
Next MetName
|
|
|
|
For each WfrID in InWfrIDs using @VM setting WaferPos
|
|
|
|
WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID)
|
|
OrigCritFailure = WOWfrRec<WO_WFR_CRITICAL_FAILURE$>
|
|
CriticalFailure = False$
|
|
ValueSet = ParamValues<WaferPos>
|
|
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<WO_MAT_WFR_IN_WFR_ID$>
|
|
ConsumedQ = WOMatWfrRec<WO_MAT_WFR_IN_PREV_WFR_ID$>
|
|
ReadyQ<0, SlotNo> = ''
|
|
ConsumedQ<0, SlotNo> = InWfrID
|
|
WOMatWfrRec<WO_MAT_WFR_IN_WFR_ID$> = ReadyQ
|
|
WOMatWfrRec<WO_MAT_WFR_IN_PREV_WFR_ID$> = 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<PROD_SPEC_PRS_STAGE_KEY$>
|
|
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<PRS_STAGE_PROC_TOOL_CLASS$> = ToolClasses
|
|
If InvActions NE '' then
|
|
PRSStageRec<PRS_STAGE_PROC_INV_ACTION$> = InvActions
|
|
If SigReq EQ True$ then
|
|
For each InvAction in InvActions using @VM setting vPos
|
|
PRSStageRec<PRS_STAGE_PROC_INV_SIG_REQ$, vPos> = 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<RowIndex, 1> = WfrRec<RETAINED_WAFERS.RUN_ID$>
|
|
WaferList<RowIndex, 2> = WfrRec<RETAINED_WAFERS.RECIPE$>
|
|
WaferList<RowIndex, 3> = WfrRec<RETAINED_WAFERS.POCKET$>
|
|
WaferList<RowIndex, 4> = WfrRec<RETAINED_WAFERS.SCRIBE$>
|
|
WaferList<RowIndex, 5> = WfrRec<RETAINED_WAFERS.GRADE$>
|
|
WaferList<RowIndex, 6> = WfrRec<RETAINED_WAFERS.PART_NO$>
|
|
WaferList<RowIndex, 7> = Field(WfrID, '*', 1)
|
|
WaferList<RowIndex, 8> = WfrRec<RETAINED_WAFERS.RETAIN_BOX$>
|
|
WaferList<RowIndex, 9> = WfrRec<RETAINED_WAFERS.RETAIN_SLOT$>
|
|
WaferList<RowIndex, 10> = WfrRec<RETAINED_WAFERS.LOCATION$>
|
|
WaferList<RowIndex, 11> = WfrRec<RETAINED_WAFERS.STATUS$>
|
|
WaferList<RowIndex, 12> = WfrRec<RETAINED_WAFERS.COMMENT$>
|
|
WaferList<RowIndex, 13> = WfrRec<RETAINED_WAFERS.RETAIN_SIG$>
|
|
WaferList<RowIndex, 14> = WfrRec<RETAINED_WAFERS.RETAIN_DT$>
|
|
WaferList<RowIndex, 15> = WfrRec<RETAINED_WAFERS.DESTROY_SIG$>
|
|
WaferList<RowIndex, 16> = WfrRec<RETAINED_WAFERS.DESTROY_DT$>
|
|
WaferList<RowIndex, 17> = 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<WO_WFR_RETAIN_LOC$> = Location
|
|
WOWfrRec<WO_WFR_RETAIN_STATUS$> = Status
|
|
WOWfrRec<WO_WFR_RETAIN_COMMENT$> = 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<fPos>, @VM)
|
|
vPos += 1
|
|
ParameterList<fPos, vPos, 1> = MetName
|
|
ParameterList<fPos, vPos, 2> = 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<fPos>, @VM)
|
|
* vPos += 1
|
|
* ParameterList<fPos, vPos, 1> = MetName
|
|
* ParameterList<fPos, vPos, 2> = 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<fPos>, @VM)
|
|
* vPos += 1
|
|
* ParameterList<fPos, vPos, 1> = MetName
|
|
* ParameterList<fPos, vPos, 2> = 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<WO_WFR_RETAIN_BOX$>
|
|
RetainSlot = WOWfrRec<WO_WFR_RETAIN_SLOT$>
|
|
If ( (RetainBox NE '') and (RetainSlot NE '') ) then
|
|
WOWfrRec<WO_WFR_RETAIN_BOX$> = ''
|
|
WOWfrRec<WO_WFR_RETAIN_SLOT$> = ''
|
|
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<RUN_STAGE_WFR_COMP_BY$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<REACT_RUN_IN_WFR_ID$>
|
|
Locate InboundWfrID in InWfrIDs using @VM setting vPos then
|
|
ReactRunRec<REACT_RUN_GAN_RET_WFR$, vPos> = 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<WO_WFR_RETAIN_BOX$>
|
|
OrigRetainSlot = WOWfrRec<WO_WFR_RETAIN_SLOT$>
|
|
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<WO_WFR_RETAIN_BOX$> = RetainBox
|
|
WOWfrRec<WO_WFR_RETAIN_SLOT$> = 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<RUN_STAGE_WFR_STATUS$>
|
|
If StageStatus NE 'COMP' then
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = 'AUTO'
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = Datetime()
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = '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<RUN_STAGE_WFR_STATUS$>
|
|
If StageStatus NE 'COMP' then
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$> = 'AUTO'
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = Datetime()
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = 'COMP'
|
|
Database_Services('WriteDataRow', 'RUN_STAGE_WFR', DispStageKey, RunStageWfrRec, True$, False$, True$)
|
|
end
|
|
ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
|
|
InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
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<REACT_RUN_GAN_RET_WFR$, vPos> = 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<WO_WFR_RETAIN_SIG$> = @User4
|
|
WOWfrRec<WO_WFR_RETAIN_DT$> = Date()
|
|
WOWfrRec<WO_WFR_RETAIN_LOC$> = '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<WO_WFR_DESTROY_SIG$> = @User4
|
|
WOWfrRec<WO_WFR_DESTROY_DT$> = Date()
|
|
WOWfrRec<WO_WFR_RETAIN_LOC$> = '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<WfrIndex, 1> = WfrID
|
|
WfrStatusList<WfrIndex, 2> = 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<WO_WFR_CRITICAL_FAILURE$> = 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<WO_WFR_CRITICAL_FAILURE$> = 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<DISPOSITION_REQUESTS.RESPONSE_DATE$>
|
|
Response = RequestRow<DISPOSITION_REQUESTS.RESPONSE$>
|
|
// 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<DISPOSITION_REQUESTS.RESPONSE$> = True$
|
|
end else
|
|
RequestRow<DISPOSITION_REQUESTS.RESPONSE$> = Error_Services('GetMessage')
|
|
end
|
|
RequestRow<DISPOSITION_REQUESTS.RESPONSE_DATE$> = Date()
|
|
RequestRow<DISPOSITION_REQUESTS.RESPONSE_TIME$> = 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<REACT_RUN_REACTOR$>
|
|
Scribes = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X')
|
|
NumWfrs = DCount(Scribes, @VM)
|
|
GanRunID = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
|
|
GaNRecipe = ReactRunRec<REACT_RUN_GAN_RECIPE$>
|
|
WfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
|
|
CassDispComp = ReactRunRec<REACT_RUN_DISP_COMPLETE$>
|
|
WONo = ReactRunRec<REACT_RUN_WO_NO$>
|
|
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<WO_WFR_RETAIN_SLOT$>
|
|
RetainBox = WOWfrRec<WO_WFR_RETAIN_BOX$>
|
|
WOWfrGrade = WOWfrRec<WO_WFR_GRADE$>
|
|
If WOWfrGrade NE ExcelGrade then
|
|
WOWfrRec<WO_WFR_GRADE$> = ExcelGrade
|
|
Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$)
|
|
end
|
|
OrigShipFlag = ReactRunRec<REACT_RUN_CARR_WFR_SHIP$, WfrIndex>
|
|
|
|
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<RUN_STAGE_WFR_COMP_BY$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = ''
|
|
RunStageWfrRec<RUN_STAGE_WFR_STATUS$> = 'INIT'
|
|
Database_Services('WriteDataRow', 'RUN_STAGE_WFR', GPackKey, RunStageWfrRec, True$, False$, True$)
|
|
end
|
|
end
|
|
|
|
Next WfrIndex
|
|
ReactRunRec<REACT_RUN_CARR_WFR_SHIP$> = ExcelShipFlags
|
|
ReactRunRec<REACT_RUN_GAN_RET_WFR$> = ExcelRetainFlags
|
|
ReactRunRec<REACT_RUN_NCR_REQ$> = ExcelNCRReqFlags
|
|
ReactRunRec<REACT_RUN_DISP_REASON$> = ExcelReasons
|
|
InternalFlags = ReactRunRec<REACT_RUN_INTERNAL$>
|
|
ExternalFlags = ReactRunRec<REACT_RUN_EXTERNAL$>
|
|
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<REACT_RUN_INTERNAL$> = InternalFlags
|
|
ReactRunRec<REACT_RUN_EXTERNAL$> = 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] != '<blank>' 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<REACT_RUN_SCRIBE_ERROR$> = 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<REACT_RUN_IN_WFR_ID$>, @VM)
|
|
For WfrIndex = 1 to NumWfrs
|
|
ReactRunRec<REACT_RUN_SCRIBE_ERROR$, WfrIndex> = 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
|