3905 lines
186 KiB
Plaintext
3905 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|