open-insight/LSL2/STPROC/QA_SERVICES.txt
Infineon\StieberD 24a246a99b added logic to RDS_ACTIONS to restore load signatures if erased unexpectedly
refactored solution to leverage the LOT and LOT_EVENT tables

minor fix
2024-11-05 17:51:13 -07:00

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