Fixed min row limit issue causing cells to be colored incorrectly. Fixed sheet rho units in rds test modules
3895 lines
186 KiB
Plaintext
3895 lines
186 KiB
Plaintext
Function QA_Services(@Service, @Params)
|
|
/***********************************************************************************************************************
|
|
|
|
Name : QA_Services
|
|
|
|
Description : Handler program for all WO_MAT_QA 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)
|
|
07/24/18 djs Original programmer. Created the CalculateHgCVData and CalculateThickData services.
|
|
07/30/18 djs Created the GetLotSODAvg service, which returns the sum of defects average
|
|
for the entire lot for which a given RDS belongs to.
|
|
08/06/18 djs Created the GetROTRStatus service, which returns the status of a given WO_STEP record.
|
|
08/08/18 djs Created the GetMaintenanceScanStatus service, which returns whether or not a 100% (failure)
|
|
scan is reguired for a given RDS. This is necessary for the first two RDS runs following
|
|
ROTR related maintenance upon a given reactor.
|
|
08/14/18 djs Created the GetUCL service, which returns the Upper Control Limit (SoD Avg) for a given RDS.
|
|
This specification is managed by and pulled from the SPC Microsoft SQL Database.
|
|
08/30/18 djs Added the PostWaferImageRequest and ProcessWaferImageRequests services. These services allow
|
|
wafer map images to be pulled from the Sharepoint database on the EC domain and sent to
|
|
users on the application server.
|
|
09/11/18 djs Added the GetSoDPerWafer service. This service queries the Sharepoint database for sum of
|
|
defect values for each wafer in a given RDS. This is primarily used when any single wafer's
|
|
SOD value exceeds the USL threshold. When this occurs a 100% failure scan is required where
|
|
the individual wafer SOD values are required for verfication.
|
|
09/13/18 djs Added the PostUCLRequest and ProcessUCLRequests services. These services allow UCL values to
|
|
be retrieved from the SPC database on the Infineon domain and sent to users/services on the
|
|
EC server.
|
|
09/14/18 djs Added the GetNumWafersExceedingUSL service, which returns the number of wafers in an RDS
|
|
that have a Sum of Defect value that exceeds the USL threshold.
|
|
09/18/18 djs Added the PostToWaferImageQueue and ProcessWaferImageQueue services. These allow wafer pdfs
|
|
to be retrieved when metrology run data is imported and also allows the pdfs to be copied to
|
|
the App server via OpenInsight.
|
|
10/08/18 djs Added the SignPreEpiStage service. This service encapsulates the business logic within the
|
|
click script event of the sign button on the RDS_PRE_EPI form, so that it can be called
|
|
from outside the form.
|
|
10/09/18 djs Added the SignLoadStage and SignUnloadStage services. These services encapsulate the
|
|
business logic within the click event of the sign button on RDS form and the RDS_UNLOAD
|
|
form respectively, so that they can be called from outside the forms.
|
|
10/10/18 djs Added the SignFQAStage service. This service encapsulates the business logic within the
|
|
click event of the sign button on the RDS_POST_EPI form (located in RDS_POST_EPI_EVENTS),
|
|
so that it can be called from outside the form.
|
|
10/29/18 djs Updated PostUCLRequest and ProcessUCLRequest to allow for a particular Tencor recipe to be
|
|
supplied. (CLEAN_INSP records can have multiple tests associated with them with different
|
|
recipe names.)
|
|
11/20/18 djs Refactored ROTR codebase to better align with functional requirements. GetNumUSLFails,
|
|
GetROTRStatus, GetNumWafersExceedingUSL have all been removed as they are no longer needed.
|
|
Updated wafer image related services to require a tencor recipe to be specified. This allows
|
|
the application to store and view wafer images for separate tencor recipes
|
|
(e.g. 8IN_THIN ROTR vs. 8IN100_ROTR).
|
|
06/13/24 djm Add new stage-specific supplement system.
|
|
|
|
***********************************************************************************************************************/
|
|
#pragma precomp SRP_PreCompiler
|
|
|
|
$insert SERVICE_SETUP
|
|
$insert RDS_EQUATES
|
|
$insert CLEAN_INSP_EQUATES
|
|
$insert REACTOR_EQUATES
|
|
$insert REACT_UTIL_EQU
|
|
$insert WO_MAT_EQUATES
|
|
$insert WO_LOG_EQUATES
|
|
$insert RLIST_EQUATES
|
|
$insert REACT_MODE_EQUATES
|
|
$insert WAFER_IMAGE_REQUESTS_EQUATES
|
|
$insert UCL_REQUESTS_EQUATES
|
|
$insert WAFER_IMAGE_QUEUE_EQUATES
|
|
$insert LSL_USERS_EQUATES
|
|
$insert PROD_SPEC_EQUATES
|
|
$insert TOOL_EQUATES
|
|
$insert PRS_STAGE_EQUATES
|
|
$insert SUPPL_INFO_ARRAY_EQU
|
|
$insert REACT_STATE_EQUATES
|
|
$insert REACT_RUN_EQUATES
|
|
$insert REACT_ITEM_EQUATES
|
|
$insert APP_INSERTS
|
|
$insert SURFACE_SCAN_EQUATES
|
|
$insert COMPANY_EQUATES
|
|
$insert WO_MAT_QA_EQUATES
|
|
$insert SCHED_DET_EQUATES
|
|
$insert MSG_EQUATES
|
|
$insert EXCEL_EQU
|
|
$insert ROTR_REQUESTS_EQUATES
|
|
$insert ROTR_EQUATES
|
|
$insert REACT_LL_EQUATES
|
|
|
|
* Clean Insp Actions
|
|
EQU ACTION$ACTIONS TO 1
|
|
EQU ACTION$SIGS TO 2
|
|
|
|
Equ COL$LOG_FILE to 1
|
|
Equ COL$LOG_DTM to 2
|
|
Equ COL$ACTION to 3
|
|
Equ COL$WH_CD to 4
|
|
Equ COL$LOC_CD to 5
|
|
Equ COL$WO_NOS to 6
|
|
Equ COL$CASS_NOS to 7
|
|
Equ COL$USER_ID to 8
|
|
Equ COL$TAGS to 9
|
|
Equ COL$TOOL_ID to 10
|
|
|
|
Equ LF$ to \0A\
|
|
Equ Comma$ to ','
|
|
|
|
AutoDisplayErrors = False$ ; // Set this to True$ when debugging so all errors will automatically display.
|
|
|
|
Declare subroutine RTI_Set_Debugger, Database_Services, Btree.Extract, Extract_SI_Keys, Rlist, Error_Services
|
|
Declare subroutine Set_Status, Sleepery, Winyield, Yield, SRP_COM, QA_Services, Logging_Services, Obj_RDS
|
|
Declare subroutine Validate, obj_WO_Mat, obj_WO_Mat_Log, obj_React_Status, Record_Lock, obj_React_State, obj_Post_Log
|
|
Declare subroutine RDS_Services, obj_WO_React, RDS_React_Run, Signature_Services, SQL_Services, SRP_Stopwatch
|
|
Declare subroutine Override_Services, Reactor_Services, Lot_Services
|
|
Declare function SRP_Sort_Array, Metrology_Services, obj_RDS_Test, obj_Test_Point_Map, Database_Services
|
|
Declare function Work_Order_Services, SRP_JSON, Logging_Services, Environment_Services, SRP_Trim, Error_Services
|
|
Declare function Min, Max, SRPSendMail, Btree.Extract, GetTickCount, HTTPClient_Services, Obj_RDS, SQL_Services
|
|
Declare function SRP_Encode, SRP_Decode, SRP_COM, QA_Services, RDS_Services, Obj_NCR, Logging_Services, Obj_Reactor
|
|
Declare function Get_Status, Set_Status, obj_WO_Mat, NextKey, MemberOf, FieldCount, obj_React_Status, Obj_Clean_Insp
|
|
Declare function Schedule_Services, Signature_Services, Date, Time, Datetime, Date_Services, SRP_Array, Math_Services
|
|
Declare function Supplement_Services, Reactor_Services
|
|
|
|
LogDate = Oconv(Date(), 'D4/')
|
|
LogTime = Oconv(Time(), 'MTS')
|
|
LoggingDTM = LogDate : ' ' : LogTime
|
|
LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\ROTR'
|
|
QueueLogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' ROTR Performance Log.csv'
|
|
Headers = 'Logging DTM' : @FM : 'RDS Key ID' : @FM : 'Notes'
|
|
QueueObjLog = Logging_Services('NewLog', LogPath, QueueLogFileName, CRLF$, Comma$, Headers, '', False$, False$)
|
|
|
|
ErrorLogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' ROTR Error Log.csv'
|
|
objErrorLog = Logging_Services('NewLog', LogPath, ErrorLogFileName, CRLF$, Comma$, Headers, '', False$, False$)
|
|
|
|
LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\WaferMap'
|
|
WaferMapLogFilename = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Wafer Map Error Log.csv'
|
|
objWaferMapLog = Logging_Services('NewLog', LogPath, WaferMapLogFileName, CRLF$, Comma$, Headers, '', False$, False$)
|
|
|
|
LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\FS_Error'
|
|
LogFilename = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' FS Error Log.csv'
|
|
Headers = 'Logging DTM' : @FM : 'User' : @FM : 'RDSNo' : @FM : 'Error Message'
|
|
objFSErrorLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
|
|
|
|
LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\ReactStatus'
|
|
LogFilename = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' React Status.csv'
|
|
Headers = 'Logging DTM' : @FM : 'User' : @FM : 'CassNo' : @FM : 'RDSNo' : @FM : 'Load DTM' : @FM : 'Service' : @FM : 'Notes'
|
|
objReactStatusLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, 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$
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Services
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// CalculateHgCVData
|
|
//
|
|
// Datapoints - [Required]
|
|
// Returns the average, min, max, edge mean delta, and range percent delimited by @VM
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service CalculateHgCVData(Datapoints)
|
|
|
|
// Find the Min, Max, Average, Edge Mean Delta, and Range % values
|
|
Sum = 0
|
|
Min = DataPoints<0,1>
|
|
Max = DataPoints<0,1>
|
|
Delta1 = 0
|
|
Delta2 = 0
|
|
RangeMin = DataPoints<0,1>
|
|
RangeMax = DataPoints<0,1>
|
|
RangeAvg = 0
|
|
RangePoints = '1,2,5,6,9'
|
|
RangeDataPoints = ''
|
|
CriticalPoints = RangePoints
|
|
NumDataPoints = 0
|
|
Edge4mmPoints = '3,4,7,8'
|
|
Edge10mmPoints = '2,5,6,9'
|
|
Edge4mmSum = 0
|
|
Edge10mmSum = 0
|
|
|
|
For each DataPoint in DataPoints using @VM setting Index
|
|
Locate Index in CriticalPoints using ',' setting unusedIndex then
|
|
NumDataPoints += 1
|
|
Sum = Sum + DataPoint
|
|
// Min & Max
|
|
Min = Min(Min, DataPoint)
|
|
Max = Max(Max, DataPoint)
|
|
// Edge Mean Delta
|
|
* If Index GE 6 AND Index LE 9 then
|
|
* Delta1 = Delta1 + DataPoint
|
|
* end
|
|
* If Index GE 2 AND Index LE 5 then
|
|
* Delta2 = Delta2 + DataPoint
|
|
* end
|
|
end
|
|
// Range %
|
|
Locate Index in RangePoints using ',' setting unusedIndex then
|
|
RangeMin = Min(RangeMin, DataPoint)
|
|
RangeMax = Max(RangeMax, DataPoint)
|
|
RangeAvg = RangeAvg + DataPoint
|
|
RangeDataPoints<0, -1> = DataPoint
|
|
end
|
|
Locate Index in Edge4mmPoints using ',' setting unusedIndex then
|
|
Edge4mmSum += DataPoint
|
|
end
|
|
Locate Index in Edge10mmPoints using ',' setting unusedIndex then
|
|
Edge10mmSum += DataPoint
|
|
end
|
|
Next DataPoint
|
|
// Edge Mean Delta
|
|
RangePct = ''
|
|
EdgeMeanDelta = ''
|
|
EdgeMean4mm = Edge4mmSum / 4
|
|
EdgeMean10mm = Edge10mmSum / 4
|
|
If EdgeMean10mm GT 0 then
|
|
* Delta1Avg = Delta1/4
|
|
* Delta2Avg = Delta2/4
|
|
EdgeMeanDelta = ( (Edge4mmSum - Edge10mmSum) / Edge10mmSum) * 100 ; // Changed divisor to Delta2Avg to match SPC - DJS
|
|
|
|
// Range %
|
|
Range = RangeMax - RangeMin
|
|
NumRangePoints = DCount(RangePoints, ',')
|
|
RangeAvg = RangeAvg / NumRangePoints
|
|
RangePct = (Range / RangeAvg) * 100
|
|
|
|
* EdgeMeanDelta = OConv(IConv(EdgeMeanDelta, 'MD3L'), 'MD3L')
|
|
* RangePct = OConv(IConv(RangePct, 'MD3L'), 'MD3L')
|
|
end
|
|
// Average
|
|
Average = Sum / NumDataPoints
|
|
* Average = OConv(IConv(Average, 'MD3L'), 'MD3L')
|
|
* Min = OConv(IConv(Min, 'MD3L'), 'MD3L')
|
|
* Max = OConv(IConv(Max, 'MD3L'), 'MD3L')
|
|
|
|
FullAvg = ''
|
|
TotalDataPoints = DCount(DataPoints, @VM)
|
|
If TotalDataPoints GT 0 then FullAvg = Sum(DataPoints)/DCount(DataPoints, @VM)
|
|
|
|
ResStdDev = Math_Services('GetStdDev', RangeDataPoints, 'SAMPLE')
|
|
|
|
Response = ''
|
|
Response<0, 1> = Average
|
|
Response<0, 2> = Min
|
|
Response<0, 3> = Max
|
|
Response<0, 4> = EdgeMeanDelta
|
|
Response<0, 5> = RangePct
|
|
Response<0, 6> = EdgeMean4mm ; // 4mm Edge Mean
|
|
Response<0, 7> = FullAvg ; // 9 point avg
|
|
Response<0, 8> = EdgeMean10mm ; // 10mm Edge Mean
|
|
Response<0, 9> = ResStdDev
|
|
|
|
End Service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// CalculateThickData
|
|
//
|
|
// Datapoints - [Required]
|
|
// Returns the average, minimum, and maximum value delimited by @VM
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service CalculateThickData(Datapoints)
|
|
// Find the Min, Max, Average, Edge Mean Delta, and Range % values
|
|
Sum = 0
|
|
Min = DataPoints<0,1>
|
|
Max = DataPoints<0,1>
|
|
NumSigPoints = 0
|
|
For each DataPoint in DataPoints using @VM setting Index
|
|
// Data points 10, 11, 12, 13, and 14 are not significant for calculation purposes per management
|
|
Until Index GE 10
|
|
TrimData = Trim(DataPoint)
|
|
If ( (TrimData NE '') and (TrimData NE 0) and (TrimData NE '0.0') ) then
|
|
NumSigPoints += 1
|
|
Sum = Sum + DataPoint
|
|
// Min & Max
|
|
Min = Min(Min, DataPoint)
|
|
Max = Max(Max, DataPoint)
|
|
end
|
|
Next DataPoint
|
|
// Average
|
|
NumDataPoints = DCount(DataPoints, @VM)
|
|
IF NumSigPoints GT 0 then
|
|
Average = Sum / NumSigPoints
|
|
end else
|
|
Average = 0
|
|
end
|
|
Average = OConv(IConv(Average, 'MD3L'), 'MD3L')
|
|
Min = OConv(IConv(Min, 'MD3L'), 'MD3L')
|
|
Max = OConv(IConv(Max, 'MD3L'), 'MD3L')
|
|
Response = Average : @VM : Min : @VM : Max
|
|
|
|
End Service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// PostROTRRequest
|
|
//
|
|
// Input:
|
|
// RDSNo - [Required]
|
|
//
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service PostROTRRequest(RDSNo)
|
|
|
|
ErrorMessage = ''
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = RDSNo
|
|
LogData<3> = 'Attempting to post ROTR request for ':RDSNo:'.'
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
If RDSNo NE '' then
|
|
// Look for a pre-existing request in the queue
|
|
hDict = Database_Services('GetTableHandle', 'DICT.ROTR_REQUESTS')
|
|
If Error_Services('NoError') then
|
|
RequestKeyID = ''
|
|
Query = 'RDS_NO' : @VM : RDSNo : @FM
|
|
Option = ''
|
|
Flag = ''
|
|
Btree.Extract(Query, 'ROTR_REQUESTS', hDict, RequestKeyID, Option, Flag)
|
|
If Flag EQ 0 then
|
|
If RequestKeyID EQ '' then
|
|
WOMatKey = Xlate('RDS', RDSNo, 'WO_MAT_KEY', 'X')
|
|
FinalSigComp = False$
|
|
* FinalSigComp = Signature_Services('FinalSigComp', WOMatKey)
|
|
If FinalSigComp NE True$ then
|
|
// This is a new request
|
|
RequestDate = Date()
|
|
RequestTime = Time()
|
|
RequestKeyID = RDSNo:'*':RequestDate :'*':RequestTime
|
|
RequestRow = ''
|
|
Database_Services('WriteDataRow', 'ROTR_REQUESTS', RequestKeyID, RequestRow, True$, False$, True$)
|
|
If Error_Services('NoError') then
|
|
// Log success
|
|
LogData<3> = 'Successfully posted ROTR request for ':RDSNo:'.'
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
end else
|
|
ErrorMessage = Error_Services('GetMessage')
|
|
end
|
|
end else
|
|
ErrorMessage = 'FQA signed for RDS ':RDSNo:' ignoring ROTR request.'
|
|
end
|
|
end else
|
|
ErrorMessage = 'Duplicate request found for RDS ':RDSNo:'. Request ignored.'
|
|
end
|
|
end else
|
|
BtreeError = Status()
|
|
ErrorMessage = 'Btree.Extract call failed. Error code: ':BtreeError:'.'
|
|
end
|
|
end else
|
|
ErrorMessage = Error_Services('GetMessage')
|
|
end
|
|
end else
|
|
ErrorMessage = 'Null parameter passed into service call. All parameters are required.'
|
|
end
|
|
If ErrorMessage NE '' then
|
|
LogData<3> = 'Error posting ROTR request for ':RDSNo:'. Error message: ':ErrorMessage
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ProcessROTRRequests
|
|
//
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ProcessROTRRequests()
|
|
|
|
hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
|
|
Lock hSysLists, ServiceKeyID then
|
|
Tablename = 'ROTR_REQUESTS'
|
|
hROTRRequests = Database_Services('GetTableHandle', Tablename)
|
|
If Error_Services('NoError') then
|
|
GoSub ClearCursors
|
|
Set_Status(0)
|
|
SortList = "RESPONSE_DATE" : @FM : "RESPONSE_TIME"
|
|
Cursor = 1
|
|
Select TableName By SortList Setting Cursor then
|
|
EOF = False$
|
|
Loop
|
|
ReadNext RequestKeyID Using Cursor else EOF = True$
|
|
Until EOF
|
|
Lock hROTRRequests, RequestKeyID then
|
|
RDSNo = Field(RequestKeyID, '*', 1)
|
|
RequestDate = Field(RequestKeyID, '*', 2)
|
|
RequestTime = Field(RequestKeyID, '*', 3)
|
|
If ( (RDSNo NE '') and (RequestDate NE '') and (RequestTime NE '') ) then
|
|
RequestRow = Database_Services('ReadDataRow', 'ROTR_REQUESTS', RequestKeyID)
|
|
QA_Services('ProcessROTRRequest', RDSNo)
|
|
If Error_Services('NoError') then
|
|
// Request was successful, so remove the request from the queue
|
|
LogDate = Date()
|
|
LogTime = Time()
|
|
TimeTaken = LogTime - RequestTime
|
|
Database_Services('DeleteDataRow', 'ROTR_REQUESTS', RequestKeyID, True$)
|
|
LogData = ''
|
|
LogData<1> = Oconv(LogDate, 'D4/') : ' ' : Oconv(LogTime, 'MTS')
|
|
LogData<2> = RDSNo
|
|
LogData<3> = 'Request successfully processed. Time taken: ':TimeTaken:' seconds. Deleting ROTR request from queue.'
|
|
Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)
|
|
end else
|
|
ErrorMsg = Error_Services('GetMessage')
|
|
LogData = ''
|
|
LogData<1> = Oconv(Date(), 'D4/') : ' ' : Oconv(Time(), 'MTS')
|
|
LogData<2> = RDSNo
|
|
LogData<3> = 'Request failed to process. Error message: ':ErrorMsg
|
|
Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)
|
|
// Keep request in the queue (i.e. do not delete it)
|
|
RequestRow<ROTR_REQUESTS.RESPONSE_TIME$> = Time()
|
|
RequestRow<ROTR_REQUESTS.RESPONSE_DATE$> = Date()
|
|
RequestRow<ROTR_REQUESTS.RESPONSE$> = ErrorMsg
|
|
Database_Services('WriteDataRow', 'ROTR_REQUESTS', RequestKeyID, RequestRow, True$, False$, True$)
|
|
Unlock hROTRRequests, RequestKeyID else
|
|
Error_Services('Add', 'Error unlocking ':RequestKeyID:', ':Tablename:' within ':Service)
|
|
end
|
|
end
|
|
end else
|
|
LogData = ''
|
|
LogData<1> = Oconv(LogDate, 'D4/') : ' ' : Oconv(LogTime, 'MTS')
|
|
LogData<2> = RequestKeyID
|
|
LogData<3> = 'Malformed request selected from ROTR_REQUESTS table in ':Service:' service.'
|
|
Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)
|
|
end
|
|
end
|
|
Repeat
|
|
end else
|
|
StatusCode = Status()
|
|
LogData = ''
|
|
LogData<1> = Oconv(LogDate, 'D4/') : ' ' : Oconv(LogTime, 'MTS')
|
|
LogData<2> = ''
|
|
LogData<3> = 'Error selecting ROTR_REQUESTS in ':Service:' service. Error code ':StatusCode
|
|
Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)
|
|
end
|
|
end else
|
|
ErrorMsg = Error_Services('GetMessage')
|
|
end
|
|
Unlock hSysLists, ServiceKeyID else Null
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ProcessROTRRequest
|
|
//
|
|
// Input:
|
|
// RDSNo - [Required]
|
|
//
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ProcessROTRRequest(RDSNo)
|
|
|
|
ErrorMessage = ''
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = RDSNo
|
|
LogData<3> = 'Attempting to process ROTR request for ':RDSNo:'.'
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
If RDSNo NE '' then
|
|
LWICINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
|
|
If LWICINo NE '' then
|
|
HaveLock = Database_Services('GetKeyIDLock', 'CLEAN_INSP', LWICINo)
|
|
If HaveLock EQ True$ then
|
|
CIRec = Database_Services('ReadDataRow', 'CLEAN_INSP', LWICINo)
|
|
If Error_Services('NoError') then
|
|
Response = QA_Services('GetROTRStatus', RDSNo)
|
|
If Error_Services('NoError') then
|
|
ResponseCopy = Response
|
|
Swap @FM with ',' in ResponseCopy
|
|
LogData<3> = 'ROTR Status response: ':Response
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
* ROTRRec = ''
|
|
* ROTRRec<ROTR.REACTOR_STATUS$> = Response<1>
|
|
* ROTRRec<ROTR.REACTOR_STATUS_REASON$> = Response<2>
|
|
* ROTRRec<ROTR.REACTOR_NCR_COUNT$> = Response<3>
|
|
* ROTRRec<ROTR.REACTOR_ZERO_NCR_RUN_COUNT$> = Response<4>
|
|
* ROTRRec<ROTR.REACTOR_UCL_EXCEEDED_RUN_COUNT$> = Response<5>
|
|
* Database_Services('WriteDataRow', 'ROTR', LWICINo, ROTRRec, True$, False$, True$)
|
|
CIRec<CLEAN_INSP_ROTR_REACTOR_STATUS$> = Response<1>
|
|
CIRec<CLEAN_INSP_ROTR_REACTOR_STATUS_REASON$> = Response<2>
|
|
CIRec<CLEAN_INSP_ROTR_REACTOR_NCR_COUNT$> = Response<3>
|
|
CIRec<CLEAN_INSP_ROTR_REACTOR_ZERO_NCR_RUN_COUNT$> = Response<4>
|
|
CIRec<CLEAN_INSP_ROTR_REACTOR_UCL_EXCEEDED_RUN_COUNT$> = Response<5>
|
|
// Set ROTR Update Flag to trigger CLEAN_INSP_ACTIONS routine (via BASE_MFS).
|
|
// That routine will recalculate the final ROTR status.
|
|
* CIRec<CLEAN_INSP_ROTR_UPDATE_FLAG$> = True$
|
|
Database_Services('WriteDataRow', 'CLEAN_INSP', LWICINo, CIRec, True$, False$, True$)
|
|
end else
|
|
ErrorMessage = 'Error in service ':Service:'. Failed to retrieve ROTR status. Error message: ':Error_Services('GetMessage')
|
|
LogData<3> = ErrorMessage
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
end
|
|
end else
|
|
ErrorMessage = 'Error in service ':Service:'. Failed to read CLEAN_INSP record: ':LWICINo:'. Error message: ':Error_Services('GetMessage')
|
|
LogData<3> = ErrorMessage
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
end
|
|
Database_Services('ReleaseKeyIDLock', 'CLEAN_INSP', LWICINo)
|
|
end else
|
|
ErrorMessage = 'Error in service ':Service:'. Failed to lock CLEAN_INSP record: ':LWICINo:'. Error message: ':Error_Services('GetMessage')
|
|
LogData<3> = ErrorMessage
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
end
|
|
end else
|
|
ErrorMessage = 'Error in service ':Service:'. Failed to find an LWI CLEAN_INSP key for RDS ':RDSNo:'.'
|
|
LogData<3> = ErrorMessage
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
end
|
|
end else
|
|
ErrorMessage = 'Error in service ':Service:'. Null RDSNo passed in.'
|
|
LogData<3> = ErrorMessage
|
|
Logging_Services('AppendLog', objErrorLog, LogData, @RM, @FM)
|
|
end
|
|
If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetROTRStatus
|
|
//
|
|
// Input: RDSKey - [Required]
|
|
//
|
|
// Response: ROTRStatus
|
|
//
|
|
// Returns the failure scan status of the work order associated with a given RDS. Note this is specifically designed
|
|
// for ASM, HTR, and ASM+. This procedure would need to be updated to support EpiPro and/or GAN.
|
|
//
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetROTRStatus(RDSKey)
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSKey)
|
|
WONo = RDSRec<RDS_WO$>
|
|
RDSCassNo = RDSRec<RDS_CASS_NO$>
|
|
ReactorKey = RDSRec<RDS_REACTOR$>
|
|
CurrPSN = RDSRec<RDS_PROD_SPEC_ID$>
|
|
ReactorRec = Database_Services('ReadDataRow', 'REACTOR', ReactorKey)
|
|
// These next six variables are set by engineering per reactor.
|
|
XPrevRuns = ReactorRec<REACTOR_X_PREV_RUNS$>
|
|
NCRLimit = ReactorRec<REACTOR_NCR_LIMIT$>
|
|
ZeroNCRMin = ReactorRec<REACTOR_ZERO_NCR_MIN$>
|
|
UCLExceededLimit = ReactorRec<REACTOR_UCL_LIMIT$>
|
|
StopRDS = ReactorRec<REACTOR_ROTR_STOP_RDS$>
|
|
ROTRMaintReset = ReactorRec<REACTOR_ROTR_MAINT_RESET$>
|
|
ROTRStatus = 'P'
|
|
ROTRFailReason = ''
|
|
RunCount = 0
|
|
NCRCount = 0
|
|
ZeroNCRCount = 0
|
|
UCLExceededCount = 0
|
|
NumOverrideSigs = 0
|
|
NumBlockOverrideSigs = 0
|
|
WafersProcessed = 0
|
|
PrevCass = RDSCassNo - 1
|
|
ThisRDSNo = ''
|
|
Done = False$
|
|
// Collect QA data for current work order. PSNs will match within current work order, so no need to check.
|
|
For Cass = PrevCass to 1 Step -1
|
|
WOMatKey = WONo :'*': Cass
|
|
WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
|
|
ThisRDSNo = WOMatRec<WO_MAT_RDS_NO$>
|
|
If ROTRMaintReset EQ True$ then If ThisRDSNo EQ StopRDS then Done = True$
|
|
While (RunCount LE XPrevRuns - 1) and (NCRCount LT NCRLimit) and (UCLExceededCount LT UCLExceededLimit) |
|
|
and (Done EQ False$)
|
|
RunCount = RunCount + 1
|
|
CleanInspKey = Xlate('RDS', ThisRDSNo, 'LWI_CI_NO', 'X')
|
|
CIRec = Database_Services('ReadDataRow', 'CLEAN_INSP', CleanInspKey)
|
|
If Error_Services('NoError') then
|
|
ScanRecipes = CIRec<CLEAN_INSP_SCAN_RECIPE$>
|
|
SpecRecipes = CIRec<CLEAN_INSP_SPEC_SURFSCAN_RECIPE$>
|
|
// Updated code to look at the original scanned SOD average value before we remove the failed
|
|
// wafers within CLEAN_INSP_ACTIONS.
|
|
ScanSODAvgs = CIRec<CLEAN_INSP_SCAN_SUM_OF_DEF_AVG_FAIL$>
|
|
SpecSODAvgs = CIRec<CLEAN_INSP_SPEC_SUM_OF_DEF_AVG$>
|
|
OverrideSigs = CIRec<CLEAN_INSP_SIGN_ROTR_SIGNATURE$>
|
|
ROTRReasons = CIRec<CLEAN_INSP_ROTR_ACTION_REASON$>
|
|
NumScanRecipes = DCount(ScanRecipes, @VM)
|
|
UCLExceeded = False$
|
|
// Check each spec recipe to find latest matching scan. Then check if the SOD Avg (UCL) has been exceeded.
|
|
For Each SpecRecipeName in SpecRecipes using @VM setting SpecRecipeIndex
|
|
// Look for the last scan entry related to the current spec recipe.
|
|
RecipeFound = False$
|
|
For ScanRecipeIndex = NumScanRecipes To 1 Step -1
|
|
ScanRecipeName = ScanRecipes<0, ScanRecipeIndex>
|
|
RecipeFound = (ScanRecipeName EQ SpecRecipeName)
|
|
Until RecipeFound
|
|
Next ScanRecipeIndex
|
|
If RecipeFound EQ True$ then
|
|
ScanSODAvg = OConv(ScanSODAvgs<0, ScanRecipeIndex>, 'MD3')
|
|
SpecSODAvg = SpecSODAvgs<0, ScanRecipeIndex>
|
|
OverrideSig = OverrideSigs<0, ScanRecipeIndex>
|
|
ROTRReason = ROTRReasons<0, ScanRecipeIndex>
|
|
If (SpecSODAvg EQ '') or (SpecSODAvg EQ 0) then
|
|
SpecSODAvg = QA_Services('PostUCLRequest', ThisRDSNo, SpecRecipeName)
|
|
If (SpecSODAvg NE '') and (SpecSODAvg NE 0) then
|
|
SpecSODAvgs<0, SpecRecipeIndex> = SpecSODAvg
|
|
CIRec<CLEAN_INSP_SPEC_SUM_OF_DEF_AVG$> = SpecSODAvgs
|
|
Database_Services('WriteDataRow', 'CLEAN_INSP', CleanInspKey, CIRec, True$, False$, True$)
|
|
end
|
|
end
|
|
If (SpecSODAvg NE '') and (SpecSODAvg NE 0) then
|
|
// A null or zero value for the spec SOD Avg (UCL) indicates there was a problem
|
|
// retrieving the UCL value from SPC. Checking here to avoid runtime errors.
|
|
UCLExceeded = (ScanSODAvg GT SpecSODAvg)
|
|
end
|
|
If UCLExceeded EQ True$ then UCLExceededCount += 1
|
|
If OverrideSig NE '' then NumOverrideSigs += 1
|
|
If (ROTRReason EQ 'NCR Limit Exceeded') or (ROTRReason EQ 'Zero NCR Minimum Not Met') |
|
|
or (ROTRReason EQ 'UCL Run Count Exceeded') then NumBlockOverrideSigs += 1
|
|
end
|
|
Until UCLExceeded EQ True$
|
|
Next SpecRecipeName
|
|
ThisNCRCount = Xlate('RDS', ThisRDSNo, 'TOT_REJ', 'X')
|
|
If ThisNCRCount EQ 0 then
|
|
ZeroNCRCount += 1
|
|
end else
|
|
NCRCount += ThisNCRCount
|
|
end
|
|
end else
|
|
Error = Error_Services('GetMessage')
|
|
end
|
|
Next Cass
|
|
|
|
// Check if we have met XPrevRuns Criteria. If not, then look at previous work orders associated with this reactor.
|
|
// Work with most recent runs to oldest runs. Previous work order PSNs must match the current RDS' PSN.
|
|
If (RunCount LE XPrevRuns - 1) and (NCRCount LT NCRLimit) and (UCLExceededCount LT UCLExceededLimit) |
|
|
and (Done EQ False$) then
|
|
CurrWorkOrder = WONo
|
|
StartDate = Date() - 90
|
|
WorkOrderList = ''
|
|
SchedDetKeys = Schedule_Services('GetScheduleDetailKeys', StartDate, Date(), ReactorKey, '', '')
|
|
For each SchedDetKey in SchedDetKeys
|
|
SchedDet = Schedule_Services('GetScheduleDetail', SchedDetKey)
|
|
ThisWorkOrder = SchedDet<SCHED_DET_WO_NO$>
|
|
Locate ThisWorkOrder in WorkOrderList using @FM setting fPos else
|
|
If ThisWorkOrder NE CurrWorkOrder then
|
|
WorkOrderList = Insert(WorkOrderList, 0, 0, 0, ThisWorkOrder)
|
|
end
|
|
end
|
|
Next SchedDetKey
|
|
For each WorkOrder in WorkOrderList using @FM setting fPos
|
|
WOMatKeys = Xlate('WO_LOG', WorkOrder, WO_LOG_WO_MAT_KEY$, 'X')
|
|
NumWOMatKeys = DCount(WoMatKeys, @VM)
|
|
For WOMatKeyIndex = NumWOMatKeys to 1 Step -1
|
|
ThisWOMatKey = WOMatKeys<1, WOMatKeyIndex>
|
|
ThisRDSNo = Xlate('WO_MAT', ThisWOMatKey, 'RDS_NO', 'X')
|
|
If ROTRMaintReset EQ True$ then If ThisRDSNo EQ StopRDS then Done = True$
|
|
While (RunCount LE XPrevRuns - 1) and (NCRCount LT NCRLimit) and (UCLExceededCount LT UCLExceededLimit) |
|
|
and (Done EQ False$)
|
|
RunCount = RunCount + 1
|
|
CleanInspKey = Xlate('RDS', ThisRDSNo, 'LWI_CI_NO', 'X')
|
|
CIRec = Database_Services('ReadDataRow', 'CLEAN_INSP', CleanInspKey)
|
|
If Error_Services('NoError') then
|
|
ScanRecipes = CIRec<CLEAN_INSP_SCAN_RECIPE$>
|
|
SpecRecipes = CIRec<CLEAN_INSP_SPEC_SURFSCAN_RECIPE$>
|
|
// Updated code to look at the original scanned SOD average value before we remove the failed
|
|
// wafers within CLEAN_INSP_ACTIONS.
|
|
ScanSODAvgs = CIRec<CLEAN_INSP_SCAN_SUM_OF_DEF_AVG_FAIL$>
|
|
SpecSODAvgs = CIRec<CLEAN_INSP_SPEC_SUM_OF_DEF_AVG$>
|
|
OverrideSigs = CIRec<CLEAN_INSP_SIGN_ROTR_SIGNATURE$>
|
|
ROTRReasons = CIRec<CLEAN_INSP_ROTR_ACTION_REASON$>
|
|
NumScanRecipes = DCount(ScanRecipes, @VM)
|
|
UCLExceeded = False$
|
|
// Check each spec recipe to find latest matching scan.
|
|
// Then check if the SOD Avg (UCL) has been exceeded.
|
|
For Each SpecRecipeName in SpecRecipes using @VM setting SpecRecipeIndex
|
|
// Look for the last scan entry related to the current spec recipe.
|
|
RecipeFound = False$
|
|
For ScanRecipeIndex = NumScanRecipes To 1 Step -1
|
|
ScanRecipeName = ScanRecipes<0, ScanRecipeIndex>
|
|
RecipeFound = (ScanRecipeName EQ SpecRecipeName)
|
|
Until RecipeFound
|
|
Next ScanRecipeIndex
|
|
If RecipeFound EQ True$ then
|
|
ScanSODAvg = OConv(ScanSODAvgs<0, ScanRecipeIndex>, 'MD3')
|
|
SpecSODAvg = SpecSODAvgs<0, ScanRecipeIndex>
|
|
OverrideSig = OverrideSigs<0, ScanRecipeIndex>
|
|
ROTRReason = ROTRReasons<0, ScanRecipeIndex>
|
|
If (SpecSODAvg EQ '') or (SpecSODAvg EQ 0) then
|
|
SpecSODAvg = QA_Services('PostUCLRequest', ThisRDSNo, SpecRecipeName)
|
|
If (SpecSODAvg NE '') and (SpecSODAvg NE 0) then
|
|
SpecSODAvgs<0, SpecRecipeIndex> = SpecSODAvg
|
|
CIRec<CLEAN_INSP_SPEC_SUM_OF_DEF_AVG$> = SpecSODAvgs
|
|
Database_Services('WriteDataRow','CLEAN_INSP',CleanInspKey,CIRec,True$ False$,True$)
|
|
end
|
|
end
|
|
If (SpecSODAvg NE '') and (SpecSODAvg NE 0) then
|
|
// A null or zero value for the spec SOD Avg (UCL) indicates there was a problem
|
|
// retrieving the UCL value from SPC. Checking here to avoid any runtime errors.
|
|
UCLExceeded = (ScanSODAvg GT SpecSODAvg)
|
|
end
|
|
If UCLExceeded EQ True$ then UCLExceededCount += 1
|
|
If OverrideSig NE '' then NumOverrideSigs += 1
|
|
If (ROTRReason EQ 'NCR Limit Exceeded') or (ROTRReason EQ 'Zero NCR Minimum Not Met') |
|
|
or (ROTRReason EQ 'UCL Run Count Exceeded') then NumBlockOverrideSigs += 1
|
|
end
|
|
Until UCLExceeded EQ True$
|
|
Next SpecRecipeName
|
|
ThisNCRCount = Xlate('RDS', ThisRDSNo, 'TOT_REJ', 'X')
|
|
If ThisNCRCount EQ 0 then
|
|
ZeroNCRCount += 1
|
|
end else
|
|
NCRCount += ThisNCRCount
|
|
end
|
|
end else
|
|
Error = Error_Services('GetMessage')
|
|
end
|
|
Next WOMatKeyIndex
|
|
Next WorkOrder
|
|
end
|
|
|
|
// Check QA Criteria
|
|
Begin Case
|
|
Case NCRCount GE NCRLimit
|
|
ROTRStatus = 'F'
|
|
ROTRFailReason = 'NCR Limit Exceeded'
|
|
Case (ZeroNCRCount LT ZeroNCRMin) and (RunCount GE ZeroNCRMin)
|
|
ROTRStatus = 'F'
|
|
ROTRFailReason = 'Zero NCR Minimum Not Met'
|
|
Case UCLExceededCount GE UCLExceededLimit
|
|
ROTRStatus = 'F'
|
|
ROTRFailReason = 'UCL Run Count Exceeded'
|
|
Case Otherwise$
|
|
// All Pass - Allow auto-sign ROTR and reset ROTR override counter logic
|
|
ReactorRec<REACTOR_ROTR_OVERRIDE_COUNT$> = 0
|
|
ReactorRec<REACTOR_PREVIOUS_ROTR_STATUS_REASON$> = ''
|
|
ReactorRec<REACTOR_PREVIOUS_ROTR_OVERRIDE_RDS$> = ''
|
|
Database_Services('WriteDataRow', 'REACTOR', ReactorKey, ReactorRec, True$, False$, True$)
|
|
End Case
|
|
|
|
ReactorYield = ''
|
|
If RunCount NE 0 then
|
|
WafersProcessed = RunCount * 25
|
|
ReactorYield = (WafersProcessed - NCRCount) / WafersProcessed
|
|
end
|
|
|
|
Response = ''
|
|
Response<1> = ROTRStatus
|
|
Response<2> = ROTRFailReason
|
|
Response<3> = NCRCount
|
|
Response<4> = ZeroNCRCount
|
|
Response<5> = UCLExceededCount
|
|
Response<6> = RunCount
|
|
Response<7> = NumOverrideSigs
|
|
Response<8> = NumBlockOverrideSigs
|
|
Response<9> = ReactorYield
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetMaintenanceScanStatus
|
|
//
|
|
// Input: RDSKey - [Required]
|
|
//
|
|
// Response: True$(1) or False$(0)
|
|
//
|
|
// The initial two RDS runs following reactor maintenance related to ROTR issues must undergo a 100% Tencor scan.
|
|
// This service checks to see if an RDS falls into this requirement. If so, then true$ is returned, else false$.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetMaintenanceScanStatus(RDSKey)
|
|
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSKey)
|
|
ReactorNo = RDSRec<RDS_REACTOR$>
|
|
CurrMode = Xlate('REACTOR', ReactorNo, 'CURR_MODE', 'X')
|
|
If CurrMode EQ 'P' then
|
|
// Reactor is in production mode. Check if it recently underwent ROTR maintenance.
|
|
SearchStart = Date() - 30
|
|
Cursor = ''
|
|
EOF = False$
|
|
Query = 'SELECT REACTOR_LOG WITH REACTOR EQ ':ReactorNo:' AND WITH START_DATE >= ':Quote(OConv(SearchStart, 'D4/')) |
|
|
: ' BY-DSND START_DATE BY-DSND START_TIME'
|
|
GoSub ClearCursors
|
|
Rlist(Query, Target_ActiveList$, '', '', '')
|
|
// Inspect most recent reactor log to see if it was ROTR related.
|
|
If @RecCount then
|
|
ReadNext ReactorLogID else ReactorLogID = ''
|
|
If ReactorLogID NE '' then
|
|
ROTRMaint = Xlate('REACTOR_LOG', ReactorLogID, 'ROTR', 'X')
|
|
If ROTRMaint EQ True$ then
|
|
// Most recent maintenance was ROTR related, therefore the first two RDS runs require
|
|
// 100% failure scans to ensure reactor is operating properly.
|
|
ProdStartDTM = Xlate('REACTOR', ReactorNo, 'CURR_MODE_DTM', 'X')
|
|
WOStepKey = RDSRec<RDS_WO_STEP_KEY$>
|
|
ProdStartDate = ProdStartDTM[1, 'F.']
|
|
ProdStartTime = ProdStartDTM[-1, 'B.']
|
|
WOStepRDSKeys = ''
|
|
FailFlag = ''
|
|
|
|
// Count number of RDS runs since reactor entered production mode.
|
|
Query = 'SELECT RDS WITH DATE_IN > ' : Quote(OConv(ProdStartDate, 'D4/'))
|
|
Query := ' OR WITH DATE_IN = ' : Quote(OConv(ProdStartDate, 'D4/'))
|
|
Query := ' AND WITH REACTOR = ' : ReactorNo
|
|
GoSub ClearCursors
|
|
Set_Status(0)
|
|
Rlist(Query, Target_ActiveList$, '', '', '')
|
|
ErrorCode = ''
|
|
If Get_Status(ErrorCode) then
|
|
ErrorMsg = 'Error calling rlist in the ' : Service : ' service. Error code: ':ErrorCode
|
|
Error_Services('Add', ErrorMsg)
|
|
end
|
|
RunsSinceProdDate = @RecCount
|
|
// Now we need to subtract the runs on ProdStartDate, but before ProdStartTime.
|
|
// Truncate production start time to speed up query.
|
|
ProdStartTime = ProdStartTime[1,5]
|
|
Query = 'SELECT RDS WITH TIME_IN < ' : ProdStartTime
|
|
Query := ' AND WITH DATE_IN = ' : Quote(OConv(ProdStartDate, 'D4/'))
|
|
Query := ' AND WITH REACTOR = ' : ReactorNo
|
|
GoSub ClearCursors
|
|
Set_Status(0)
|
|
Rlist(Query, Target_ActiveList$, '', '', '')
|
|
If Get_Status(ErrorCode) then
|
|
ErrorMsg = 'Error calling rlist in the ' : Service : ' service. Error code: ':ErrorCode
|
|
Error_Services('Add', ErrorMsg)
|
|
end
|
|
RunsBeforeProdTime = @RecCount
|
|
RunsSinceProdDTM = RunsSinceProdDate - RunsBeforeProdTime
|
|
// If the number of runs since production mode began is less than 2,
|
|
// then 100% failure scan is required for this RDS. -> Return True$
|
|
If RunsSinceProdDTM LE 2 then
|
|
Response = True$
|
|
end
|
|
GoSub ClearCursors
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetUCL
|
|
//
|
|
// Input: RDSKey - [Required]
|
|
// TencorRecipe - [Optional] - If no recipe is supplied, then this service will attempt to look for the
|
|
// most recent scan to use to fetch the associated UCL value.
|
|
//
|
|
// Response: Upper Control Limit (UCL) Specification from SPC database for the given RDS.
|
|
//
|
|
// Note: This can't be called directly from the EC server because the SPC database is not within the EC domain.
|
|
// Use the PostUCLRequest service within this service module to retrieve the UCL value. This request will
|
|
// be processed on the App server (if the Process UCL Requests service is running in the service manager)
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetUCL(RDSKey, TencorRecipe)
|
|
|
|
If RDSKey NE '' then
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSKey)
|
|
If Error_Services('NoError') then
|
|
ReactorKey = RDSRec<RDS_REACTOR$>
|
|
If ReactorKey EQ '' then
|
|
ReactorKey = Xlate('RDS', RDSKey, 'SCHED_REACTOR', 'X')
|
|
end
|
|
LWICIKey = Xlate('RDS', RDSKey, 'LWI_CI_NO', 'X')
|
|
If TencorRecipe EQ '' then
|
|
// No recipe name supplied, so grab the most recent scan recipe name.
|
|
ScanRecipes = Xlate('CLEAN_INSP', LWICIKey, CLEAN_INSP_SPEC_SURFSCAN_RECIPE$, 'X')
|
|
TencorRecipe = ScanRecipes<1,1>
|
|
end
|
|
TestName = 'Average Sum of Defects'
|
|
ReactorUCLMean = ''
|
|
ReactorUCLSigma = ''
|
|
ReactorUCLData = ''
|
|
UCL = ''
|
|
|
|
QueryStatement = 'SELECT [F_MEAN] as ProcessMean,[F_SP] as ProcessSigma ' |
|
|
: 'FROM [SPCEPIWORLD].[dbo].[CTRL_LIM] CTRL ' |
|
|
: 'JOIN [SPCEPIWORLD].[dbo].[PART_DAT] PART ON PART.F_PART = CTRL.F_PART ' |
|
|
: 'JOIN [SPCEPIWORLD].[dbo].[PRCS_DAT] PROCESS ON PROCESS.F_PRCS = CTRL.F_PRCS ' |
|
|
: 'JOIN [SPCEPIWORLD].[dbo].[TEST_DAT] TEST ON TEST.F_TEST = CTRL.F_TEST ' |
|
|
: "WHERE PART.F_NAME = '" : TencorRecipe : "' AND " |
|
|
: "PROCESS.F_NAME = '" : ReactorKey : "' AND " |
|
|
: "TEST.F_NAME = '" : TestName : "'"
|
|
ReactorUCLData = SQL_Services('GetDataRows', 'SPC', QueryStatement)
|
|
// Occassionally more than one record will be returned. Just grab the first record.
|
|
ReactorUCLData = Field(ReactorUCLData, @RM, 1)
|
|
If ReactorUCLData NE '' then
|
|
ReactorUCLMean = ReactorUCLData<1>
|
|
ReactorUCLSigma = ReactorUCLData<2>
|
|
UCL = ReactorUCLMean + ( 3 * ReactorUCLSigma )
|
|
end
|
|
end else
|
|
ErrorMessage = Error_Services('GetMessage')
|
|
end
|
|
Response = UCL
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// PostUCLRequest
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS number associated with the desired UCL. [Required]
|
|
// TencorRecipe - Particular Tencor recipe associated with the desired UCL. [Optional]
|
|
//
|
|
// Output:
|
|
// Response - Upper Control Limit (UCL) associated with the given RDS.
|
|
//
|
|
// Posts a wafer map image request to the UCL_REQUESTS database table, which is then
|
|
// processed on the APP server by the Process UCL Requests service.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service PostUCLRequest(RDSNo, TencorRecipe)
|
|
|
|
UCL = ''
|
|
RequestDate = Date()
|
|
RequestTime = Time()
|
|
RequestKeyID = RequestDate : '*' : RequestTime : '*' : RDSNo
|
|
RequestRow = ''
|
|
If TencorRecipe NE '' then RequestRow<UCL_REQUESTS.TENCOR_RECIPE$> = TencorRecipe
|
|
Database_Services('WriteDataRow', 'UCL_REQUESTS', RequestKeyID, RequestRow, True$, '', True$)
|
|
If Error_Services('NoError') then
|
|
TimeExpired = False$
|
|
Start = GetTickCount()
|
|
Loop
|
|
RequestRow = Database_Services('ReadDataRow', 'UCL_REQUESTS', RequestKeyID)
|
|
ResponseDate = RequestRow<UCL_REQUESTS.RESPONSE_DATE$>
|
|
If ResponseDate NE '' then
|
|
UCL = RequestRow<UCL_REQUESTS.UCL$>
|
|
If UCL EQ '' then Error_Services('Add', 'UCL not found for RDS: ' : RDSNo : ' in service :' Service)
|
|
end
|
|
// Time will expire after 30 seconds.
|
|
If GetTickCount() - Start GE 30000 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', 'Timed out attempting to retrieve UCL for RDS: ': RDSNo)
|
|
end
|
|
Database_Services('DeleteDataRow', 'UCL_REQUESTS', RequestKeyID, True$)
|
|
end else
|
|
ErrorMsg = Error_Services('GetMessage')
|
|
Error_Services('Add', 'Error posting UCL request for RDS: ': RDSNo : ' Error: ' : ErrorMsg)
|
|
end
|
|
|
|
Response = UCL
|
|
|
|
return
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ProcessUCLRequests
|
|
//
|
|
// Service that attempts to process all UCL requests. These requests are queued in the
|
|
// UCL_REQUESTS table and are to be processed on the App server (at least while SPC database is not in EC domain).
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ProcessUCLRequests()
|
|
|
|
hValidationRequests = Database_Services('GetTableHandle', 'UCL_REQUESTS')
|
|
If Error_Services('NoError') then
|
|
Sentence = 'SELECT UCL_REQUESTS 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', 'UCL_REQUESTS', RequestKeyID)
|
|
RDSNo = Field(RequestKeyID, '*', 3, 1)
|
|
TencorRecipe = RequestRow<UCL_REQUESTS.TENCOR_RECIPE$>
|
|
If TencorRecipe NE '' then
|
|
RequestRow<UCL_REQUESTS.UCL$> = QA_Services('GetUCL', RDSNo, TencorRecipe)
|
|
end else
|
|
RequestRow<UCL_REQUESTS.UCL$> = QA_Services('GetUCL', RDSNo)
|
|
end
|
|
RequestRow<UCL_REQUESTS.RESPONSE_DATE$> = Date()
|
|
RequestRow<UCL_REQUESTS.RESPONSE_TIME$> = Time()
|
|
Database_Services('WriteDataRow', 'UCL_REQUESTS', RequestKeyID, RequestRow, True$)
|
|
Unlock hValidationRequests, RequestKeyID else Null
|
|
end
|
|
Repeat
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSODPerWafer
|
|
//
|
|
// Input: RDSKey - [Required]
|
|
//
|
|
// Response: The Sum of Defects (SOD) for each wafer in a given cassette (RDS). This is used when the SOD of any given
|
|
// exceeds the Upper Control Limit (UCL).
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSODPerWafer(RDSKey, TencorRecipe, ScanDTM)
|
|
|
|
If RDSKey NE '' and TencorRecipe NE '' then
|
|
|
|
If Num(ScanDTM) then ScanDTM = OConv(ScanDTM, 'DT/^S')
|
|
Query = "DECLARE @RDS varchar(10) "
|
|
Query := "DECLARE @RECIPE varchar(30) "
|
|
Query := "DECLARE @INSERT_DT datetime "
|
|
Query := "SET @RDS = '":RDSKey:"' "
|
|
Query := "SET @RECIPE = '":TencorRecipe:"' "
|
|
Query := "SET @INSERT_DT = '":ScanDTM:"' "
|
|
Query := "SELECT child.slot, child.sumofdefects, child.sort "
|
|
Query := "FROM Metrology.dbo.TencorRunHeader header "
|
|
Query := "INNER JOIN Metrology.dbo.TencorRunData child on header.id = child.headerid "
|
|
Query := "WHERE header.rds = @RDS AND header.recipe like @RECIPE + '%' "
|
|
If ScanDTM NE '' then
|
|
Query := "AND Child.InsertDate BETWEEN (SELECT(DATEADD(minute, -5, @INSERT_DT))) and (SELECT(DATEADD(minute, 5, @INSERT_DT))) "
|
|
end
|
|
Query := "ORDER BY header.insertDate DESC, child.slot ASC"
|
|
|
|
MetrologyWaferRunData = SQL_Services('PostSQLRequest', 'SPC', Query)
|
|
Response = ''
|
|
// Data validation
|
|
// Ensure WaferNo column is formatted correctly (* followed by two integers)
|
|
ValidData = True$
|
|
Done = False$
|
|
NumWafers = DCount(MetrologyWaferRunData, @RM)
|
|
|
|
For WaferIndex = NumWafers to 1 Step -1
|
|
Wafer = Field(MetrologyWaferRunData, @RM, WaferIndex)
|
|
Until ValidData EQ False$
|
|
WaferNo = Wafer<1>
|
|
If WaferNo NE '' then
|
|
ValidData = WaferNo Matches '*2N'
|
|
If ValidData then
|
|
WaferNo = Trim(WaferNo[-1, 'B*'])
|
|
If WaferNo[1, 1] EQ 0 then WaferNo [1, 1] = ''
|
|
If WaferNo EQ 1 then Done = True$
|
|
SumOfDefects = Wafer<2>
|
|
// Validate SumOfDefects value is formatted correctly (any number of integers)
|
|
ValidData = SumOfDefects Matches '0N'
|
|
Sort = Wafer<3>
|
|
Locate WaferNo in Response<1> using @VM setting WaferPos else
|
|
// No SOD value for this wafer yet recorded, so add it to the response array.
|
|
Response<1, WaferNo> = WaferNo
|
|
Response<2, WaferNo> = SumOfDefects
|
|
Response<3, WaferNo> = Sort
|
|
end
|
|
end
|
|
end
|
|
Until Done EQ True$
|
|
Next WaferIndex
|
|
|
|
If ValidData EQ False$ then
|
|
ErrorMsg = 'Malformed data received'
|
|
end
|
|
|
|
end else
|
|
ErrorMsg = 'Null value for RDSKey or TencorRecipe passed in. Both values are required.'
|
|
Error_Services('Add', ErrorMsg)
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// PostWaferImageRequest
|
|
//
|
|
// Input:
|
|
// Username - Username of user whom is posting the request.
|
|
// RDSNo - RDS number associated with the desired wafer image.
|
|
// WaferNo - Wafer number in the cassette associated with the RDS number of the request.
|
|
//
|
|
// Output:
|
|
// Response - Wafer map image associated with RDSNo and WaferNo in PDF format.
|
|
//
|
|
// Posts a wafer map image request to the WAFER_IMAGE_REQUESTS database table, which is then
|
|
// processed on the EC server by the Process Wafer Image Requests service.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service PostWaferImageRequest(RDSNo, WaferNo, TencorRecipe)
|
|
|
|
If ( (RDSNo NE '') and (WaferNo NE '') and (TencorRecipe NE '') ) then
|
|
ResponseBody = ''
|
|
RequestDate = Date()
|
|
RequestTime = Time()
|
|
RequestKeyID = RequestDate : '*' : RequestTime
|
|
RequestRow = ''
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RDS_NO$> = RDSNo
|
|
RequestRow<WAFER_IMAGE_REQUESTS.WAFER_NO$> = WaferNo
|
|
RequestRow<WAFER_IMAGE_REQUESTS.TENCOR_RECIPE$> = TencorRecipe
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RETURN_PDF$> = False$
|
|
Database_Services('WriteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, RequestRow, False$, False$, False$)
|
|
end else
|
|
Error_Services('Add', 'Null parameter passed into service call. All parameters are required.')
|
|
end
|
|
|
|
return
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetWaferImage
|
|
//
|
|
// Input:
|
|
// Username - Username of user whom is posting the request.
|
|
// RDSNo - RDS number associated with the desired wafer image.
|
|
// WaferNo - Wafer number in the cassette associated with the RDS number of the request.
|
|
//
|
|
// Output:
|
|
// Response - Wafer map image associated with RDSNo and WaferNo in PDF format.
|
|
//
|
|
// Posts a wafer map image request to the WAFER_IMAGE_REQUESTS database table, which is then
|
|
// processed on the EC server by the Process Wafer Image Requests service.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetWaferImage(RDSNo, WaferNo, TencorRecipe)
|
|
|
|
If (RDSNo NE '') and (WaferNo NE '') and (TencorRecipe NE '') then
|
|
ResponseBody = ''
|
|
RequestDate = Date()
|
|
RequestTime = Time()
|
|
RequestKeyID = RequestDate : '*' : RequestTime
|
|
RequestRow = ''
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RDS_NO$> = RDSNo
|
|
RequestRow<WAFER_IMAGE_REQUESTS.WAFER_NO$> = WaferNo
|
|
RequestRow<WAFER_IMAGE_REQUESTS.TENCOR_RECIPE$> = TencorRecipe
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RETURN_PDF$> = True$
|
|
Database_Services('WriteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, RequestRow, False$, False$, False$)
|
|
If Error_Services('NoError') then
|
|
TimeExpired = False$
|
|
Start = GetTickCount()
|
|
Loop
|
|
RequestRow = Database_Services('ReadDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID)
|
|
ResponseDate = RequestRow<WAFER_IMAGE_REQUESTS.RESPONSE_DATE$>
|
|
If ResponseDate NE '' then
|
|
ResponseBody = RequestRow<WAFER_IMAGE_REQUESTS.RESPONSE_BODY$>
|
|
If ResponseBody EQ '' then Error_Services('Add', 'Wafer image not found.')
|
|
end
|
|
// Time will expire after 30 seconds.
|
|
If GetTickCount() - Start GE 30000 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', 'Timed out attempting to retrieve wafer image.')
|
|
end
|
|
Database_Services('DeleteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, True$)
|
|
end
|
|
|
|
If ResponseBody NE '' then
|
|
ResponseBody = SRP_Decode(ResponseBody)
|
|
end
|
|
Response = ResponseBody
|
|
end else
|
|
Error_Services('Add', 'Null parameter passed into service call. All parameters are required.')
|
|
Response = ''
|
|
end
|
|
|
|
return
|
|
|
|
|
|
Service ProcessWaferImageRequests()
|
|
|
|
hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
|
|
Lock hSysLists, ServiceKeyID then
|
|
hWaferImageRequests = Database_Services('GetTableHandle', 'WAFER_IMAGE_REQUESTS')
|
|
If Error_Services('NoError') then
|
|
Sentence = 'SELECT WAFER_IMAGE_REQUESTS 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
|
|
RequestDate = Field(RequestKeyID, '*', 1)
|
|
RequestTime = Field(RequestKeyID, '*', 2)
|
|
RequestTime = RequestTime / 86400
|
|
RequestTime = RequestTime[3, 5]
|
|
RequestDTM = RequestDate:'.':RequestTime
|
|
CurrDTM = Datetime()
|
|
TimeInQueue = CurrDTM - RequestDTM
|
|
If TimeInQueue LT 1 then
|
|
Lock hWaferImageRequests, RequestKeyID then
|
|
EncodedPDF = ''
|
|
ResponseBody = ''
|
|
RequestRow = Database_Services('ReadDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID)
|
|
RDSNo = RequestRow<WAFER_IMAGE_REQUESTS.RDS_NO$>
|
|
WaferNo = RequestRow<WAFER_IMAGE_REQUESTS.WAFER_NO$>
|
|
TencorRecipe = RequestRow<WAFER_IMAGE_REQUESTS.TENCOR_RECIPE$>
|
|
ReturnPDF = RequestRow<WAFER_IMAGE_REQUESTS.RETURN_PDF$>
|
|
Reactor = Xlate('RDS', RDSNo, 'REACTOR', 'X')
|
|
PSN = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X')
|
|
// Format Wafer Number for SQL Query
|
|
WaferNo = TrimF(WaferNo)
|
|
If WaferNo < 10 then
|
|
WaferNo = '*0' : WaferNo
|
|
end else
|
|
WaferNo = '*' : WaferNo
|
|
end
|
|
Query = "DECLARE @RDS varchar(10) " |
|
|
: "DECLARE @RECIPE varchar(30) " |
|
|
: "DECLARE @WFRID varchar(10) " |
|
|
: "SET @RDS = '":RDSNo:"' " |
|
|
: "SET @RECIPE = '":TencorRecipe:"' " |
|
|
: "SET @WFRID = '":WaferNo:"' " |
|
|
: "SELECT TOP (1) child.AttachmentID, child.insertDate " |
|
|
: "FROM Metrology.dbo.TencorRunHeader header " |
|
|
: "INNER JOIN Metrology.dbo.TencorRunData child on header.id = child.headerid " |
|
|
: "WHERE header.rds = @RDS and header.recipe like @RECIPE + '%' and child.slot = @WFRID " |
|
|
: "ORDER BY header.insertDate DESC, child.slot ASC"
|
|
|
|
WaferMapDB = Environment_Services('GetMetrologyProductionPath')
|
|
SQLResponse = SQL_Services('GetDataRows', 'SPC', Query)
|
|
AttachmentID = SQLResponse<1>
|
|
InsertDTM = SQLResponse<2>
|
|
AttachmentID = SRP_Trim(AttachmentID, 'FB', '{}')
|
|
WaferMapRoot = Environment_Services('GetWaferMapProductionPath')
|
|
InsertDate = InsertDTM[1, 'F ']
|
|
Year = Field(InsertDate, '/', 3, 1)
|
|
WorkWeek = 'WW':Date_Services('GetWeekNum', InsertDate)
|
|
PDFPath = WaferMapRoot:'_\':Year:'\':WorkWeek:'\':AttachmentID:'\image.pdf'
|
|
Set_Status(0)
|
|
PDFFile = ''
|
|
OSRead PDFFile from PDFPath then
|
|
// "Cache" PDF on app server
|
|
QA_Services('PostToWaferImageQueue', RDSNo, WaferNo, TencorRecipe, PDFFile)
|
|
If ReturnPDF EQ True$ then
|
|
ResponseBody = SRP_Encode(PDFFile)
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RESPONSE_BODY$> = ResponseBody
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RESPONSE_DATE$> = Date()
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RESPONSE_TIME$> = Time()
|
|
Database_Services('WriteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, RequestRow, True$)
|
|
end else
|
|
Database_Services('DeleteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, True$)
|
|
end
|
|
end else
|
|
// Log failure to find wafer image in the new wafer map path only if the request is over
|
|
// 30 minutes old.
|
|
TimeElapsed = CurrDTM - RequestDTM
|
|
// (30 minutes * (60 seconds / 1 minute) )
|
|
HalfHourInSecs = 30 * 60
|
|
// Convert seconds to internal datetime format ( (24 hours/day) * (60 minutes/hour) * 60 seconds )
|
|
HalfHourInDTM = HalfHourInSecs / (24 * 60 * 60)
|
|
If TimeElapsed GT HalfHourInDTM then
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = RDSNo
|
|
LogData<3> = PDFPath
|
|
Logging_Services('AppendLog', objWaferMapLog, LogData, @RM, @FM)
|
|
end
|
|
ErrorCode = Status()
|
|
Error_Services('Add', 'Error retrieving wafer map image for AttachmentID ':AttachmentID)
|
|
end
|
|
Unlock hWaferImageRequests, RequestKeyID else Null
|
|
end
|
|
end else
|
|
// Delete request because wafer image may never become available.
|
|
Database_Services('DeleteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, True$)
|
|
end
|
|
Repeat
|
|
end
|
|
Unlock hSysLists, ServiceKeyID else Null
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ProcessWaferImageRequests
|
|
//
|
|
// Service that attempts to process all wafer map image requests. These requests are queued in the
|
|
// WAFER_IMAGE_REQUESTS table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ProcessWaferImageRequestsOld()
|
|
|
|
hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
|
|
Lock hSysLists, ServiceKeyID then
|
|
hWaferImageRequests = Database_Services('GetTableHandle', 'WAFER_IMAGE_REQUESTS')
|
|
If Error_Services('NoError') then
|
|
Sentence = 'SELECT WAFER_IMAGE_REQUESTS 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
|
|
RequestDate = Field(RequestKeyID, '*', 1)
|
|
RequestTime = Field(RequestKeyID, '*', 2)
|
|
RequestTime = RequestTime / 86400
|
|
RequestTime = RequestTime[3, 5]
|
|
RequestDTM = RequestDate:'.':RequestTime
|
|
CurrDTM = Datetime()
|
|
TimeInQueue = CurrDTM - RequestDTM
|
|
If TimeInQueue LT 1 then
|
|
Lock hWaferImageRequests, RequestKeyID then
|
|
EncodedPDF = ''
|
|
ResponseBody = ''
|
|
RequestRow = Database_Services('ReadDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID)
|
|
RDSNo = RequestRow<WAFER_IMAGE_REQUESTS.RDS_NO$>
|
|
WaferNo = RequestRow<WAFER_IMAGE_REQUESTS.WAFER_NO$>
|
|
TencorRecipe = RequestRow<WAFER_IMAGE_REQUESTS.TENCOR_RECIPE$>
|
|
ReturnPDF = RequestRow<WAFER_IMAGE_REQUESTS.RETURN_PDF$>
|
|
Reactor = Xlate('RDS', RDSNo, 'REACTOR', 'X')
|
|
PSN = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X')
|
|
// Format Wafer Number for SQL Query
|
|
WaferNo = TrimF(WaferNo)
|
|
If WaferNo < 10 then
|
|
WaferNo = '*0' : WaferNo
|
|
end else
|
|
WaferNo = '*' : WaferNo
|
|
end
|
|
Query = "DECLARE @RDS varchar(10) " |
|
|
: "DECLARE @RECIPE varchar(30) " |
|
|
: "DECLARE @WFRID varchar(10) " |
|
|
: "SET @RDS = '":RDSNo:"' " |
|
|
: "SET @RECIPE = '":TencorRecipe:"' " |
|
|
: "SET @WFRID = '":WaferNo:"' " |
|
|
: "SELECT child.AttachmentID " |
|
|
: "FROM Metrology.dbo.TencorRunHeader header " |
|
|
: "INNER JOIN Metrology.dbo.TencorRunData child on header.id = child.headerid " |
|
|
: "WHERE header.rds = @RDS and header.recipe like @RECIPE + '%' and child.slot = @WFRID " |
|
|
: "ORDER BY header.insertDate DESC, child.slot ASC"
|
|
|
|
WaferMapDB = Environment_Services('GetMetrologyProductionPath')
|
|
AttachmentID = SQL_Services('GetDataRows', 'SPC', Query)
|
|
AttachmentID = SRP_Trim(AttachmentID, 'FB', '{}')
|
|
WaferMapRoot = Environment_Services('GetWaferMapProductionPath')
|
|
PDFPath = WaferMapRoot:'\':AttachmentID:'\image.pdf'
|
|
Set_Status(0)
|
|
PDFFile = ''
|
|
OSRead PDFFile from PDFPath then
|
|
// "Cache" PDF on app server
|
|
QA_Services('PostToWaferImageQueue', RDSNo, WaferNo, TencorRecipe, PDFFile)
|
|
If ReturnPDF EQ True$ then
|
|
ResponseBody = SRP_Encode(PDFFile)
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RESPONSE_BODY$> = ResponseBody
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RESPONSE_DATE$> = Date()
|
|
RequestRow<WAFER_IMAGE_REQUESTS.RESPONSE_TIME$> = Time()
|
|
Database_Services('WriteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, RequestRow, True$)
|
|
end else
|
|
Database_Services('DeleteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, True$)
|
|
end
|
|
|
|
end else
|
|
ErrorCode = Status()
|
|
Error_Services('Add', 'Error retrieving wafer map image for AttachmentID ':AttachmentID)
|
|
end
|
|
Unlock hWaferImageRequests, RequestKeyID else Null
|
|
end
|
|
end else
|
|
// Delete request because wafer image may never become available.
|
|
Database_Services('DeleteDataRow', 'WAFER_IMAGE_REQUESTS', RequestKeyID, True$)
|
|
end
|
|
Repeat
|
|
end
|
|
Unlock hSysLists, ServiceKeyID else Null
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// PostToWaferImageQueue
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS number associated with the desired wafer image.
|
|
// WaferNo - Wafer number in the cassette associated with the RDS number of the request.
|
|
// PDFData - Raw PDF Data to be written to the App server's local storage.
|
|
//
|
|
// Posts a wafer map image pdf to the WAFER_IMAGE_QUEUE database table, which is then
|
|
// processed on the App server by the Process Wafer Queue service.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service PostToWaferImageQueue(RDSNo, WaferNo, TencorRecipe, PDFData)
|
|
|
|
If (PDFData NE '') and (RDSNo NE '') and (WaferNo NE '') and (TencorRecipe NE '') then
|
|
KeyID = RDSNo : '*' : WaferNo : '*' : TencorRecipe
|
|
DataRow = ''
|
|
PDFData = SRP_Encode(PDFData)
|
|
DataRow<WAFER_IMAGE_QUEUE.PDF_DATA$> = PDFData
|
|
Database_Services('WriteDataRow', 'WAFER_IMAGE_QUEUE', KeyID, DataRow, True$)
|
|
If Error_Services('HasError') then
|
|
Error_Services('Add','Failed to write ':KeyID:' to the WAFER_IMAGE_QUEUE table in the ':Service:' service.')
|
|
end
|
|
end else
|
|
ErrorMsg = 'One or more null variables passed into function. All parameters are required to not be null.'
|
|
Error_Services('Add', ErrorMsg)
|
|
end
|
|
|
|
return
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ProcessWaferImageQueue
|
|
//
|
|
// This service is to be run on the App server within the Service Manager. It's purpose is to save wafer map PDFs
|
|
// locally to the App server. The wafer map PDFs are only available on the EC domain from Sharepoint and as a result
|
|
// they must be transmitted to the App server using OpenInsight. Storing the wafer map PDFs locally allows for a
|
|
// responsive user interface.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ProcessWaferImageQueue()
|
|
|
|
hValidationRequests = Database_Services('GetTableHandle', 'WAFER_IMAGE_QUEUE')
|
|
If Error_Services('NoError') then
|
|
Sentence = "SELECT WAFER_IMAGE_QUEUE WITH PDF_DATA NE ''"
|
|
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', 'WAFER_IMAGE_QUEUE', RequestKeyID)
|
|
If RequestRow NE '' then
|
|
RDSNo = Field(RequestKeyID, '*', 1)
|
|
WaferNo = Field(RequestKeyID, '*', 2)
|
|
TencorRecipe = Field(RequestKeyID, '*', 3)
|
|
WaferPDF = RequestRow<WAFER_IMAGE_QUEUE.PDF_DATA$>
|
|
WaferPDF = SRP_Decode(WaferPDF)
|
|
If WaferPDF NE '' then
|
|
OIAppPath = Environment_Services('GetApplicationRootPath')
|
|
WaferImagePath = OIAppPath:'\Wafer_Images\'
|
|
If TencorRecipe EQ '' then
|
|
FileName = 'WaferMap_RDSNo_':RDSNo:'_WaferNo_':WaferNo:'.pdf'
|
|
end else
|
|
FileName = 'WaferMap_RDSNo_':RDSNo:'_Recipe_':TencorRecipe:'_WaferNo_':WaferNo:'.pdf'
|
|
end
|
|
Status = ''
|
|
Set_Status(0)
|
|
OSWrite WaferPDF to WaferImagePath:FileName
|
|
Status = Get_Status()
|
|
end
|
|
end
|
|
Database_Services('DeleteDataRow', 'WAFER_IMAGE_QUEUE', RequestKeyID, True$)
|
|
Unlock hValidationRequests, RequestKeyID else Null
|
|
end
|
|
Repeat
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ROTRImpactReport
|
|
//
|
|
// Input: None
|
|
//
|
|
// The purpose of this service is to provide management with an on demand report to see how the production floor
|
|
// would be impacted by the tighter process controls introduced when using the current ROTR parameters for each reactor.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ROTRImpactReport()
|
|
|
|
Server = Environment_Services('GetServer')
|
|
If (Server NE 'MESST5201') and (Server NE 'MESST5202') then
|
|
LogPath = Environment_Services('GetReportsRootPath')
|
|
LogDate = Oconv(Date(), 'D4/')
|
|
LogTime = Oconv(Time(), 'MTS')
|
|
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' ROTR Impact Analysis.csv'
|
|
Headers = 'Reactor No,Reactor Load Blocked,ROTR Fail Reason,# of 100% Inspections,Total NCR Count,' |
|
|
: 'Zero NCR Count,UCL Exceeded Count,Run Count,# of Override Sigs,# of Block Override Sigs,' |
|
|
: 'Reactor Yield %'
|
|
Swap ',' with @FM in Headers
|
|
objLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, True$)
|
|
end
|
|
|
|
ReactorList = Database_Services('ReadDataRow', 'SYSLISTS', 'REACTOR_LIST')
|
|
ReactorList = Delete(ReactorList, 1, 0, 0)
|
|
ReactorBlockArray = ''
|
|
ReactorCnt = DCount(ReactorList, @FM)
|
|
Def = ""
|
|
Def<MCAPTION$> = "Building ROTR Impact Analysis Report..."
|
|
Def<MTYPE$> = "GC"
|
|
Def<MEXTENT$> = ReactorCnt
|
|
Def<MTEXTWIDTH$> = 600
|
|
// Start progress meter
|
|
MsgUp = Msg(@WINDOW, Def)
|
|
|
|
For each Reactor in ReactorList setting ReactorIndex
|
|
ReactorType = Xlate('REACTOR', Reactor, 'REACT_TYPE', 'X')
|
|
If (ReactorType EQ 'ASM+' OR ReactorType EQ 'HTR' OR ReactorType EQ 'ASM') then
|
|
ReactorBlockArray<ReactorIndex, 1> = Reactor
|
|
ReactorBlockArray<ReactorIndex, 2> = '' ; // Reactor Load Blocked
|
|
ReactorBlockArray<ReactorIndex, 3> = '' ; // ROTR Failure Reason
|
|
ReactorBlockArray<ReactorIndex, 4> = 0 ; // Num Runs requiring 100% scans
|
|
ReactorBlockArray<ReactorIndex, 5> = 0 ; // NCRCount
|
|
ReactorBlockArray<ReactorIndex, 6> = 0 ; // ZeroNCRCount
|
|
ReactorBlockArray<ReactorIndex, 7> = 0 ; // UCLExceededCount
|
|
ReactorBlockArray<ReactorIndex, 8> = 0 ; // Run Count
|
|
ReactorBlockArray<ReactorIndex, 9> = 0 ; // Num Override Sigs (blocking + non-blocking events)
|
|
ReactorBlockArray<ReactorIndex, 10> = 0 ; // Num Block Override Sigs (e.g. NCR Limit Exceeded)
|
|
ReactorBlockArray<ReactorIndex, 11> = 0 ; // Reactor Yield %
|
|
CurrRDS = Xlate('REACT_STATE', Reactor, 'LAST_RDS_NO', 'X')
|
|
LWICIKey = Xlate('RDS', CurrRDS, 'LWI_CI_NO', 'X')
|
|
CIRec = Database_Services('ReadDataRow', 'CLEAN_INSP', LWICIKey)
|
|
|
|
If Error_Services('NoError') then
|
|
|
|
SigRequired = CIRec<CLEAN_INSP_SPEC_SURFSCAN_REQ$>
|
|
ScanSigs = CIRec<CLEAN_INSP_SCAN_SIG$>
|
|
// Get the specifications
|
|
SpecRecipes = CIRec<CLEAN_INSP_SPEC_SURFSCAN_RECIPE$>
|
|
SpecDefects = CIRec<CLEAN_INSP_SPEC_SURF_DEFECTS$> ; // Spec USL
|
|
SpecHazes = CIRec<CLEAN_INSP_SPEC_SURF_HAZE$>
|
|
// Get the scanned values
|
|
ScanRecipes = CIRec<CLEAN_INSP_SCAN_RECIPE$>
|
|
ScanDefectsAvg = CIRec<CLEAN_INSP_SCAN_SUM_OF_DEF_AVG$>
|
|
ScanDefects = CIRec<CLEAN_INSP_SCAN_SUM_OF_DEF_MAX$>
|
|
ScanHazes = CIRec<CLEAN_INSP_SCAN_HAZE_AVG_AVG$>
|
|
// Get the individual SOD values per wafer.
|
|
SODPerWafer = CIRec<CLEAN_INSP_SCAN_SOD_PER_WAFER$>
|
|
// Get the fail scan flags so that we can get/set them as necessary.
|
|
FailScanFlags = CIRec<CLEAN_INSP_FAIL_SCAN_REQ$>
|
|
ScanMismatch = CIRec<CLEAN_INSP_SCAN_RECIPE_MISMATCH$>
|
|
NumFailedWfrs = CIRec<CLEAN_INSP_NUM_FAILED_WAFERS$>
|
|
|
|
NumScanRecipes = DCount(ScanRecipes, @VM)
|
|
ROTRAction = '' ; // Assume it passes until otherwise proven.
|
|
ROTRActionReason = ''
|
|
CIReactor = Reactor
|
|
// Check if a mismatching recipe name has been set.
|
|
If (ScanMismatch EQ '') then
|
|
// ROTR Reactor/PSN Status Health Check - Uses parameters set within the ROTR Parameters form.
|
|
// We only need to run this once per CLEAN_INSP record.
|
|
RDSKey = CurrRDS
|
|
ROTRStatus = 'P' ; // Assume pass until proven otherwise.
|
|
ROTRResponse = QA_Services('GetROTRStatus', RDSKey)
|
|
ROTRStatus = ROTRResponse<1>
|
|
ROTRFailReason = ROTRResponse<2>
|
|
NCRCount = ROTRResponse<3>
|
|
ZeroNCRCount = ROTRResponse<4>
|
|
UCLExceededCount = ROTRResponse<5>
|
|
RunCount = ROTRResponse<6>
|
|
NumOverrideSigs = ROTRResponse<7>
|
|
NumBlockOverrideSigs = ROTRResponse<8>
|
|
ReactorYield = ROTRResponse<9>
|
|
ReactorBlockArray<ReactorIndex, 5> = NCRCount
|
|
ReactorBlockArray<ReactorIndex, 6> = ZeroNCRCount
|
|
ReactorBlockArray<ReactorIndex, 7> = UCLExceededCount
|
|
ReactorBlockArray<ReactorIndex, 8> = RunCount
|
|
ReactorBlockArray<ReactorIndex, 9> = NumOverrideSigs
|
|
ReactorBlockArray<ReactorIndex, 10> = NumBlockOverrideSigs
|
|
ReactorBlockArray<ReactorIndex, 11> = ReactorYield
|
|
|
|
// Check each spec recipe. There must be at least one scan recorded for each spec recipe.
|
|
For Each SpecRecipeName in SpecRecipes using @VM setting SpecRecipeIndex
|
|
// Look for the last scan entry related to the current spec recipe.
|
|
RecipeFound = False$
|
|
For ScanRecipeIndex = NumScanRecipes To 1 Step -1
|
|
ScanRecipeName = ScanRecipes<0, ScanRecipeIndex>
|
|
RecipeFound = (ScanRecipeName EQ SpecRecipeName)
|
|
Until RecipeFound
|
|
Next ScanRecipeIndex
|
|
// If a scan was found, then analyze the scan data for spec requirements. If not found, this
|
|
// automatically fails.
|
|
If RecipeFound EQ True$ then
|
|
SpecDefect = Oconv(SpecDefects<0, SpecRecipeIndex>, 'MD0')
|
|
SpecHaze = Oconv(SpecHazes<0, SpecRecipeIndex>, 'MD2')
|
|
ScanDefect = Oconv(ScanDefects<0, ScanRecipeIndex>, 'MD0')
|
|
ScanHaze = Oconv(ScanHazes<0, ScanRecipeIndex>, 'MD3')
|
|
ScanSig = ScanSigs<0, ScanRecipeIndex>
|
|
ScanFailedWfrs = NumFailedWfrs<0, ScanRecipeIndex>
|
|
FailScanReq = FailScanFlags<0, ScanRecipeIndex>
|
|
FailureScan = False$
|
|
ScanSODPerWafer = SODPerWafer<0, ScanRecipeIndex>
|
|
WaferCount = 0
|
|
NumFailedWafers = 0
|
|
For each WaferSOD in ScanSODPerWafer using @SVM setting WaferIndex
|
|
If WaferSOD NE '' then
|
|
// We have SOD data for this wafer so check if it is < SOD Max
|
|
If WaferSOD LE SpecDefect then
|
|
WaferCount += 1
|
|
end else
|
|
NumFailedWafers += 1
|
|
end
|
|
end
|
|
Next WaferSOD
|
|
ReactorBlockArray<ReactorIndex, 4> = NumFailedWafers
|
|
// We need to check if reactor recently underwent ROTR maintenance. If so, then
|
|
// we must also check if this RDS is one of the first two runs after maintenance.
|
|
// If this is the case, then the RDS must undergo a 100% scan.
|
|
MaintScanReq = QA_Services('GetMaintenanceScanStatus', RDSKey)
|
|
FailScanReq = (FailScanReq or MaintScanReq)
|
|
// Search for '100' in the recipe name to determine if this is a failure scan.
|
|
FailureScan = Index(ScanRecipeName, '100', 1)
|
|
If FailureScan > 0 then FailureScan = True$
|
|
If (FailScanReq EQ True$) and (FailureScan EQ True$) then
|
|
// Fail scan was required and submitted, so turn off the flag.
|
|
FailScanReq = False$
|
|
FailScanFlags<0, ScanRecipeIndex> = False$
|
|
end
|
|
Begin Case
|
|
Case (ROTRStatus EQ 'F')
|
|
// Check this case first as to not miss blocking the reactor if necessary.
|
|
// Block load signature on this reactor for future lots until overriden at Load button
|
|
// signature click event by supervisor, lead, or engineer.
|
|
ReactorBlockArray<ReactorIndex, 2> = 'Yes'
|
|
ReactorBlockArray<ReactorIndex, 3> = ROTRFailReason
|
|
Case (ScanDefect GT SpecDefect)
|
|
ROTRFailLimit = Xlate('REACTOR', CIReactor, 'ROTR_FAIL_LIMIT', 'X')
|
|
// If a Post Clean is required, then disregard the ROTRFailLimit per Tom Tillery.
|
|
CleanReq = False$
|
|
PSNo = Xlate('CLEAN_INSP', LWICIKey, 'PS_NO', 'X')
|
|
PRSStages = Xlate('PROD_SPEC', PSNo, 'PRS_STAGE_KEY', 'X')
|
|
If Index(PRSStages, 'POST', 1) then
|
|
CleanReq = Xlate('PRS_STAGE', PSNo:'*POST', 'CLEAN_SIG_REQ', 'X')
|
|
end
|
|
If (NumFailedWafers LT ROTRFailLimit) or (CleanReq EQ True$) or (FailureScan EQ True$) then
|
|
Null
|
|
end else
|
|
FailScanReq = True$
|
|
FailScanFlags<0, ScanRecipeIndex> = FailScanReq
|
|
FailScanCnt = ReactorBlockArray<ReactorIndex, 4>
|
|
ReactorBlockArray<ReactorIndex, 4> = FailScanCnt + 1
|
|
end
|
|
Case (FailScanReq EQ True$) and (FailureScan EQ False$)
|
|
If MaintScanReq EQ True$ then
|
|
Null
|
|
end else
|
|
// If ROTR maintenance flag not set, then wafer fail limit must have been met in
|
|
// an earlier Tencor run. We must set it here again due to the "assume pass until
|
|
// fail design of this MFS."
|
|
FailScanReq = True$
|
|
FailScanFlags<0, ScanRecipeIndex> = FailScanReq
|
|
FailScanCnt = ReactorBlockArray<ReactorIndex, 4>
|
|
ReactorBlockArray<ReactorIndex, 4> = FailScanCnt + 1
|
|
end
|
|
Case Otherwise$
|
|
// This scan data is within spec, check next recipe.
|
|
End Case
|
|
end
|
|
Next SpecRecipeName
|
|
end
|
|
end
|
|
end else
|
|
// ROTR not currently implemented for EpiPro reactors.
|
|
ReactorBlockArray<ReactorIndex, 1> = Reactor
|
|
ReactorBlockArray<ReactorIndex, 2> = 'N/A' ; // ROTR Status
|
|
end
|
|
|
|
If (Server NE 'MESST5201') and (Server NE 'MESST5202') then
|
|
LogData = ''
|
|
LogData<1> = ReactorBlockArray<ReactorIndex, 1> ; // Reactor No
|
|
LogData<2> = ReactorBlockArray<ReactorIndex, 2> ; // Reactor Load Blocked
|
|
LogData<3> = ReactorBlockArray<ReactorIndex, 3> ; // ROTR Failure Reason
|
|
LogData<4> = ReactorBlockArray<ReactorIndex, 4> ; // Num Runs requiring 100% scans
|
|
LogData<5> = ReactorBlockArray<ReactorIndex, 5> ; // NCRCount
|
|
LogData<6> = ReactorBlockArray<ReactorIndex, 6> ; // ZeroNCRCount
|
|
LogData<7> = ReactorBlockArray<ReactorIndex, 7> ; // UCLExceededCount
|
|
LogData<8> = ReactorBlockArray<ReactorIndex, 8> ; // Run Count
|
|
LogData<9> = ReactorBlockArray<ReactorIndex, 9> ; // Num Override Sigs
|
|
LogData<10> = ReactorBlockArray<ReactorIndex, 10> ; // Num Blocking Override Sigs
|
|
ReactorYield = ReactorBlockArray<ReactorIndex, 11> ; // Reactor Yield
|
|
If ReactorYield NE '' then
|
|
LogData<11> = (ReactorYield * 100) : '%'
|
|
end else
|
|
LogData<11> = ReactorYield
|
|
end
|
|
Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
|
|
end
|
|
|
|
// Update progress meter, check for cancel input.
|
|
While Msg(@WINDOW, MsgUp, ReactorIndex, MSGINSTUPDATE$)
|
|
Next Reactor
|
|
|
|
If (Server NE 'MESST5201') and (Server NE 'MESST5202') then
|
|
ReportMsg = ''
|
|
ReportMsg<MTEXT$> = 'ROTR Impact Analysis.csv saved to your local OIReports folder.'
|
|
Msg(@Window, ReportMsg)
|
|
end
|
|
|
|
// Take down progress meter message.
|
|
Msg(@WINDOW, MsgUp)
|
|
|
|
If (Server NE 'MESSA005') and (Server NE 'MESSA01EC') then
|
|
// Try to create Excel spreadsheet for the user
|
|
eXcelCols = 'A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,EE,FF,GG,HH,II,JJ,KK,LL'
|
|
Swap "','" with ',' in eXcelCols
|
|
Convert ',' to @VM in eXcelCols
|
|
xlApp = OleCreateInstance("excel.Application") ;* Create instance of Excel
|
|
If Not(OleStatus()) then
|
|
OlePutProperty(XlApp, 'Visible', xlSheetVisible)
|
|
xlWorkBooks =OleGetProperty(xlApp, "Workbooks") ;* Create workbook
|
|
xlWkb = OleCallMethod(xlWorkbooks, "Add")
|
|
xlSht = OleGetProperty(xlWkb, "Worksheets", 1) ;* Select worksheet 1
|
|
|
|
DataArray = ReactorBlockArray
|
|
FirstRow = DataArray<1>
|
|
ColCnt = DCount(FirstRow, @VM)
|
|
Header = 'Reactor No,Reactor Load Blocked,ROTR Fail Reason,# of 100% Inspections,Total NCR Count,' |
|
|
: 'Zero NCR Count,UCL Exceeded Count,Run Count,# of Override Sigs,# of Block Override Sigs,' |
|
|
: 'Reactor Yield %'
|
|
Swap ',' with @FM in Header
|
|
DataArray = Insert(DataArray, 0, 0, 0, '')
|
|
LineCnt = DCount(DataArray, @FM)
|
|
For Col = 1 to ColCnt
|
|
DataArray = Insert(DataArray, 1, Col, 0, Header<Col>)
|
|
Next Col
|
|
FOR LineNo = 1 to LineCnt
|
|
FOR Column = 1 to ColCnt
|
|
RangeColumn = eXcelCols<1,Column>
|
|
range = OleGetProperty( xlSht, 'Range',RangeColumn:LineNo)
|
|
OlePutProperty(range, 'Value', DataArray<LineNo, Column>) ;* Load report array into spreadsheet cells
|
|
OlePutProperty(range, 'HorizontalAlignment', -4108) ;* Set cell alignment to centered
|
|
NEXT Column
|
|
NEXT LineCnt
|
|
|
|
column = OleGetProperty( xlSht, 'Range' , 'A:A' )
|
|
OlePutProperty( column , 'ColumnWidth' , '10' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'B:B' )
|
|
OlePutProperty( column , 'ColumnWidth' , '20' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'C:C' )
|
|
OlePutProperty( column , 'ColumnWidth' , '35' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'D:D' )
|
|
OlePutProperty( column , 'ColumnWidth' , '22' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'E:E' )
|
|
OlePutProperty( column , 'ColumnWidth' , '15' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'F:F' )
|
|
OlePutProperty( column , 'ColumnWidth' , '15' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'G:G' )
|
|
OlePutProperty( column , 'ColumnWidth' , '20' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'H:H' )
|
|
OlePutProperty( column , 'ColumnWidth' , '10' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'I:I' )
|
|
OlePutProperty( column , 'ColumnWidth' , '20' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'J:J' )
|
|
OlePutProperty( column , 'ColumnWidth' , '25' )
|
|
column = OleGetProperty( xlSht, 'Range' , 'K:K' )
|
|
OlePutProperty( column , 'ColumnWidth' , '15' )
|
|
OlePutProperty( column , 'NumberFormat' , '0.0%' )
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// CleanWaferImageRepo
|
|
//
|
|
// Input: NumDays - The number of days which determines whether or not a file will be deleted.
|
|
//
|
|
// Removes wafer image files from the wafer images folder that are more than the desired number of days old.
|
|
// Note that users can still access wafer images that are deleted, however OI will have retrieve them from Sharepoint
|
|
// again. Wafer images are automatically pulled from Sharepoint and stored locally for quick access from OI.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service CleanWaferImageRepo(NumDays)
|
|
|
|
RepoPath = Environment_Services('GetApplicationRootPath') : '\Wafer_Images\'
|
|
InitDir RepoPath:'*.pdf'
|
|
FileList = DirList()
|
|
Today = Date()
|
|
For each Filename in FileList
|
|
FileInfo = Dir(RepoPath:Filename)
|
|
LastWriteDate = FileInfo<2>
|
|
FileAge = Today - LastWriteDate
|
|
If FileAge GT NumDays then
|
|
OSDelete RepoPath:Filename
|
|
end
|
|
Next Filename
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// PreEpiSignReady
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS to be signed
|
|
// Username - LSL Username of the operator signing the stage.
|
|
// WaferQty - User supplied wafer quantity to verify against the schedule.
|
|
// Reactor - Reactor to assign to the RDS record.
|
|
//
|
|
// Output:
|
|
// Reponse - True$ if PRE stage is ready to be signed (all preconditions met and no errors encountered),
|
|
// False$ otherwise.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service PreEpiSignatureReady(RDSNo, Username, WaferQty, Reactor)
|
|
|
|
If (RDSNo EQ '') or (Username EQ '') or (WaferQty EQ '') or (Reactor EQ '') then
|
|
ErrorMessage = 'RDSNo, Username, WaferQty, or Reactor not supplied in the ':Service:' service.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
If Error_Services('HasError') then
|
|
ErrorMessage = 'Error reading RDS record: ':RDSNo:' in the ':Service:' service.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
WONo = RDSRec<RDS_WO$>
|
|
CassNo = RDSRec<RDS_CASS_NO$>
|
|
WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X')
|
|
PreEpiSig = RDSRec<RDS_PRE_EPI_SIG$>
|
|
ReactorType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X')
|
|
SchedQty = RDSRec<RDS_CASS_WAFER_QTY$>
|
|
|
|
If WaferQty NE SchedQty then
|
|
ErrorMessage = 'The verified wafer quantity does not match the scheduled quantity.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
If PreEpiSig NE '' THEN
|
|
ErrorMessage = 'Process Error: Pre Epi stage has already been signed.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
WOMatCurrStatus = obj_WO_Mat('CurrStatus',WONo:'*':CassNo)
|
|
IF WOMatCurrStatus EQ 'HOLD' THEN
|
|
ErrorMessage = 'Process Error: Cassette is on Hold and may not be signed off.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
IF ReactorType NE 'EPP' THEN
|
|
WOMatLocs = XLATE('WO_MAT', WONo:'*':CassNo, WO_MAT_INV_LOCATION$, 'X')
|
|
IF NOT(INDEX(WOMatLocs,'PTI',1)) THEN
|
|
ErrorMessage = 'Process Error: Cassette has not been scanned through the Passthrough!'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
END
|
|
END
|
|
|
|
PREInstAckReq = Xlate('RDS', RDSNo, 'PRE_INST_ACK_REQ', 'X')
|
|
IF PREInstAckReq EQ True$ THEN
|
|
ErrorMessage = 'The pre epi stage engineering instructions must be acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
END
|
|
|
|
Query = 'SELECT REACT_STATE WITH SCHED_WO CONTAINING ':WONo
|
|
ClearSelect
|
|
Set_Status(0)
|
|
RList(Query, TARGET_ACTIVELIST$, '', '' '')
|
|
SchedReactors = ''
|
|
EOF = False$
|
|
Loop
|
|
ReadNext SchedReactor else EOF = True$
|
|
Until EOF
|
|
SchedReactors<0, -1> = SchedReactor
|
|
Repeat
|
|
|
|
Locate Reactor in SchedReactors using @VM setting vPos else
|
|
Swap @VM with ',' in SchedReactors
|
|
ErrorMessage = 'Assigned reactor, ':Reactor:', does not equal any scheduled reactor(s), ':SchedReactors:'.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
// Add check for supplement signatures
|
|
UnacknowledgedSupp = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'VER')
|
|
If UnacknowledgedSupp NE FALSE$ then
|
|
ErrorMessage = 'The pre epi stage supplements must be acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
Response = True$
|
|
end else
|
|
Response = False$
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SignPreEpiStage
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS to be signed
|
|
// Username - LSL Username of the operator signing the stage.
|
|
// WaferQty - User supplied wafer quantity to verify against the schedule.
|
|
// Reactor - Reactor to assign to the RDS record.
|
|
//
|
|
// Output:
|
|
// Reponse - True$ if PRE stage was successfully signed, False$ otherwise.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SignPreEpiStage(RDSNo, Username, WaferQty, Reactor, ScanEntry)
|
|
|
|
Error_Services('Clear')
|
|
If (RDSNo EQ '') or (Username EQ '') or (WaferQty EQ '') or (Reactor EQ '') then
|
|
ErrorMessage = 'RDSNo, Username, or WaferQty not supplied in the ':Service:' service.'
|
|
Error_Services('Add', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
If Error_Services('HasError') then
|
|
ErrorMsg = 'Error reading RDS record: ':RDSNo:' in the ':Service:' service.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
// Code copied from RDS_PRE_EPI write script event handler
|
|
|
|
OrgReactNo = XLATE('RDS', RDSNo, RDS_REACTOR$, 'X')
|
|
|
|
WONo = RDSRec<RDS_WO$>
|
|
CassNo = RDSRec<RDS_CASS_NO$>
|
|
PSNo = RDSRec<RDS_PROD_SPEC_ID$>
|
|
WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X')
|
|
|
|
BEGIN CASE
|
|
CASE OrgReactNo = '' AND Reactor NE ''
|
|
obj_WO_React('AddRdsNo',WONo:@RM:WOStep:@RM:Reactor:@RM:RDSNo:@RM:CassNo:@RM:PSNo)
|
|
|
|
CASE OrgReactNo NE '' AND Reactor = ''
|
|
obj_WO_React('RemRdsNo',WONo:@RM:WOStep:@RM:OrgReactNo:@RM:RDSNo:@RM:CassNo:@RM:PSNo)
|
|
|
|
CASE OrgReactNo NE Reactor
|
|
obj_WO_React('RemRdsNo',WONo:@RM:WOStep:@RM:OrgReactNo:@RM:RDSNo:@RM:CassNo:@RM:PSNo)
|
|
obj_WO_React('AddRdsNo',WONo:@RM:WOStep:@RM:Reactor:@RM:RDSNo:@RM:CassNo:@RM:PSNo)
|
|
|
|
CASE Otherwise$
|
|
NULL
|
|
|
|
END CASE
|
|
|
|
// Need to update/save RDS record before calling RDS_React_Run
|
|
RDSRec<RDS_REACTOR$> = Reactor
|
|
RDSRec<RDS_VERIFY_QTY$> = WaferQty
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
|
|
|
|
RDS_React_Run(RDSNo) ;* Conversion of data to REACT_RUN and REACT_RUN_CI & Adds Sig to WO_MAT 3/18/2008 JCH
|
|
|
|
PreEpiSig = RDSRec<RDS_PRE_EPI_SIG$>
|
|
ReactorType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X')
|
|
// Removing OI_SUPERUSER wrappers to alleviate signature issues. 11/18/19 djs
|
|
* IF MemberOf(Username, 'OI_SUPERUSER') THEN
|
|
|
|
IF ReactorType NE 'EPP' THEN
|
|
|
|
WOMatKey = WONo:'*':CassNo
|
|
WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
|
|
WOMatSigProfile = WOMatRec<WO_MAT_SIG_PROFILE$>
|
|
SigAction = WOStep:'VER'
|
|
|
|
LOCATE SigAction IN WOMatSigProfile USING @VM SETTING Pos THEN
|
|
WOMatSig = WOMatRec<WO_MAT_SIGNATURE$,Pos>
|
|
CurrDTM = OCONV(WOMatRec<WO_MAT_SIG_DTM$,Pos>,'DT/4^HS')
|
|
END ELSE
|
|
WOMatSig = ''
|
|
END
|
|
|
|
RDSSig = PreEpiSig
|
|
|
|
BEGIN CASE
|
|
CASE (WOMatSig EQ '') AND (RDSSig NE '')
|
|
|
|
* Signature missing on WO_MAT
|
|
SigDate = RDSRec<RDS_PRE_EPI_SIG_DATE$>
|
|
SigTime = RDSRec<RDS_PRE_EPI_SIG_TIME$>
|
|
owmParms = WONo:@RM:CassNo:@RM:WOStep:@RM:'VER':@RM:RDSSig:@RM:SigDate:' ':SigTime
|
|
* Sets VER signature
|
|
* obj_WO_Mat('SetSignature',owmParms)
|
|
errCode = ''
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Process Error: Error calling Obj_WO_Mat("SetSignature") within ':Service:'. Error code: ':errCode
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
END else
|
|
Response = True$
|
|
end
|
|
return
|
|
|
|
CASE (RDSSig = '') AND (WOMatSig NE '')
|
|
|
|
* Signature missing on RDS
|
|
CurrDate = CurrDTM[1,' ']
|
|
CurrTime = CurrDTM[COL2()+1,' ']
|
|
RDSRec<RDS_PRE_EPI_SIG$> = WOMatSig
|
|
RDSRec<RDS_PRE_EPI_SIG_DATE$> = IConv(CurrDate, 'D')
|
|
RDSRec<RDS_PRE_EPI_SIG_TIME$> = IConv(CurrTime, 'MT')
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
|
|
Response = True$
|
|
return
|
|
|
|
CASE RDSSig NE '' AND WOMatSig NE ''
|
|
|
|
ErrorMessage = 'Process Error: Pre Epi stage has already been signed.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
|
|
CASE Otherwise$
|
|
Null
|
|
|
|
END CASE
|
|
|
|
END ;* End of check for Reactor Type
|
|
|
|
* END
|
|
|
|
SigDate = OCONV( Date(), 'D2/' )
|
|
SigTime = OCONV( Time(), 'MTS' )
|
|
|
|
IF ReactorType NE 'EPP' THEN
|
|
|
|
ReactID = 'R':Reactor
|
|
ReactWH = XLATE('REACTOR', Reactor, 'TOOL_WH', 'X')
|
|
ReactLoc = XLATE('REACTOR', Reactor, 'TOOL_LOC', 'X')
|
|
Tag = RDSNo
|
|
|
|
owmParms = WONo:@RM:CassNo:@RM:WOStep:@RM:'VER':@RM:Username:@RM:SigDate:' ':SigTime ;* Sets VER signature
|
|
|
|
* obj_WO_Mat('SetSignature',owmParms)
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMessage = 'Process Error: Error calling obj_WO_Mat("SetSignature"). Error code: ':errCode
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
Return
|
|
END
|
|
|
|
LogFile = 'WO_MAT' ; WOMLParms = LogFile:@RM
|
|
LogDTM = SigDate:' ':SigTime ; WOMLParms := LogDTM:@RM
|
|
Action = WOStep:'VER' ; WOMLParms := Action:@RM
|
|
WhCd = ReactWH ; WOMLParms := WhCd:@RM
|
|
LocCd = ReactLoc ; WOMLParms := LocCd:@RM
|
|
WONos = WONo ; WOMLParms := WONos:@RM
|
|
CassNos = CassNo ; WOMLParms := CassNos:@RM
|
|
UserID = Username ; WOMLParms := UserID:@RM
|
|
Tags = '' ; WOMLParms := Tags:@RM
|
|
ToolID = ReactID ; WOMLParms := ToolID:@RM
|
|
WOMLParms := ScanEntry
|
|
|
|
obj_WO_Mat_Log('Create',WOMLParms) ;* Add 'VER' event WO_MAT Event Log
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMessage = 'Process Error: Error calling obj_WO_Mat_Log("Create"). Error code: ':errCode
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
Return
|
|
END
|
|
|
|
END
|
|
|
|
/* Sync up the RDS record with WO_MAT */
|
|
RDSRec<RDS_PRE_EPI_SIG$> = Username
|
|
RDSRec<RDS_PRE_EPI_SIG_DATE$> = IConv(SigDate, 'D')
|
|
RDSRec<RDS_PRE_EPI_SIG_TIME$> = IConv(SigTime, 'MT')
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
|
|
RunOrderNo = Xlate('RDS', RDSNo, 'RUN_ORDER_NUM', 'X')
|
|
PSNo = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X')
|
|
|
|
// The first run on a reactor requires that RDS Layer parameters be entered manually.
|
|
If RunOrderNo GT 1 then
|
|
// Wafer is eligible to have recipe parameters automatically applied from the previous cassette.
|
|
// Check to see if parameters have already been populated. This might be because the user manually entered them.
|
|
// If so, do not automatically copy parameters from the previous cassette.
|
|
LSParmsComp = Xlate('RDS', RDSNo, 'LS_PARMS_COMP', 'X')
|
|
FirstParmsComp = LSParmsComp<1,1>
|
|
If (FirstParmsComp EQ False$) then RDS_Services('CopyRDSLayerParameters', RDSNo)
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
Response = True$
|
|
end else
|
|
Response = False$
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// LoadSignatureReady
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS to be signed
|
|
// Username - LSL Username of the operator signing the stage.
|
|
// WaferQty - User supplied wafer quantity to verify against the schedule.
|
|
// LLSide - Load lock side to be saved within the RDS record.
|
|
// PreFlag - Optional parameter. Skip pre-stage check(Used in Scanner)
|
|
// ReactNoOpt - Used in conjunction with above Pre-Flag. If skipping pre-stage check ReactorNo not saved to RDS record so need to pass it through
|
|
//
|
|
// Output:
|
|
// Response - True$ if LOAD stage is ready to be signed (all preconditions met and no errors encountered),
|
|
// False$ otherwise.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service LoadSignatureReady(RDSNo, Username, WaferQty, LLSide, PreFlag, ReactNoOpt)
|
|
|
|
Error_Services('Clear')
|
|
If (RDSNo EQ '') or (Username EQ '') or (WaferQty EQ '') then
|
|
ErrorMessage = 'Process Error: RDSNo, Username, or WaferQty not supplied in the ':Service:' service.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
If Error_Services('HasError') then
|
|
ErrorMsg = 'Error reading RDS record: ':RDSNo:' in the ':Service:' service.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
WONo = RDSRec<RDS_WO$>
|
|
CassNo = RDSRec<RDS_CASS_NO$>
|
|
Reactor = RDSRec<RDS_REACTOR$>
|
|
If Reactor EQ '' AND Assigned(ReactNoOpt) AND PreFlag EQ True$ then
|
|
Reactor = ReactNoOpt
|
|
end
|
|
PSN = RDSRec<RDS_PROD_SPEC_ID$>
|
|
LoadSig = RDSRec<RDS_OPERATOR_IN$>
|
|
WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X')
|
|
ReactorType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X')
|
|
LoadLockVal = LLSide
|
|
TWAllSigned = Xlate('RDS', RDSNo, 'TW_ALL_SIGNED', 'X')
|
|
RunOrderNo = Xlate('RDS', RDSNo, 'RUN_ORDER_NUM', 'X')
|
|
SchedQty = RDSRec<RDS_CASS_WAFER_QTY$>
|
|
ParamOutOfSpec = Xlate('RDS', RDSNo, 'PARAM_OUT_OF_SPEC', 'X')
|
|
Supplement = RDSRec<RDS_SUPPLEMENT$>
|
|
|
|
ReactorCapacity = Reactor_Services('GetReactorAvailChamberCount', Reactor)
|
|
If ReactorCapacity LE 0 then
|
|
|
|
ErrorMessage = 'Process Error: Reactor ' : Reactor : ' does not have the capacity for this lot.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
If WaferQty NE SchedQty then
|
|
ErrorMessage = 'Process Error: Verified wafer quantity does not match the scheduled quantity.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
// The first run on a reactor requires that RDS Layer parameters be entered manually. This is when Prove-in
|
|
// occurs.
|
|
If RunOrderNo GT 1 then
|
|
// Wafer is eligible to have recipe parameters automatically applied from the previous cassette.
|
|
// Check to see if parameters have already been populated. This might be because the user manually entered them.
|
|
// If so, do not automatically copy parameters from the previous cassette.
|
|
LSParmsComp = Xlate('RDS', RDSNo, 'LS_PARMS_COMP', 'X')
|
|
FirstParmsComp = LSParmsComp<1,1>
|
|
IF (FirstParmsComp EQ False$) THEN
|
|
RDS_Services('CopyRDSLayerParameters', RDSNo)
|
|
end
|
|
// If this lot falls upon a test wafer frequency, then we need to prompt the user to confirm
|
|
// the parameters. If they do not confirm them, then we need to stop the load signature.
|
|
RDSLayerAckReq = Xlate('RDS', RDSNo, 'RDS_LAYER_ACK_REQ', 'X')
|
|
If RDSLayerAckReq EQ True$ then
|
|
ErrorMessage = 'RDS layer parameters must be reviewed for accuracy and acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
end
|
|
|
|
// Ensure recipe parameters (RDS Layer parameters) have been manually entered or automatically applied.
|
|
LSParmsComp = Xlate('RDS', RDSNo, 'LS_PARMS_COMP', 'X')
|
|
Buf1 = DCount(LSParmsComp, @VM)
|
|
Buf2 = Sum(LSParmsComp)
|
|
If DCount(LSParmsComp, @VM) NE Sum(LSParmsComp) then
|
|
ErrorMsg = 'Process Error: Recipe parameters for layer(s) '
|
|
RDSLSKeys = Xlate('RDS', RDSNo, 'RDS_LS_SORTED', 'X')
|
|
For each LSParmFlag in LSParmsComp using @VM setting vPos
|
|
If Not(LSParmFlag) then
|
|
LayerNo = RDSLSKeys<0, vPos>
|
|
LayerNo = LayerNo[-1, 'B*']
|
|
ErrorMsg := LayerNo : ','
|
|
end
|
|
Next LSParmsComp
|
|
ErrorMsg[-1, 1] = ''
|
|
ErrorMsg := ' have not been entered.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
//Added 8/18/2021 JRO - checks to make sure recipe limits aren't oos
|
|
* IF ParamOutOfSpec then
|
|
* IF Supplement NE True$ then
|
|
* ErrorMsg = 'Process Error: Recipe parameters are out of spec and no supplement has been set.'
|
|
* Error_Services('Set', ErrorMsg)
|
|
* Response = False$
|
|
* return
|
|
* end
|
|
* end
|
|
Locate True$ in ParamOutOfSpec using @VM setting oPos then
|
|
IF Supplement NE True$ then
|
|
ErrorMsg = 'Process Error: Recipe parameters are out of spec and no supplement has been set.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
end
|
|
IF TWAllSigned EQ False$ THEN
|
|
ErrorMsg = 'Process Error: All Test Wafers have not been signed off.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
* IF (ReactorType = 'ASM+' OR ReactorType = 'HTR') AND (LoadLockVal EQ '') THEN
|
|
* ErrorMsg = 'Process Error: Load Lock Side must be set to either Left or Right before signing.'
|
|
* Error_Services('Set', ErrorMsg)
|
|
* Response = False$
|
|
* return
|
|
* END
|
|
|
|
//Added JRO 3/16/2021
|
|
//Load Lock Required Check
|
|
LoadLockReq = Xlate('REACTOR', Reactor, REACTOR_PICK_PLACE$, 'X');
|
|
IF LoadLockReq AND LoadLockVal EQ '' then
|
|
ErrorMsg = 'Process Error: Load Lock Side must be set to either Left or Right before signing.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
END
|
|
|
|
//Check if Selected LoadLock is down - New JRO 3/16/21
|
|
If LoadLockReq AND LoadLockVal NE '' then
|
|
DisabledLL = Xlate('REACTOR', Reactor, REACTOR_ACTIVE_LL_DISABLED$, 'X')
|
|
IF DisabledLL NE '' then
|
|
ReactLLRec = Database_Services('ReadDataRow', 'REACT_LL', DisabledLL)
|
|
SideDisabled = ReactLLRec<REACT_LL_DISABLED$>
|
|
QualMode = ReactLLRec<REACT_LL_QUAL_MODE$>
|
|
If LoadLockVal EQ SideDisabled AND (QualMode EQ '' OR QualMode EQ FALSE$) then
|
|
ErrorMsg = 'Process Error: Selected Load Lock (':SideDisabled:') is currently down.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
If QualMode then
|
|
ReactLLRec<REACT_LL_QUAL_MODE$> = FALSE$
|
|
Database_Services('WriteDataRow','REACT_LL',DisabledLL, ReactLLRec)
|
|
end
|
|
end
|
|
end
|
|
|
|
**********************
|
|
* Verify ROTR Status *
|
|
**********************
|
|
ROTREnabled = Xlate('REACTOR', Reactor, 'ENABLE_ROTR', 'X')
|
|
If (ReactorType = 'ASM+' OR ReactorType = 'HTR' OR ReactorType = 'ASM') AND (ROTREnabled EQ True$) then
|
|
|
|
If Reactor NE '' then
|
|
ReactorRec = Database_Services('ReadDataRow', 'REACTOR', Reactor)
|
|
ROTRStatus = ReactorRec<REACTOR_ROTR_STATUS$>
|
|
|
|
If ROTRStatus EQ 'F' then
|
|
ErrorMessage = 'Process Error: Unable to load reactor due to ROTR load block. ' |
|
|
: 'A lead or supervisor must clear the ROTR load block.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
If LoadSig NE '' then
|
|
ErrorMsg = 'Process Error: Load stage has already been signed.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
WOMatCurrStatus = obj_WO_Mat('CurrStatus',WONo:'*':CassNo)
|
|
IF WOMatCurrStatus = 'HOLD' THEN
|
|
ErrorMsg = 'Process Error: Cassette is on Hold and may not be signed off.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
Return
|
|
END
|
|
|
|
FWIInstAckReq = Xlate('RDS', RDSNo, 'FWI_INST_ACK_REQ', 'X')
|
|
IF FWIInstAckReq EQ True$ THEN
|
|
ErrorMsg = 'The FWI stage engineering instructions must be acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
Return
|
|
END
|
|
|
|
* IF ReactorType NE 'EPP' THEN
|
|
|
|
If PreFlag EQ True$ then
|
|
// PRE and LOAD stages are being signed together. This is usually because the operator is loading a reactor
|
|
// using the barcode scanner, which attempts to sign both stages at once to save time.
|
|
Stage = 'VER'
|
|
end else
|
|
Stage = 'LOAD'
|
|
end
|
|
|
|
If ReactorType EQ 'EPP' then
|
|
WOMatKey = ''
|
|
end else
|
|
WOMatKey = WONo:'*':CassNo
|
|
end
|
|
|
|
Signature_Services('CheckSigOrder', WOMatKey, Stage, False$, RDSNo)
|
|
|
|
If Error_Services('HasError') then
|
|
Response = False$
|
|
Return
|
|
end
|
|
|
|
! Deprecated 11/20/2019
|
|
* Set_Status(0)
|
|
* obj_WO_Mat('CheckSigOrder',WONo:'*':CassNo:@RM:WOStep:'LOAD')
|
|
* IF Get_Status(errCode) THEN
|
|
* ErrorMsg = 'Process Error: Error calling obj_WO_Mat("CheckSigOrder"). Error code: ':errCode
|
|
* Error_Services('Set', ErrorMsg)
|
|
* Response = False$
|
|
* RETURN
|
|
* END
|
|
|
|
* END ;* End of check for Epi Pro
|
|
|
|
// Deprecated in favor of stage specific supplements
|
|
* SupplAckReq = Xlate('RDS', RDSNo, 'SUPPL_ACK_REQ', 'X')
|
|
* IF (SupplAckReq EQ True$) then
|
|
* ErrorMsg = 'The RDS Supplement must be acknowledged before the load operation can be signed.'
|
|
* Error_Services('Set', ErrorMsg)
|
|
* Response = False$
|
|
* Return
|
|
* END
|
|
|
|
SigDate = RDSRec<RDS_PRE_EPI_SIG_DATE$>
|
|
SigTime = RDSrec<RDS_PRE_EPI_SIG_TIME$>
|
|
PreCINo = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
|
|
|
|
LastCleanSigDTM = XLATE('CLEAN_INSP',PreCINo,CLEAN_INSP_CLEAN_VER_SIG_DTM$,'X')[-1,'B':@VM]
|
|
|
|
IF LastCleanSigDTM NE '' THEN
|
|
|
|
CurrDTM = ICONV(OCONV(Date(),'D4/'):' ':OCONV(Time(),'MTS'),'DT')
|
|
|
|
Delta = CurrDTM - LastCleanSigDTM
|
|
|
|
IF Delta _GTX 1.0000000000 THEN
|
|
ErrorMsg = 'Process Error: It has been more than 24 hours since the last cleaning... ' |
|
|
: 'Please re-clean the material and record in the pre epi window'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
END
|
|
END
|
|
|
|
LoadInstAckReq = Xlate('RDS', RDSNo, 'LOAD_INST_ACK_REQ', 'X')
|
|
IF LoadInstAckReq EQ True$ THEN
|
|
ErrorMsg = 'The LOAD stage engineering instructions must be acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
Return
|
|
END
|
|
|
|
// Add check for supplement signatures
|
|
UnacknowledgedSupp = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'LOAD')
|
|
If UnacknowledgedSupp NE FALSE$ then
|
|
ErrorMessage = 'The LOAD stage supplements must be acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
Response = True$
|
|
end else
|
|
Response = False$
|
|
end
|
|
|
|
end service
|
|
|
|
Service LoadExtra1stReady(RDSNo)
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
StandardLoadSignature = RDSRec<RDS_OPERATOR_IN$>
|
|
UnloadExtra1Signature = RDSRec<RDS_OP_OUT_EX1$>
|
|
LoadExtra1Signature = RDSRec<RDS_OP_IN_EX2$>
|
|
UnloadExtra2Signature = RDSRec<RDS_OP_OUT_EX2$>
|
|
LoadExtra2Signature = RDSRec<RDS_OP_IN_EX3$>
|
|
StandardUnloadSignature = RDSRec<RDS_OPERATOR_OUT$>
|
|
If StandardLoadSignature NE '' AND UnloadExtra1Signature NE '' AND LoadExtra1Signature EQ '' And LoadExtra2Signature EQ ''AND UnloadExtra2Signature EQ '' and StandardUnloadSignature EQ '' then
|
|
Response = True$
|
|
end
|
|
end service
|
|
|
|
Service UnsignLoadExtra1stReady(RDSNo)
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
StandardLoadSignature = RDSRec<RDS_OPERATOR_IN$>
|
|
UnloadExtra1Signature = RDSRec<RDS_OP_OUT_EX1$>
|
|
LoadExtra1Signature = RDSRec<RDS_OP_IN_EX2$>
|
|
UnloadExtra2Signature = RDSRec<RDS_OP_OUT_EX2$>
|
|
LoadExtra2Signature = RDSRec<RDS_OP_IN_EX3$>
|
|
StandardUnloadSignature = RDSRec<RDS_OPERATOR_OUT$>
|
|
If LoadExtra1Signature NE '' then
|
|
If UnloadExtra2Signature EQ '' AND LoadExtra2Signature EQ '' and StandardUnloadSignature EQ '' then
|
|
Response = True$
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
Service LoadExtra2ndReady(RDSNo)
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
StandardLoadSignature = RDSRec<RDS_OPERATOR_IN$>
|
|
UnloadExtra1Signature = RDSRec<RDS_OP_OUT_EX1$>
|
|
LoadExtra1Signature = RDSRec<RDS_OP_IN_EX2$>
|
|
UnloadExtra2Signature = RDSRec<RDS_OP_OUT_EX2$>
|
|
LoadExtra2Signature = RDSRec<RDS_OP_IN_EX3$>
|
|
StandardUnloadSignature = RDSRec<RDS_OPERATOR_OUT$>
|
|
If StandardLoadSignature NE '' AND UnloadExtra1Signature NE '' AND LoadExtra1Signature NE '' AND UnloadExtra2Signature NE '' And LoadExtra2Signature EQ '' and StandardUnloadSignature EQ '' then
|
|
Response = True$
|
|
end
|
|
end service
|
|
|
|
Service UnsignLoadExtra2ndReady(RDSNo)
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
StandardLoadSignature = RDSRec<RDS_OPERATOR_IN$>
|
|
UnloadExtra1Signature = RDSRec<RDS_OP_OUT_EX1$>
|
|
LoadExtra1Signature = RDSRec<RDS_OP_IN_EX2$>
|
|
UnloadExtra2Signature = RDSRec<RDS_OP_OUT_EX2$>
|
|
LoadExtra2Signature = RDSRec<RDS_OP_IN_EX3$>
|
|
StandardUnloadSignature = RDSRec<RDS_OPERATOR_OUT$>
|
|
If LoadExtra2Signature NE '' then
|
|
If StandardUnloadSignature EQ '' then
|
|
Response = True$
|
|
end
|
|
end
|
|
end service
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SignLoadStage
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS to be signed
|
|
// Username - LSL Username of the operator signing the stage.
|
|
// WaferQty - User supplied wafer quantity to verify against the schedule.
|
|
//
|
|
// Output:
|
|
// Response - True$ if LOAD stage is ready to be signed (all preconditions met and no errors encountered),
|
|
// False$ otherwise.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SignLoadStage(RDSNo, Username, WaferQty, LLSide, ScanEntry)
|
|
|
|
Error_Services('Clear')
|
|
If (RDSNo EQ '') or (Username EQ '') or (WaferQty EQ '') then
|
|
ErrorMessage = 'RDSNo, Username, or WaferQty not supplied in the ':Service:' service.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
If Error_Services('HasError') then
|
|
ErrorMsg = 'Error reading RDS record: ':RDSNo:' in the ':Service:' service.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
RDSRec<RDS_WAFERS_IN$> = WaferQty
|
|
RDSRec<RDS_LOAD_LOCK_SIDE$> = LLSide
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
|
|
WONo = RDSRec<RDS_WO$>
|
|
CassNo = RDSRec<RDS_CASS_NO$>
|
|
Reactor = RDSRec<RDS_REACTOR$>
|
|
PSN = RDSRec<RDS_PROD_SPEC_ID$>
|
|
WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X')
|
|
ReactorType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X')
|
|
LoadLockVal = LLSide
|
|
TWAllSigned = Xlate('RDS', RDSNo, 'TW_ALL_SIGNED', 'X')
|
|
RunOrderNo = Xlate('RDS', RDSNo, 'RUN_ORDER_NUM', 'X')
|
|
SchedQty = RDSRec<RDS_CASS_WAFER_QTY$>
|
|
|
|
CurrReactorLoadCapacity = Reactor_Services('GetReactorAvailChamberCount', Reactor)
|
|
If CurrReactorLoadCapacity LE 0 then
|
|
ErrorMsg = 'Error: Reactor load limit has been reached.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end else
|
|
Lot_Services('CreateLotEvent', RDSNo, 'RDS', Datetime(), 'LOAD', 'Load stage signed', Reactor, '', '', '', Username)
|
|
end
|
|
|
|
IF ReactorType NE 'EPP' THEN
|
|
|
|
WOMatRec = XLATE('WO_MAT',WONo:'*':CassNo,'','X')
|
|
WOMatSigProfile = WOMatRec<WO_MAT_SIG_PROFILE$>
|
|
SigAction = WOStep:'LOAD'
|
|
|
|
LOCATE SigAction IN WOMatSigProfile USING @VM SETTING Pos THEN
|
|
WOMatSig = WOMatRec<WO_MAT_SIGNATURE$,Pos>
|
|
CurrDTM = OCONV(WOMatRec<WO_MAT_SIG_DTM$,Pos>,'DT/4^HS')
|
|
END ELSE
|
|
WOMatSig = ''
|
|
END
|
|
RDSSig = Xlate('RDS', RDSNo, 'OPERATOR_IN', 'X')
|
|
|
|
BEGIN CASE
|
|
CASE WOMatSig = '' AND RDSSig NE ''
|
|
|
|
* Signature missing on WO_MAT
|
|
SigDate = RDSRec<RDS_DATE_IN$>
|
|
SigTime = RDSRec<RDS_TIME_IN$>
|
|
owmParms = WONo:@RM:CassNo:@RM:WOStep:@RM:'LOAD':@RM:RDSSig:@RM:SigDate:' ':SigTime
|
|
* Sets LOAD signature
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Process Error: Error calling obj_WO_Mat("SetSignature"). Error code: ':errCode
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
END else
|
|
Response = True$
|
|
end
|
|
return
|
|
|
|
CASE RDSSig = '' AND WOMatSig NE ''
|
|
|
|
* Signature missing on RDS
|
|
CurrDate = CurrDTM[1,' ']
|
|
CurrTime = CurrDTM[COL2()+1,' ']
|
|
RDSRec<RDS_OPERATOR_IN$> = WOMatSig
|
|
RDSRec<RDS_DATE_IN$> = IConv(CurrDate, 'D')
|
|
RDSRec<RDS_TIME_IN$> = IConv(CurrTime, 'MT')
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
|
|
Response = True$
|
|
return
|
|
|
|
CASE RDSSig NE '' AND WOMatSig NE ''
|
|
ErrorMsg = 'Process Error: Load stage has already been signed.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
RETURN
|
|
|
|
CASE Otherwise$
|
|
Null
|
|
// Continue
|
|
END CASE
|
|
|
|
END
|
|
|
|
SigDt = OCONV( Date(), 'D2/' )
|
|
SigTm = OCONV( Time(), 'MTS' )
|
|
LoadDTM = SigDt:' ':SigTm
|
|
|
|
IF ReactorType NE 'EPP' THEN
|
|
|
|
ReactID = 'R':Reactor
|
|
ReactWH = XLATE('REACTOR',Reactor,'TOOL_WH','X')
|
|
ReactLoc = XLATE('REACTOR',Reactor,'TOOL_LOC','X')
|
|
Tags = RDSNo
|
|
|
|
owmParms = WONo:@RM:CassNo:@RM:WOStep:@RM:'LOAD':@RM:Username:@RM:LoadDTM:@RM:ReactID:@RM:ReactWH:@RM:ReactLoc:@RM:Tags
|
|
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Process Error: Error calling obj_WO_Mat("SetSignature"). Error code: ':errCode
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
Return
|
|
END
|
|
|
|
LogFile = 'WO_MAT' ; WOMLParms = LogFile:@RM
|
|
LogDTM = LoadDTM ; WOMLParms := LogDTM:@RM
|
|
Action = WOStep:'LOAD' ; WOMLParms := Action:@RM
|
|
WhCd = ReactWH ; WOMLParms := WhCd:@RM
|
|
LocCd = ReactLoc ; WOMLParms := LocCd:@RM
|
|
WONos = WONo ; WOMLParms := WONos:@RM
|
|
CassNos = CassNo ; WOMLParms := CassNos:@RM
|
|
UserID = Username ; WOMLParms := UserID:@RM
|
|
Tags = RDSNo ; WOMLParms := Tags:@RM
|
|
ToolID = ReactID ; WOMLParms := ToolID:@RM
|
|
WOMLParms := ScanEntry
|
|
|
|
obj_WO_Mat_Log('Create',WOMLParms) ;* Add WOStep:'LOAD' to WO_MAT Event Log
|
|
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Process Error: Error calling obj_WO_Mat_Log("Create"). Error code: ':errCode
|
|
Error_Services('Set', ErrorMsg)
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = @User4
|
|
LogData<3> = RDSNo
|
|
LogData<4> = 'Error calling obj_WO_Mat_Log("Create") within the ':Service:' service. Error code: ':errCode
|
|
Logging_Services('AppendLog', objFSErrorLog, LogData, @RM, @FM)
|
|
Response = False$
|
|
Return
|
|
END
|
|
|
|
IdleTime = obj_React_Status('CassLoad',Reactor:@RM:WONo:@RM:RunOrderNo:@RM:LoadDTM:@RM:RDSNo)
|
|
|
|
IF Get_Status(errCode) THEN
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = @User4
|
|
LogData<3> = RDSNo
|
|
LogData<4> = 'Error calling obj_React_Status("CassLoad") within the ':Service:' service. Error code: ':errCode
|
|
Logging_Services('AppendLog', objFSErrorLog, LogData, @RM, @FM)
|
|
END ELSE
|
|
RDSRec<RDS_REACT_IDLE_TIME$> = IdleTime
|
|
END
|
|
|
|
END ELSE
|
|
|
|
* Update REACT_STATUS record
|
|
AllInCassettes = RDSRec<RDS_IN_CASS_NO$>
|
|
LOOP
|
|
LastChar = AllInCassettes[-1,1]
|
|
UNTIL LastChar NE @VM OR AllInCassettes = ''
|
|
AllInCassettes[-1,1] = ''
|
|
REPEAT
|
|
|
|
InCassettes = ''
|
|
CassCnt = COUNT(AllInCassettes,@VM) + (AllInCassettes NE '')
|
|
|
|
FOR N = 1 TO CassCnt
|
|
InCass = AllInCassettes<1,N>
|
|
LOCATE InCass IN InCassettes USING @VM SETTING Pos ELSE
|
|
InCassettes = INSERT(InCassettes,1,Pos,0,InCass)
|
|
END
|
|
NEXT N
|
|
|
|
IdleTime = obj_React_Status('ReactorLoad',Reactor:@RM:WONo:@RM:InCassettes:@RM:LoadDTM:@RM:RDSNo)
|
|
|
|
IF Get_Status(errCode) THEN
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = @User4
|
|
LogData<3> = RDSNo
|
|
LogData<4> = 'Error calling obj_React_Status("ReactorLoad") within the ':Service:' service. Error code: ':errCode
|
|
Logging_Services('AppendLog', objFSErrorLog, LogData, @RM, @FM)
|
|
END ELSE
|
|
RDSRec<RDS_REACT_IDLE_TIME$> = IdleTime
|
|
END ;* Added Else branch 3/22/2012 JCH
|
|
|
|
END ;* End of check for valid load signature
|
|
|
|
RDSRec<RDS_OPERATOR_IN$> = Username
|
|
RDSRec<RDS_DATE_IN$> = IConv(SigDt, 'D')
|
|
RDSRec<RDS_TIME_IN$> = IConv(SigTm, 'MT')
|
|
|
|
// Set LOAD signature in RDS record
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
|
|
|
|
If Error_Services('NoError') then
|
|
Rds_Services('AddComment', RDSNo, 'Load Stage signed onto Reactor ' : Reactor, Username)
|
|
If ReactorType EQ 'EPP' then
|
|
Reactor_Services('IncrementWfrMetrics', RDSNo)
|
|
end
|
|
Response = True$
|
|
end else
|
|
Response = False$
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// UnloadSignatureReady
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS to be signed
|
|
// Username - LSL Username of the operator signing the stage.
|
|
//
|
|
// Output:
|
|
// Response - True$ if UNLOAD stage is ready to be signed (all preconditions met and no errors encountered),
|
|
// False$ otherwise.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service UnloadSignatureReady(RDSNo, Username, Reactor)
|
|
|
|
If (RDSNo EQ '') or (Username EQ '') or (Reactor EQ '') then
|
|
ErrorMsg = 'RDSNo or Username or Reactor not supplied in the ':Service:' service.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
Return
|
|
end
|
|
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
If Error_Services('HasError') then
|
|
ErrorMsg = 'Error reading RDS record: ':RDSNo:' in the ':Service:' service.'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
WONo = RDSRec<RDS_WO$>
|
|
CassNo = RDSRec<RDS_CASS_NO$>
|
|
WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X')
|
|
RDSSig = RDSRec<RDS_OPERATOR_OUT$>
|
|
RDSReactor = RDSRec<RDS_REACTOR$>
|
|
|
|
If RDSReactor NE Reactor then
|
|
ErrorMsg = 'Process Error: Unload reactor does not match the load reactor.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
WOMatCurrStatus = obj_WO_Mat('CurrStatus',WONo:'*':CassNo)
|
|
|
|
IF WOMatCurrStatus = 'HOLD' THEN
|
|
ErrorMsg = 'Process Error: Cassette is on Hold and may not be signed off.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
END
|
|
ParamOutOfSpec = Xlate('RDS', RDSNo, 'PARAM_OUT_OF_SPEC', 'X')
|
|
Supplement = RDSRec<RDS_SUPPLEMENT$>
|
|
Locate True$ in ParamOutOfSpec using @VM setting oPos then
|
|
IF Supplement NE True$ then
|
|
ErrorMsg = 'Process Error: Recipe parameters are out of spec and no supplement has been set.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
end
|
|
ReactorType = XLATE('RDS',RDSNo,'REACTOR_TYPE','X')
|
|
|
|
|
|
* IF ReactorType NE 'EPP' THEN
|
|
|
|
|
|
If ReactorType EQ 'EPP' then
|
|
WOMatKey = ''
|
|
end else
|
|
WOMatKey = WONo:'*':CassNo
|
|
end
|
|
|
|
Signature_Services('CheckSigOrder', WOMatKey, 'UNLOAD', RDSNo)
|
|
If Error_Services('HasError') then
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
* END ELSE
|
|
*
|
|
* ReactRunRec = XLATE('REACT_RUN',RDSNo,'','X')
|
|
* LOCATE 'LWI' IN ReactRunRec<REACT_RUN_CI_STAGE$> USING @VM SETTING Pos THEN
|
|
* CINo = ReactRunRec<REACT_RUN_CI_NO$>
|
|
* Actions = obj_Clean_Insp('GetActions',CINo)
|
|
* LOCATE 'Inspection' IN Actions<ACTION$ACTIONS> USING @FM SETTING Pos THEN
|
|
* IF Actions<ACTION$SIGS,1> = '' THEN
|
|
* ErrorMsg = 'Process Error: A Wafer Inspection is required and has not been completed.'
|
|
* Error_Services('Set', ErrorMsg)
|
|
* Response = False$
|
|
* return
|
|
* END
|
|
* END
|
|
* END
|
|
*
|
|
* END ;* End of check for EpiPRO (EPP) reactor type
|
|
|
|
LWIInstAckReq = Xlate('RDS', RDSNo, 'LWI_INST_ACK_REQ', 'X')
|
|
If LWIInstAckReq EQ True$ then
|
|
ErrorMsg = 'The LWI stage engineering instructions must be acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
UnloadInstAckReq = Xlate('RDS', RDSNo, 'UNLOAD_INST_ACK_REQ', 'X')
|
|
If UnloadInstAckReq EQ True$ then
|
|
ErrorMsg = 'The UNLOAD stage engineering instructions must be acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
! Todo: We will have to define special logic for the barcode application to handle extra loads and unloads.
|
|
* Check for extra unloads without corresponding extra load
|
|
IF RDSRec<RDS_OP_OUT_EX1_DATE$> <> '' THEN
|
|
IF RDSRec<RDS_OP_IN_EX2_DATE$> = '' THEN
|
|
ErrorMsg = 'Process Error: You have an extra unload defined in the first wafer area, however, ' |
|
|
: 'it must be reloaded there as well...You have the provision of overriding ' |
|
|
: 'the date and time in, due to it being forgotten.'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
END
|
|
END
|
|
|
|
IF RDSRec<RDS_OP_OUT_EX2_DATE$> <> '' THEN
|
|
IF RDSRec<RDS_OP_IN_EX3_DATE$> = '' THEN
|
|
ErrorMsg = 'Process Error You have a second extra unload defined in the first wafer area, however, ' |
|
|
: 'it must be reloaded there as well...You have the provision of overriding ' |
|
|
: 'the date and time in, due to it being forgotten.'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
END
|
|
END
|
|
|
|
// Verify runtime is not greater than 2 days.
|
|
// Calculate runtime
|
|
LoadDTM = IConv(OConv(RDSRec<RDS_DATE_IN$>, 'D'):' ':OConv(RDSRec<RDS_TIME_IN$>, 'MT'), 'DT')
|
|
LoadEx2DTM = IConv(OConv(RDSRec<RDS_OP_IN_EX2_DATE$>, 'D'):' ':OConv(RDSRec<RDS_OP_IN_EX2_TIME$>, 'MT'), 'DT')
|
|
LoadEx3DTM = IConv(OConv(RDSRec<RDS_OP_IN_EX3_DATE$>, 'D'):' ':OConv(RDSRec<RDS_OP_IN_EX3_TIME$>, 'MT'), 'DT')
|
|
UnloadEx1DTM = IConv(OConv(RDSRec<RDS_OP_OUT_EX1_DATE$>, 'D'):' ':OConv(RDSRec<RDS_OP_OUT_EX1_TIME$>, 'MT'), 'DT')
|
|
UnloadEx2DTM = IConv(OConv(RDSRec<RDS_OP_OUT_EX2_DATE$>, 'D'):' ':OConv(RDSRec<RDS_OP_OUT_EX2_TIME$>, 'MT'), 'DT')
|
|
UnloadDTM = Datetime()
|
|
Begin Case
|
|
Case UnloadEx2DTM NE ''
|
|
Runtime = UnloadEx2DTM - LoadEx3DTM
|
|
Case UnloadEx1DTM NE ''
|
|
Runtime = UnloadEx1DTM - LoadEx2DTM
|
|
Case Otherwise$
|
|
Runtime = UnloadDTM - LoadDTM
|
|
End Case
|
|
|
|
Supervisor = Memberof(@User4, 'SUPERVISOR')
|
|
Lead = Memberof(@User4, 'LEAD')
|
|
|
|
If ( (Runtime GT 3) and Not(Lead or Supervisor) ) then
|
|
ErrorMsg = 'Cassette runtime exceeds three days! An override by a LEAD or SUPERVISOR is required.'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
If ( (Runtime GT 3) and (Lead or Supervisor) ) then
|
|
Override_Services('SetOverride', 'RDS', RDSNo, 'UNLOAD_DTM', @User4)
|
|
end
|
|
|
|
// Add check for supplement signatures
|
|
UnacknowledgedSupp = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'UNLOAD')
|
|
If UnacknowledgedSupp NE FALSE$ then
|
|
ErrorMessage = 'The UNLOAD stage supplements must be acknowledged before the unload operation can be signed.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
Response = True$
|
|
end else
|
|
Response = False$
|
|
end
|
|
|
|
end service
|
|
|
|
Service UnloadExtra1stReady(RDSNo)
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
StandardLoadSignature = RDSRec<RDS_OPERATOR_IN$>
|
|
UnloadExtra1Signature = RDSRec<RDS_OP_OUT_EX1$>
|
|
LoadExtra1Signature = RDSRec<RDS_OP_IN_EX2$>
|
|
UnloadExtra2Signature = RDSRec<RDS_OP_OUT_EX2$>
|
|
LoadExtra2Signature = RDSRec<RDS_OP_IN_EX3$>
|
|
StandardUnloadSignature = RDSRec<RDS_OPERATOR_OUT$>
|
|
If StandardLoadSignature NE '' AND UnloadExtra1Signature EQ '' AND LoadExtra1Signature EQ '' AND UnloadExtra2Signature EQ '' And LoadExtra2Signature eq '' and StandardUnloadSignature EQ '' then
|
|
Response = True$
|
|
end
|
|
end service
|
|
|
|
Service UnloadExtra2ndReady(RDSNo)
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
StandardLoadSignature = RDSRec<RDS_OPERATOR_IN$>
|
|
UnloadExtra1Signature = RDSRec<RDS_OP_OUT_EX1$>
|
|
LoadExtra1Signature = RDSRec<RDS_OP_IN_EX2$>
|
|
UnloadExtra2Signature = RDSRec<RDS_OP_OUT_EX2$>
|
|
LoadExtra2Signature = RDSRec<RDS_OP_IN_EX3$>
|
|
StandardUnloadSignature = RDSRec<RDS_OPERATOR_OUT$>
|
|
If StandardLoadSignature NE '' AND UnloadExtra1Signature NE '' AND LoadExtra1Signature NE '' And LoadExtra2Signature EQ ''AND UnloadExtra2Signature EQ '' and StandardUnloadSignature EQ '' then
|
|
Response = True$
|
|
end
|
|
end service
|
|
|
|
Service UnsignUnloadExtra1stReady(RDSNo)
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
StandardLoadSignature = RDSRec<RDS_OPERATOR_IN$>
|
|
UnloadExtra1Signature = RDSRec<RDS_OP_OUT_EX1$>
|
|
LoadExtra1Signature = RDSRec<RDS_OP_IN_EX2$>
|
|
UnloadExtra2Signature = RDSRec<RDS_OP_OUT_EX2$>
|
|
LoadExtra2Signature = RDSRec<RDS_OP_IN_EX3$>
|
|
StandardUnloadSignature = RDSRec<RDS_OPERATOR_OUT$>
|
|
If UnloadExtra1Signature NE '' then
|
|
If LoadExtra1Signature EQ '' AND UnloadExtra2Signature EQ '' AND LoadExtra2Signature EQ '' and StandardUnloadSignature EQ '' then
|
|
Response = True$
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
Service UnsignUnloadExtra2ndReady(RDSNo)
|
|
Response = False$
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
StandardLoadSignature = RDSRec<RDS_OPERATOR_IN$>
|
|
UnloadExtra1Signature = RDSRec<RDS_OP_OUT_EX1$>
|
|
LoadExtra1Signature = RDSRec<RDS_OP_IN_EX2$>
|
|
UnloadExtra2Signature = RDSRec<RDS_OP_OUT_EX2$>
|
|
LoadExtra2Signature = RDSRec<RDS_OP_IN_EX3$>
|
|
StandardUnloadSignature = RDSRec<RDS_OPERATOR_OUT$>
|
|
If UnloadExtra2Signature NE '' then
|
|
If LoadExtra2Signature EQ '' and StandardUnloadSignature EQ '' then
|
|
Response = True$
|
|
end
|
|
end
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SignUnloadStage
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS to be signed
|
|
// Username - LSL Username of the operator signing the stage.
|
|
//
|
|
// Output:
|
|
// Response - True$ if UNLOAD stage was successfully signed , False$ otherwise.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SignUnloadStage(RDSNo, Username, ScanEntry)
|
|
If (RDSNo EQ '') or (Username EQ '') then
|
|
ErrorMsg = 'RDSNo or Username not supplied in the ':Service:' service.'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
Return
|
|
end
|
|
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
If Error_Services('HasError') then
|
|
ErrorMsg = 'Error reading RDS record: ':RDSNo:' in service: ':Service
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
WONo = RDSRec<RDS_WO$>
|
|
CassNo = RDSRec<RDS_CASS_NO$>
|
|
WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X')
|
|
RDSSig = RDSRec<RDS_OPERATOR_OUT$>
|
|
ReactType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X')
|
|
IF RDSSig = '' THEN
|
|
|
|
WOMatRec = XLATE('WO_MAT',WONo:'*':CassNo,'','X')
|
|
|
|
LOCATE WOStep:'UNLOAD' IN WOMatRec<WO_MAT_SIG_PROFILE$> USING @VM SETTING Pos THEN
|
|
|
|
CurrSig = WOMatRec<WO_MAT_SIGNATURE$,Pos>
|
|
|
|
IF CurrSig NE '' THEN
|
|
|
|
* Already signed off without saving the RDS signatures
|
|
|
|
CurrSigDTM = WOMatRec<WO_MAT_SIG_DTM$,Pos>
|
|
CurrSigDate = CurrSigDTM[1, 'F.']
|
|
CurrSigTime = CurrSigDTM[-1, 'B.']
|
|
|
|
RDSRec<RDS_OPERATOR_OUT$> = CurrSig
|
|
RDSRec<RDS_DATE_OUT$> = CurrSigDate
|
|
RDSRec<RDS_TIME_OUT$> = CurrSigTime
|
|
|
|
/* Sync up the RDS record with WO_MAT */
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, False$)
|
|
If ReactType NE 'EPP' then
|
|
Reactor_Services('IncrementWfrMetrics', RDSNo)
|
|
end
|
|
Response = True$
|
|
RETURN
|
|
END
|
|
END
|
|
END
|
|
|
|
SigDt = OCONV( date(), 'D2/' )
|
|
SigTm = OCONV( time(), 'MTS' )
|
|
SigBy = Username
|
|
|
|
WONo = RDSRec<RDS_WO$>
|
|
CassNo = RDSRec<RDS_CASS_NO$>
|
|
WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X')
|
|
ReactorType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X')
|
|
|
|
UnloadDTM = SigDt:' ':SigTm
|
|
Reactor = RDSRec<RDS_REACTOR$>
|
|
|
|
IF ReactorType NE 'EPP' THEN
|
|
|
|
ReactID = 'R':Reactor
|
|
ReactWH = XLATE('REACTOR',Reactor,'TOOL_WH','X')
|
|
ReactLoc = XLATE('REACTOR',Reactor,'TOOL_LOC','X')
|
|
Tag = RDSNo
|
|
|
|
* 4/30/2013 JCH added parms for merging of two methods
|
|
owmParms = WONo:@RM:CassNo:@RM:WOStep:@RM:'UNLOAD':@RM:Username:@RM:SigDt:' ':SigTm:@RM:ReactID:@RM:ReactWH:@RM:ReactLoc:@RM:Tag
|
|
* obj_WO_Mat('SetSignature',owmParms)
|
|
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Process Error: Error calling Obj_WO_Mat("SetSignature") within the ':Service:' service'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
END
|
|
|
|
IF WONo NE '' AND CassNo NE '' THEN
|
|
|
|
LogFile = 'WO_MAT' ; WOMLParms = LogFile:@RM
|
|
LogDTM = UnloadDTM ; WOMLParms := LogDTM:@RM
|
|
Action = WOStep:'UNLOAD' ; WOMLParms := Action:@RM
|
|
WhCd = ReactWH ; WOMLParms := WhCd:@RM
|
|
LocCd = ReactLoc ; WOMLParms := LocCd:@RM
|
|
WONos = WONo ; WOMLParms := WONos:@RM
|
|
CassNos = CassNo ; WOMLParms := CassNos:@RM
|
|
UserID = Username ; WOMLParms := UserID:@RM
|
|
Tags = RDSNo ; WOMLParms := Tags:@RM
|
|
ToolID = ReactID ; WOMLParms := ToolID:@RM
|
|
WOMLParms := ScanEntry
|
|
|
|
obj_WO_Mat_Log('Create',WOMLParms) ;* Add to WO_MAT Event Log
|
|
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Process Error: Error calling Obj_WO_Mat_Log("Create") within the ':Service:' service'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
END
|
|
|
|
obj_React_Status('CassUnload',Reactor:@RM:WONo:@RM:CassNo:@RM:UnloadDTM:@RM:RDSNo)
|
|
IF Get_Status(errCode) THEN
|
|
* ErrorMsg = 'Process Error: Error calling Obj_React_Status("CassUnload") within the ':Service:' service'
|
|
* Error_Services('Add', ErrorMsg)
|
|
* Response = False$
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = @User4
|
|
LogData<3> = RDSNo
|
|
LogData<4> = 'Error calling Obj_React_Status("CassUnload") within the ':Service:' service. Error code: ':errCode
|
|
Logging_Services('AppendLog', objFSErrorLog, LogData, @RM, @FM)
|
|
end
|
|
Reactor_Services('IncrementWfrMetrics', RDSNo)
|
|
END ;* End of check for WONo and CassNo
|
|
;* End of check for EpiPRO reactor type
|
|
END else
|
|
* EpiPro
|
|
InCassettes = RDSRec<RDS_IN_CASS_NO$>
|
|
InCassettes = SRP_Array('Clean', InCassettes, 'TrimAndMakeUnique', @VM)
|
|
obj_React_Status('ReactorUnload',Reactor:@RM:WONo:@RM:InCassettes:@RM:UnloadDTM:@RM:RDSNo)
|
|
//Reactor_Services('IncrementWfrMetrics', RDSNo)
|
|
end
|
|
|
|
RDSRec<RDS_OPERATOR_OUT$> = SigBy
|
|
RDSRec<RDS_DATE_OUT$> = IConv(SigDt, 'D')
|
|
RDSRec<RDS_TIME_OUT$> = IConv(SigTm, 'MT')
|
|
|
|
/* Sync up the RDS record with WO_MAT */
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
|
|
If Error_Services('NoError') then
|
|
CurReactor = RDSRec<RDS_REACTOR$>
|
|
|
|
* * Set Current Reactor Information on the RDS record **
|
|
* * Flag Graphite records if this PSN was using 1% Boron dopant * *
|
|
|
|
PSNo = RDSRec<RDS_PROD_SPEC_ID$>
|
|
ReactRec = XLATE('REACTOR', CurReactor, '', 'X')
|
|
CurrInjSetting = XLATE('REACT_STATE', CurReactor, 'CURR_INJ_SETTINGS', 'X')
|
|
Dopants = XLATE('PROD_SPEC', PSNo, 'LS_DOPANT', 'X')
|
|
|
|
IF INDEX(Dopants,'1% Boron',1) THEN
|
|
GraphiteRINos = obj_Reactor('CurrGraphite',CurReactor)
|
|
|
|
grCnt = COUNT(GraphiteRINos,@VM) + (GraphiteRINos NE '')
|
|
|
|
FOR I = 1 TO grCnt
|
|
GraphiteRINo = GraphiteRINos<1,I>
|
|
PPlusFlag = XLATE('REACT_ITEM', GraphiteRINo, REACT_ITEM_PPLUS_RDS_NO$, 'X')
|
|
IF PPLusFlag = '' THEN
|
|
oplParms = 'REACT_ITEM':@RM
|
|
oplParms := GraphiteRINo:@RM
|
|
oplParms := REACT_ITEM_PPLUS_RDS_NO$:@RM
|
|
oplParms := RDSNo
|
|
|
|
obj_Post_Log('Create',oplParms) ;* Add RDSNo to Tube Record indicating the tube has been used with PPlus Dopant
|
|
|
|
END ;* End of check for existing RdsNo on the Graphite Record
|
|
NEXT I
|
|
END ;* End of check for '1% Boron' in the Dopant list
|
|
|
|
TubeID = obj_Reactor('CurrItem',CurReactor:@RM:'T':@RM:ReactRec)
|
|
TubeGrade = XLATE('REACT_ITEM',TubeID,REACT_ITEM_TUBE_GRADE$,'X')
|
|
SusceptorID = obj_Reactor('CurrItem',CurReactor:@RM:'S':@RM:ReactRec)
|
|
|
|
RDSRec<RDS_INJECTORS$> = CurrInjSetting
|
|
RDSRec<RDS_TUBE_ID$> = TubeID
|
|
RDSRec<RDS_TUBE_GRADE$> = TubeGrade
|
|
RDSRec<RDS_SUSCEPTOR_ID$> = SusceptorID
|
|
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
|
|
If Error_Services('NoError') then
|
|
|
|
* Sets Reactor State with parameters from the PROD_SPEC record
|
|
obj_React_State('ReactRunUnload',CurReactor:@RM:PSNo:@RM:RDSNo:@RM:UnloadDTM)
|
|
|
|
IF Get_Status(errCode) THEN
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = @User4
|
|
LogData<3> = RDSNo
|
|
LogData<4> = 'Error calling Obj_React_State("ReactRunUnload") within the ':Service:' service. Error code: ':errCode
|
|
Logging_Services('AppendLog', objFSErrorLog, LogData, @RM, @FM)
|
|
end
|
|
end
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
Rds_Services('AddComment', RDSNo, 'Unload Stage signed onto Reactor ' : Reactor, Username)
|
|
Response = True$
|
|
end else
|
|
Response = False$
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
Service PostEpiSignatureReady(RDSNo, User)
|
|
|
|
Response = True$
|
|
ErrorMsg = ''
|
|
|
|
If ( (RDSNo EQ '') or (User EQ '') ) then
|
|
ErrorMsg = 'Error in ':Service:' service. Null RDSNo or User passed in.'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
|
|
If Error_Services('HasError') then
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
WONo = RDSRec<RDS_WO$>
|
|
CassNo = RDSRec<RDS_CASS_NO$>
|
|
Reactor = RDSRec<RDS_REACTOR$>
|
|
WOStep = 1
|
|
WOMatCurrStatus = obj_WO_Mat('CurrStatus',WONo:'*':CassNo)
|
|
|
|
IF WOMatCurrStatus = 'HOLD' THEN
|
|
ErrorMsg = 'Error in ':Service:' service. Cassette is on Hold and may not be signed off.'
|
|
Error_Services('Add', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
END
|
|
|
|
Set_Status(0)
|
|
obj_WO_Mat('MQAComp',WONo:'*':CassNo:@RM:WOStep:@RM:'UNLOAD')
|
|
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Error in ':Service:' service. ':errCode
|
|
Error_Services('Add', ErrorMsg)
|
|
RETURN 0
|
|
END
|
|
|
|
// Ensure all previous stages are complete
|
|
ReactorType = XLATE('RDS',RDSNo,'REACTOR_TYPE','X')
|
|
|
|
IF ReactorType NE 'EPP' THEN
|
|
WOMatKey = WONo:'*':CassNo
|
|
Signature_Services('CheckSigOrder', WOMatKey, 'POST')
|
|
If Error_Services('HasError') then
|
|
Response = False$
|
|
return
|
|
end
|
|
end
|
|
|
|
* Added 1/28/2013 JCH *
|
|
// Ensure any POST stage specific instructions have been acknowledged
|
|
PostInstAckReq = Xlate('RDS', RDSNo, 'POST_INST_ACK_REQ', 'X')
|
|
If PostInstAckReq EQ True$ then
|
|
ErrorMsg = 'The POST stage engineering instructions must be acknowledged before the load operation can be signed.'
|
|
Error_Services('Set', ErrorMsg)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
// Add check for supplement signatures
|
|
UnacknowledgedSupp = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'POST')
|
|
If UnacknowledgedSupp NE FALSE$ then
|
|
ErrorMessage = 'The post epi stage supplements must be acknowledged before the post epi operation can be signed.'
|
|
Error_Services('Set', ErrorMessage)
|
|
Response = False$
|
|
return
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
Service UpdateFailedWafers(WOMatKey)
|
|
|
|
WONo = Field(WOMatKey, '*', 1)
|
|
CassNo = Field(WOMatKey, '*', 2)
|
|
PSNo = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
|
|
|
|
If PSNo NE '' then
|
|
|
|
ReactorType = Xlate('PROD_SPEC', PSNo, 'REACTOR_TYPE', 'X')
|
|
|
|
If ReactorType NE '' then
|
|
|
|
Begin Case
|
|
Case ReactorType EQ 'EPP' or ReactorType EQ 'P'
|
|
// Epipro Silicon
|
|
// N/A
|
|
Null
|
|
|
|
Case ReactorType EQ 'GAN'
|
|
// Gallium Nitride
|
|
// N/A
|
|
Null
|
|
|
|
Case Otherwise$
|
|
// Non-Epipro Silicon
|
|
// Check RDS' pre, first (FWI), unload (LWI), and post cleans
|
|
RDSNo = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
|
|
WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
|
|
If RDSNo NE '' then
|
|
|
|
AllFailedWafers = ''
|
|
PostSupercede = Xlate('RDS', RDSNo, 'POST_SUPERCEDE', 'X')
|
|
If PostSupercede then
|
|
PostCINo = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
|
|
AllFailedWafers = Xlate('CLEAN_INSP', PostCINo, 'FAILED_WAFERS', 'X')
|
|
end else
|
|
PreCINo = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
|
|
PreFailedWafers = Xlate('CLEAN_INSP', PreCINo, 'FAILED_WAFERS', 'X')
|
|
FWICINo = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
|
|
FWIFailedWafers = Xlate('CLEAN_INSP', FWICINo, 'FAILED_WAFERS', 'X')
|
|
LWICINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
|
|
LWIFailedWafers = Xlate('CLEAN_INSP', LWICINo, 'FAILED_WAFERS', 'X')
|
|
PostCINo = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
|
|
PostFailedWafers = Xlate('CLEAN_INSP', PostCINo, 'FAILED_WAFERS', 'X')
|
|
For WfrIndex = 1 to 25
|
|
AllFailedWafers<0, WfrIndex> = (PreFailedWafers<0, WfrIndex> OR FWIFailedWafers<0, WfrIndex> OR LWIFailedWafers<0, WfrIndex> OR PostFailedWafers<0, WfrIndex>)
|
|
Next WfrIndex
|
|
end
|
|
WOMatRec<WO_MAT_FAILED_WAFERS$> = AllFailedWafers
|
|
Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$)
|
|
end else
|
|
Error_Services('Add', 'Error in ':Service:' service. RDSNo is null')
|
|
end
|
|
|
|
End Case
|
|
end else
|
|
Error_Services('Add', 'Error in ':Service:' service. ReactorType is null')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error in ':Service:' service. PSNo is null')
|
|
end
|
|
|
|
|
|
end service
|
|
|
|
|
|
Service GetNCRRequired(WOMatKey)
|
|
|
|
// Verify that NCR wafer quantity meets or exceeds the number of failed wafers.
|
|
NCRReq = False$
|
|
|
|
If WOMatKey NE '' then
|
|
WONo = Field(WOMatKey, '*', 1)
|
|
PSNo = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
|
|
|
|
If PSNo NE '' then
|
|
|
|
ReactorType = Xlate('PROD_SPEC', PSNo, 'REACTOR_TYPE', 'X')
|
|
|
|
If ReactorType NE '' then
|
|
|
|
Begin Case
|
|
Case ReactorType EQ 'EPP' or ReactorType EQ 'P'
|
|
// Epipro Silicon
|
|
|
|
Case ReactorType EQ 'GAN'
|
|
// Gallium Nitride
|
|
// No NCR verification at the moment
|
|
Null
|
|
|
|
Case Otherwise$
|
|
// Non-Epipro Silicon
|
|
RDSKey = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
|
|
|
|
LWICIKey = Xlate('RDS', RDSKey, 'LWI_CI_NO', 'X')
|
|
PostSupercede = Xlate('RDS', RDSKey, 'POST_SUPERCEDE', 'X')
|
|
CIKey = LWICIKey
|
|
If PostSupercede then
|
|
PostCIKey = Xlate('RDS', RDSKey, 'POST_CI_NO', 'X')
|
|
If PostCIKey NE '' then
|
|
CIKey = PostCIKey
|
|
end
|
|
end else
|
|
CIKey = LWICIKey
|
|
end
|
|
|
|
NumFailedWfrs = Sum(Xlate('CLEAN_INSP', CIKey, 'FAILED_WAFERS', 'X'))
|
|
NumNCRWfrs = Xlate('WO_MAT', WOMatKey, 'REJ_WFRS', 'X')
|
|
If NumNCRWfrs EQ '' then NumNCRWfrs = 0
|
|
If NumNCRWfrs LT NumFailedWfrs then NCRReq = True$
|
|
|
|
End Case
|
|
end else
|
|
Error_Services('Add', 'Error in ':Service:' service. ReactorType is null.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error in ':Service:' service. PSNo is null.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error in ':Service:' service. WOMatKey is null.')
|
|
end
|
|
|
|
Response = NCRReq
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SignFQAStage
|
|
//
|
|
// Input:
|
|
// RDSNo - RDS to be signed
|
|
// Username - LSL Username of the operator signing the stage.
|
|
//
|
|
// The purpose of this service is to encapsulate the sign button click event of the RDS_POST_EPI form. Ultimately
|
|
// this service will be used in the barcode integration application so that we can decouple the sign event from
|
|
// the OpenInsight form interface. Once this service returns to the calling procedure, Error_Services('NoError') should
|
|
// be called to check if an error was set, which would have prevented the stage from being signed successfully.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SignFQAStage(RDSNo, Username)
|
|
|
|
Database_Services('ActivateRecord', 'RDS', RDSNo)
|
|
If Error_Services('NoError') then
|
|
Valid = ''
|
|
CurRdsNo = RDSNo
|
|
WONo = {WO}
|
|
CassNo = {CASS_NO}
|
|
WOStep = {WO_STEP}
|
|
ReactorType = {REACTOR_TYPE}
|
|
|
|
******************************************
|
|
* Update the form with signature, if any *
|
|
******************************************
|
|
CurrSig = ''
|
|
CurrDTM = ''
|
|
CurrDate = ''
|
|
CurrTime = ''
|
|
SupSig = {SUP_VER_SIG}
|
|
|
|
IF ((ReactorType NE 'EPP') AND (SupSig = '')) THEN
|
|
WOMatRec = XLATE('WO_MAT',WONo:'*':CassNo,'','X')
|
|
WOMatSigProfile = WOMatRec<WO_MAT_SIG_PROFILE$>
|
|
SigAction = WOStep:'QA'
|
|
LOCATE SigAction IN WOMatSigProfile USING @VM SETTING Pos THEN
|
|
IF WOMatRec<WO_MAT_SIGNATURE$,Pos> NE '' THEN
|
|
CurrSig = WOMatRec<WO_MAT_SIGNATURE$,Pos>
|
|
CurrDTM = OCONV(WOMatRec<WO_MAT_SIG_DTM$,Pos>,'DT/4^HS')
|
|
CurrDate = CurrDTM[1,' ']
|
|
CurrTime = CUrrDTM[COL2()+1,' ']
|
|
{SUP_VER_SIG} = CurrSig
|
|
{SUP_VER_SIG_DATE} = CurrDate
|
|
{SUP_VER_SIG_TIME} = CurrTime
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, @Record, True$, False$, False$)
|
|
END
|
|
END
|
|
END
|
|
|
|
*********************************************
|
|
* Verify if the FQA has already been signed *
|
|
*********************************************
|
|
IF Not(MemberOf(Username,'BYPASS')) THEN
|
|
IF (CurrSig NE '') THEN
|
|
ErrorMsg = 'Supervisor Verification for this material has already been signed.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
END
|
|
|
|
******************************************
|
|
* Verify if the Post-EPI has been signed *
|
|
******************************************
|
|
PostEpiSig = {POST_EPI_SIG}
|
|
IF (PostEpiSig = '') THEN
|
|
ErrorMsg = 'Process Error: Technician Signature Required before Final QA Verification.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
**************************************************************************
|
|
* Verify if the FQA signature is a different signature than the Post-EPI *
|
|
**************************************************************************
|
|
IF Not(MemberOf(Username,'BYPASS')) THEN ;* Bypass requirement for a different signer for post-epi and FQA -dkk 10/9/14
|
|
IF (PostEpiSig EQ Username) THEN
|
|
ErrorMsg = 'Process Error: User ':QUOTE(PostEpiSig):' has signed this RDS for Post-Epi.' |
|
|
: 'Another qualified user must sign FQA verification'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
END
|
|
|
|
***************************************
|
|
* Verify Metrology has been completed *
|
|
***************************************
|
|
Set_Status(0)
|
|
obj_WO_Mat('MQAComp',WONo:'*':CassNo:@RM:WOStep:@RM:'QA')
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Error calling Obj_WO_Mat("MQAComp") in ':Service:'. Error code: ':errCode
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
**********************************************
|
|
* Verify Signatures Profile has been fulfill *
|
|
**********************************************
|
|
WOMatKey = WONo:'*':CassNo
|
|
Signature_Services('CheckSigOrder', WOMatKey, 'QA')
|
|
If Error_Services('HasError') then
|
|
Response = False$
|
|
Return
|
|
end
|
|
|
|
Signature_Services('FQAReady', WOMatKey)
|
|
If Error_Services('HasError') then
|
|
Response = False$
|
|
Return
|
|
end
|
|
|
|
WOMatRec = ''
|
|
WOMatKey = WONo:'*':CassNo:@RM:WOStep
|
|
WOMatRec = XLATE('WO_MAT', WOMatKey, '', 'X')
|
|
IF (WOMatRec NE '') THEN
|
|
ProfileSignatureCnt = COUNT(WOMatRec<WO_MAT_SIG_PROFILE$>,@VM) + (WOMatRec<WO_MAT_SIG_PROFILE$> NE '')
|
|
FOR Index = 1 TO ProfileSignatureCnt-1 /* Minus -1 = Do not look at the last signature, which is Final QA */
|
|
IF (WOMatRec<WO_MAT_SIG_PROFILE$,Index>[1,1] = WOStep) THEN
|
|
SigProfile = WOMatRec<WO_MAT_SIG_PROFILE$,Index>
|
|
Signature = WOMatRec<WO_MAT_SIGNATURE$,Index>
|
|
SigDTM = WOMatRec<WO_MAT_SIG_DTM$,Index>
|
|
SigVer = WOMatRec<WO_MAT_SIG_VER$,Index>
|
|
IF ((Signature = '') OR (SigDTM = '') OR (SigVer = '')) THEN
|
|
ErrorMsg = 'Unable to sign FQA due to incomplete Signature Profile at ':SigProfile:' stage.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
END
|
|
NEXT Index
|
|
END
|
|
|
|
|
|
**********************************************
|
|
* Verify the FlatFinder information *
|
|
**********************************************
|
|
EpiPartNo = {EPI_PART_NO}
|
|
WaferSize = Xlate('EPI_PART', EpiPartNo, 'SUB_WAFER_SIZE', 'X')
|
|
WaferSizeInch = Field(WaferSize, ' ', 3, 1)
|
|
CustNo = {CUST_NO}
|
|
CompanyRow = Xlate('COMPANY', CustNo, '', 'X')
|
|
WafersOut = {WFRS_OUT}
|
|
|
|
Begin Case
|
|
|
|
Case WaferSizeInch = '6'
|
|
|
|
WaferFlatSizeInches = CompanyRow<COMPANY_WAFER_FLAT_WAFER_SIZE_INCH$>
|
|
WaferFlatLengthMins = Oconv(CompanyRow<COMPANY_WAFER_FLAT_LENGTH_MIN$>, 'MD1')
|
|
WaferFlatLengthMaxes = Oconv(CompanyRow<COMPANY_WAFER_FLAT_LENGTH_MAX$>, 'MD1')
|
|
|
|
Locate WaferSizeInch in WaferFlatSizeInches using @VM setting vPos then
|
|
WaferFlatLengthMin = WaferFlatLengthMins<0, vPos>
|
|
WaferFlatLengthMax = WaferFlatLengthMaxes<0, vPos>
|
|
end else
|
|
WaferFlatLengthMin = ''
|
|
WaferFlatLengthMax = ''
|
|
end
|
|
|
|
// Get FlatFinder Read Value
|
|
* FlatFinderWafersQty = {FLATFINDER_WAFER_CNT}
|
|
FlatFinderWaferLength = {FLATFINDER_FLAT_LENGTH}
|
|
|
|
***************************************
|
|
* FlatFinder - Wafers Quatity Section *
|
|
***************************************
|
|
* If (FlatFinderWafersQty NE '') then
|
|
* If (FlatFinderWafersQty NE WafersOut) then
|
|
* ErrorMsg = 'Unable to sign FQA because Flat Finder and Wafers Out quantities do not match.'
|
|
* Error_Services('Add', ErrorMsg)
|
|
* return
|
|
* end
|
|
* end else
|
|
* ErrorMsg = 'Unable to sign FQA because the Flat Finder quantity is missing.'
|
|
* Error_Services('Add', ErrorMsg)
|
|
* return
|
|
* end
|
|
************************************
|
|
* FlatFinder - Flat Length Section *
|
|
************************************
|
|
If (WaferFlatLengthMin NE '') AND (WaferFlatLengthMax NE '') then
|
|
If (FlatFinderWaferLength NE '') then
|
|
If (FlatFinderWaferLength GE WaferFlatLengthMin) AND (FlatFinderWaferLength LE WaferFlatLengthMax) then
|
|
end else
|
|
ErrorMsg = 'Unable to sign FQA because Flat Finder wafer lengths are out of bounds.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
end
|
|
end else
|
|
ErrorMsg = 'Unable to sign FQA because the Flat Finder wafer length is missing.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
end
|
|
end
|
|
|
|
Case WaferSizeInch = '8'
|
|
|
|
// Get NotchFinder Read Value
|
|
* NotchFinderWafersQty = {FLATFINDER_WAFER_CNT}
|
|
*
|
|
* ***************************************
|
|
* * NotchFinder - Wafers Quatity Section *
|
|
* ***************************************
|
|
* If (NotchFinderWafersQty NE '') then
|
|
* If (NotchFinderWafersQty NE WafersOut) then
|
|
* ErrorMsg = 'Unable to sign FQA because Notch Finder and Wafers Out quantities do not match.'
|
|
* Error_Services('Add', ErrorMsg)
|
|
* return
|
|
* end
|
|
* end else
|
|
* ErrorMsg = 'Unable to sign FQA because the Notch Finder quantity is missing.'
|
|
* Error_Services('Add', ErrorMsg)
|
|
* return
|
|
* end
|
|
|
|
Case 1
|
|
End Case
|
|
|
|
*************************
|
|
* Verify Wafer Quantity *
|
|
*************************
|
|
! The barcode application will need to inform the user that an override is required. We will return an error for
|
|
! now. Once the user is informed, a LEAD or SUPERVISOR can scan their badge to override and complete the scan.
|
|
CassSchedWafers = {CASS_SHIP_QTY}
|
|
WafersOut = {WFRS_OUT}
|
|
|
|
If CassSchedWafers NE WafersOut then
|
|
|
|
If NOT( MemberOf(Username, 'LEAD') OR MemberOf(Username, 'SUPERVISOR') ) then
|
|
ErrorMsg = 'Unable to sign FQA because the Cass Sched. Qty does not equal the ' |
|
|
: 'Wafers Out Qty. A lead or supervisor must override.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
end else
|
|
{WAFER_OUT_SIG} = Username
|
|
{WAFER_OUT_DATE} = Date()
|
|
{WAFER_OUT_TIME} = Time()
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, @Record, True$, False$, False$)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
*********************************
|
|
* Verify NCR total >= USL Fails *
|
|
*********************************
|
|
! The barcode application will need to inform the user that an override is required. We will return an error for
|
|
! now. Once the user is informed, a LEAD or SUPERVISOR can scan their badge to override and complete the scan.
|
|
If Username EQ 'FRANCOIS_R' then
|
|
NCRStatus = QA_Services('GetNCRStatus', RDSNo)
|
|
If NCRStatus EQ False$ then
|
|
If NOT( MemberOf(Username, 'LEAD') OR MemberOf(Username, 'SUPERVISOR') ) then
|
|
ErrorMsg = 'Unable to sign FQA because the total quantity of NCR wafers is less than the '|
|
|
: 'number of wafers above the USL threshold. A lead or supervisor must override.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
*************************
|
|
* Verify if Shift exist *
|
|
*************************
|
|
IF {SHIFT} EQ '' THEN
|
|
ErrorMsg = 'Process Error: Shift is required before signing.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
****************************
|
|
* Verify if lot is on Hold *
|
|
****************************
|
|
WOMatCurrStatus = obj_WO_Mat('CurrStatus',WONo:'*':CassNo)
|
|
IF WOMatCurrStatus = 'HOLD' THEN
|
|
ErrorMsg = 'Process Error: Cassette is on Hold and may not be signed off.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
**********************
|
|
* Check ROTR Failure *
|
|
**********************
|
|
ROTRAction = {ROTR_ACTION}
|
|
If (ROTRAction NE 'P') AND (ROTRAction NE 'A') AND (ROTRAction NE '') then
|
|
ErrorMsg = 'Process Error: ROTR does not meet all requirements.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
end
|
|
|
|
***************************************
|
|
* Verify Metrology has been completed *
|
|
***************************************
|
|
MetTest = {MET_TEST_QA}
|
|
MetMin = {MET_MIN_QA}
|
|
MetMax = {MET_MAX_QA}
|
|
MetResult = {MET_RESULT_QA}
|
|
MetSig = {MET_SIG_QA}
|
|
|
|
For each Test in MetTest using @FM setting fPos
|
|
If Test NE '' then
|
|
Begin Case
|
|
Case (MetMin<fPos> EQ '') and (MetMax<fPos> EQ '')
|
|
Null
|
|
Case (MetResult<fPos> EQ '')
|
|
ErrorMsg = 'Process Error: Required QA Metrology results have not been entered.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
Case (MetResult<fPos> LT MetMin<fPos>) or (MetResult<fPos> GT MetMax<fPos>)
|
|
ErrorMsg = 'Process Error: One or more QA Metrology results is out of specification.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
Case (MetResult<fPos> NE '') and (MetSig<fPos> EQ '')
|
|
ErrorMsg = 'Process Error: One or more QA Metrology results are not signed off.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
End Case
|
|
end
|
|
Next Test
|
|
|
|
******************************************
|
|
* Verify Unload Stage QA Metrology Tests * On hold until approved for release to production
|
|
******************************************
|
|
If Username EQ 'FRANCOIS_R' then
|
|
WONo = {WO}
|
|
CassNo = {CASS_NO}
|
|
WOMatQAKey = WONo : '*' : CassNo
|
|
WOMatQARec = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatQAKey)
|
|
OutOfSpec = WOMatQARec<WO_MAT_QA_OUT_OF_SPEC$>
|
|
OutOfSpec = Sum(OutOfSpec)
|
|
If OutOfSpec GT 0 then
|
|
ErrorMsg = 'Process Error: One or more Unload QA Metrology results are out of specification.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
end
|
|
end
|
|
|
|
*******************************************
|
|
* Verify if all steps have been completed *
|
|
*******************************************
|
|
PsnNo = {PROD_SPEC_ID}
|
|
WONo = {WO}
|
|
WOStep = {WO_STEP}
|
|
CassNo = {CASS_NO}
|
|
Reactor = {REACTOR}
|
|
WOMatRec = XLATE('WO_MAT',WONo:'*':CassNo,'','X')
|
|
RDSNos = WOMatRec<WO_MAT_RDS_NO$>
|
|
MakeupBox = WOMatRec<WO_MAT_MAKEUP_BOX$>
|
|
|
|
LOCATE RDSNo IN RDSNos USING @VM SETTING Pos THEN
|
|
NextRDS = RDSNos<1,Pos+1>
|
|
END ELSE
|
|
NextRDS = ''
|
|
END
|
|
|
|
IF (WOMatCurrStatus NE 'QA' AND WOMatCurrStatus NE 'PSTI' AND WOMatCurrStatus NE 'COMP') AND (NextRDS NE '' AND WOMatCurrStatus NE 'VER') THEN
|
|
ErrorMsg = 'Process Error: Run is not ready for final QA.' |
|
|
: 'The ':OCONV(WOMatCurrStatus,'[WO_MAT_CURR_STATUS_CONV]'):' step is not complete.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
IF NOT(MemberOF( Username, 'SUPERVISOR' ) OR MemberOf( Username, 'LEAD' ) OR MemberOf( Username, 'FINAL_QA' )) THEN
|
|
ErrorMsg = 'Process Error: You must be a supervisor to verify this RDS.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
************************************************************************
|
|
* Prompt user to validate the Process Specification Stage Instructions *
|
|
************************************************************************
|
|
PSNo = {PROD_SPEC_ID}
|
|
VerInst = XLATE('PRS_STAGE',PSNo:'*QA',PRS_STAGE_INST$,'X')
|
|
VerInst = TRIM(VerInst)
|
|
IF (LEN(VerInst) > 5) THEN
|
|
ErrorMsg = 'RDS Verification: Special engineering instructions must be acknowledged.'
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
**************************
|
|
* Apply User's Signature *
|
|
**************************
|
|
|
|
SigDt = OCONV( Date(), 'D2/' )
|
|
|
|
SigTm = Time()
|
|
SigTmPlusOne = OCONV(SigTm + 1, 'MTS' )
|
|
SigTmPlusTwo = OCONV(SigTm + 2, 'MTS' )
|
|
SigTmPlusFive = OCONV(SigTm + 5, 'MTS' )
|
|
SigTmPlusTen = OCONV(SigTm + 10, 'MTS' )
|
|
SigTm = OCONV(SigTm,'MTS')
|
|
SigBy = Username
|
|
WOStep = {WO_STEP}
|
|
|
|
EventParms = ''
|
|
EventCnt = 1
|
|
|
|
IF ReactorType NE 'EPP' THEN
|
|
|
|
ToolID = ''
|
|
WHCd = 'CR'
|
|
LocCD = 'QA'
|
|
Tag = ''
|
|
|
|
;* 4/30/2013 JCH added parms for merging of two methods
|
|
owmParms = WONo:@RM:CassNo:@RM:WOStep:@RM:'QA':@RM:Username:@RM:SigDt:' ':SigTm:@RM:ToolID:@RM:WHCd:@RM:LocCd:@RM:Tag
|
|
|
|
* obj_WO_Mat('SetSignature',owmParms) ; * * * * * S I G N A T U R E * * * * * *
|
|
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Error calling Obj_WO_Mat("SetSignature") within ':Service:'. Error code: ':errCode
|
|
Error_Services('Add', ErrorMsg)
|
|
return ;* Added 4/4/2013 JCH
|
|
END
|
|
|
|
EventParms<COL$LOG_FILE> = 'WO_MAT'
|
|
EventParms<COL$LOG_DTM> = SigDt:' ':SigTm
|
|
EventParms<COL$ACTION> = WOStep:'QA'
|
|
EventParms<COL$WH_CD> = 'CR'
|
|
EventParms<COL$LOC_CD> = 'QA'
|
|
EventParms<COL$WO_NOS> = WONo
|
|
EventParms<COL$CASS_NOS> = CassNo
|
|
EventParms<COL$USER_ID> = Username
|
|
EventParms<COL$TAGS> = ''
|
|
EventParms<COL$TOOL_ID> = ''
|
|
|
|
CONVERT @FM TO @RM IN EventParms
|
|
|
|
obj_WO_Mat_Log('Create',EventParms) ;* * * * * INV EVENT LOG * * * * *
|
|
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Error calling Obj_WO_Mat_Log("Create") within ':Service:'. Error code: ':errCode
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
IF MakeupBox = 1 THEN
|
|
|
|
EventParms<COL$LOG_FILE> = 'WO_MAT'
|
|
EventParms<COL$LOG_DTM> = SigDt:' ':SigTmPlusTwo
|
|
EventParms<COL$ACTION> = 'RTU'
|
|
EventParms<COL$WH_CD> = 'CR'
|
|
EventParms<COL$LOC_CD> = 'MU'
|
|
EventParms<COL$WO_NOS> = WONo
|
|
EventParms<COL$CASS_NOS> = CassNo
|
|
EventParms<COL$USER_ID> = Username
|
|
EventParms<COL$TAGS> = ''
|
|
EventParms<COL$TOOL_ID> = ''
|
|
|
|
CONVERT @FM TO @RM IN EventParms
|
|
|
|
obj_WO_Mat_Log('Create',EventParms) ;* * * * * INV EVENT LOG * * * * *
|
|
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Error calling Obj_WO_Mat_Log("Create") within ':Service:'. Error code: ':errCode
|
|
Error_Services('Add', ErrorMsg)
|
|
return
|
|
END
|
|
|
|
END
|
|
|
|
END ;* End of check for non EpiPRO reactor
|
|
|
|
{SUP_VER_SIG} = Username
|
|
{SUP_VER_SIG_DATE} = IConv(SigDt, 'D')
|
|
{SUP_VER_SIG_TIME} = IConv(SigTmPlusOne, 'MT')
|
|
|
|
/* Sync up the RDS record with WO_MAT */
|
|
Database_Services('WriteDataRow', 'RDS', RDSNo, @Record, True$, False$, False$)
|
|
|
|
WOMatKey = WONo:'*':CassNo
|
|
WOMatRec = XLATE('WO_MAT',WOMatKey,'','X')
|
|
WMRDSNos = WOMatRec<WO_MAT_RDS_NO$>
|
|
|
|
LOCATE RDSNo IN WMRDSNos USING @VM SETTING Pos THEN
|
|
NextRDSNo = WMRDSNos<1,Pos+1>
|
|
|
|
IF NextRDSNo NE '' THEN
|
|
CurrWfrQty = obj_WO_Mat('CurrWaferCnt',WOMatKey:@RM:WOMatRec)
|
|
Set_Status(0)
|
|
obj_RDS('SetSchedWfrQty',NextRDSNo:@RM:CurrWfrQty)
|
|
IF Get_Status(errCode) THEN
|
|
ErrorMsg = 'Error calling Obj_RDS("SetSchedWfrQty") within ':Service:'. Error code: ':errCode
|
|
Error_Services('Add', ErrorMsg)
|
|
end
|
|
END
|
|
END
|
|
|
|
* Added 08/05/2013 JCH
|
|
|
|
WOMatKey = WONo:'*':CassNo
|
|
OrgMUPart = XLATE('WO_MAT',WOMatKey,'MU_PART_NO','X')
|
|
NewMUPart = XLATE('WO_MAT',WOMatKey,'MU_PART_NO','X')
|
|
|
|
IF NewMUPart NE OrgMUPart THEN
|
|
|
|
IndexTransactionRow = 'MU_PART_NO':@FM:WOMatKey:@FM:OrgMUPart:@FM:NewMUPart:@FM
|
|
|
|
OPEN "!WO_MAT" TO BangTable THEN
|
|
|
|
LOCK BangTable, 0 THEN
|
|
|
|
READ PendingTrans FROM BangTable, 0 ELSE PendingTrans = '0':@FM
|
|
|
|
PendingTrans := IndexTransactionRow
|
|
|
|
WRITE PendingTrans ON BangTable, 0 ELSE
|
|
ErrorMsg = 'Unable to write index transaction to !WO_MAT. ':WOMatKey
|
|
Error_Services('Add', ErrorMsg)
|
|
END
|
|
UNLOCK BangTable, 0 ELSE
|
|
ErrorMsg = 'Unable to Open !WO_MAT to add index transaction. ':WOMatKey
|
|
Error_Services('Add', ErrorMsg)
|
|
end
|
|
END ELSE
|
|
ErrorMsg = 'Unable to Lock !WO_MAT to add index transaction. ':WOMatKey
|
|
Error_Services('Add', ErrorMsg)
|
|
END
|
|
END ELSE
|
|
ErrorMsg = 'Unable to Open !WO_MAT to add index transaction. ':WOMatKey
|
|
Error_Services('Add', ErrorMsg)
|
|
END
|
|
END ;* End of check for changed index value
|
|
|
|
* End of 10/14/2010 update
|
|
end else
|
|
ErrorMsg = 'Error reading RDS record: ':RDSNo:' in service: ':Service
|
|
Error_Services('Add', ErrorMsg)
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Internal GoSubs
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ClearCursors:
|
|
|
|
For counter = 0 to 8
|
|
ClearSelect counter
|
|
Next counter
|
|
|
|
return
|
|
|