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 11, 12, 13, and 14 are not significant for calculation purposes per management Until Index GT 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 = Time() RequestRow = Date() RequestRow = 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 = Response<1> * ROTRRec = Response<2> * ROTRRec = Response<3> * ROTRRec = Response<4> * ROTRRec = Response<5> * Database_Services('WriteDataRow', 'ROTR', LWICINo, ROTRRec, True$, False$, True$) CIRec = Response<1> CIRec = Response<2> CIRec = Response<3> CIRec = Response<4> CIRec = Response<5> // Set ROTR Update Flag to trigger CLEAN_INSP_ACTIONS routine (via BASE_MFS). // That routine will recalculate the final ROTR status. * CIRec = 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 RDSCassNo = RDSRec ReactorKey = RDSRec CurrPSN = RDSRec ReactorRec = Database_Services('ReadDataRow', 'REACTOR', ReactorKey) // These next six variables are set by engineering per reactor. XPrevRuns = ReactorRec NCRLimit = ReactorRec ZeroNCRMin = ReactorRec UCLExceededLimit = ReactorRec StopRDS = ReactorRec ROTRMaintReset = ReactorRec 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 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 SpecRecipes = CIRec // Updated code to look at the original scanned SOD average value before we remove the failed // wafers within CLEAN_INSP_ACTIONS. ScanSODAvgs = CIRec SpecSODAvgs = CIRec OverrideSigs = CIRec ROTRReasons = CIRec 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 = 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 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 SpecRecipes = CIRec // Updated code to look at the original scanned SOD average value before we remove the failed // wafers within CLEAN_INSP_ACTIONS. ScanSODAvgs = CIRec SpecSODAvgs = CIRec OverrideSigs = CIRec ROTRReasons = CIRec 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 = 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 = 0 ReactorRec = '' ReactorRec = '' 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 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 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 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 = 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 If ResponseDate NE '' then UCL = RequestRow 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 If TencorRecipe NE '' then RequestRow = QA_Services('GetUCL', RDSNo, TencorRecipe) end else RequestRow = QA_Services('GetUCL', RDSNo) end RequestRow = Date() RequestRow = 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 = RDSNo RequestRow = WaferNo RequestRow = TencorRecipe RequestRow = 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 = RDSNo RequestRow = WaferNo RequestRow = TencorRecipe RequestRow = 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 If ResponseDate NE '' then ResponseBody = RequestRow 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 WaferNo = RequestRow TencorRecipe = RequestRow ReturnPDF = RequestRow 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 = ResponseBody RequestRow = Date() RequestRow = 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 WaferNo = RequestRow TencorRecipe = RequestRow ReturnPDF = RequestRow 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 = ResponseBody RequestRow = Date() RequestRow = 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 = 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 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 = "Building ROTR Impact Analysis Report..." Def = "GC" Def = ReactorCnt Def = 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 = Reactor ReactorBlockArray = '' ; // Reactor Load Blocked ReactorBlockArray = '' ; // ROTR Failure Reason ReactorBlockArray = 0 ; // Num Runs requiring 100% scans ReactorBlockArray = 0 ; // NCRCount ReactorBlockArray = 0 ; // ZeroNCRCount ReactorBlockArray = 0 ; // UCLExceededCount ReactorBlockArray = 0 ; // Run Count ReactorBlockArray = 0 ; // Num Override Sigs (blocking + non-blocking events) ReactorBlockArray = 0 ; // Num Block Override Sigs (e.g. NCR Limit Exceeded) ReactorBlockArray = 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 ScanSigs = CIRec // Get the specifications SpecRecipes = CIRec SpecDefects = CIRec ; // Spec USL SpecHazes = CIRec // Get the scanned values ScanRecipes = CIRec ScanDefectsAvg = CIRec ScanDefects = CIRec ScanHazes = CIRec // Get the individual SOD values per wafer. SODPerWafer = CIRec // Get the fail scan flags so that we can get/set them as necessary. FailScanFlags = CIRec ScanMismatch = CIRec NumFailedWfrs = CIRec 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 = NCRCount ReactorBlockArray = ZeroNCRCount ReactorBlockArray = UCLExceededCount ReactorBlockArray = RunCount ReactorBlockArray = NumOverrideSigs ReactorBlockArray = NumBlockOverrideSigs ReactorBlockArray = 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 = 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 = 'Yes' ReactorBlockArray = 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 ReactorBlockArray = 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 ReactorBlockArray = 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 = Reactor ReactorBlockArray = 'N/A' ; // ROTR Status end If (Server NE 'MESST5201') and (Server NE 'MESST5202') then LogData = '' LogData<1> = ReactorBlockArray ; // Reactor No LogData<2> = ReactorBlockArray ; // Reactor Load Blocked LogData<3> = ReactorBlockArray ; // ROTR Failure Reason LogData<4> = ReactorBlockArray ; // Num Runs requiring 100% scans LogData<5> = ReactorBlockArray ; // NCRCount LogData<6> = ReactorBlockArray ; // ZeroNCRCount LogData<7> = ReactorBlockArray ; // UCLExceededCount LogData<8> = ReactorBlockArray ; // Run Count LogData<9> = ReactorBlockArray ; // Num Override Sigs LogData<10> = ReactorBlockArray ; // Num Blocking Override Sigs ReactorYield = ReactorBlockArray ; // 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 = '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) 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) ;* 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 CassNo = RDSRec WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X') PreEpiSig = RDSRec ReactorType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X') SchedQty = RDSRec 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 CassNo = RDSRec PSNo = RDSRec 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 = Reactor RDSRec = 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 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 SigAction = WOStep:'VER' LOCATE SigAction IN WOMatSigProfile USING @VM SETTING Pos THEN WOMatSig = WOMatRec CurrDTM = OCONV(WOMatRec,'DT/4^HS') END ELSE WOMatSig = '' END RDSSig = PreEpiSig BEGIN CASE CASE (WOMatSig EQ '') AND (RDSSig NE '') * Signature missing on WO_MAT SigDate = RDSRec SigTime = RDSRec 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 = WOMatSig RDSRec = IConv(CurrDate, 'D') RDSRec = 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 = Username RDSRec = IConv(SigDate, 'D') RDSRec = 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 CassNo = RDSRec Reactor = RDSRec If Reactor EQ '' AND Assigned(ReactNoOpt) AND PreFlag EQ True$ then Reactor = ReactNoOpt end PSN = RDSRec LoadSig = RDSRec 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 ParamOutOfSpec = Xlate('RDS', RDSNo, 'PARAM_OUT_OF_SPEC', 'X') Supplement = RDSRec 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 QualMode = ReactLLRec 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 = 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 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 SigTime = RDSrec 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 UnloadExtra1Signature = RDSRec LoadExtra1Signature = RDSRec UnloadExtra2Signature = RDSRec LoadExtra2Signature = RDSRec StandardUnloadSignature = RDSRec 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 UnloadExtra1Signature = RDSRec LoadExtra1Signature = RDSRec UnloadExtra2Signature = RDSRec LoadExtra2Signature = RDSRec StandardUnloadSignature = RDSRec 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 UnloadExtra1Signature = RDSRec LoadExtra1Signature = RDSRec UnloadExtra2Signature = RDSRec LoadExtra2Signature = RDSRec StandardUnloadSignature = RDSRec 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 UnloadExtra1Signature = RDSRec LoadExtra1Signature = RDSRec UnloadExtra2Signature = RDSRec LoadExtra2Signature = RDSRec StandardUnloadSignature = RDSRec 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 = WaferQty RDSRec = LLSide Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$) WONo = RDSRec CassNo = RDSRec Reactor = RDSRec PSN = RDSRec 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 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 SigAction = WOStep:'LOAD' LOCATE SigAction IN WOMatSigProfile USING @VM SETTING Pos THEN WOMatSig = WOMatRec CurrDTM = OCONV(WOMatRec,'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 SigTime = RDSRec 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 = WOMatSig RDSRec = IConv(CurrDate, 'D') RDSRec = 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 = IdleTime END END ELSE * Update REACT_STATUS record AllInCassettes = RDSRec 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 = IdleTime END ;* Added Else branch 3/22/2012 JCH END ;* End of check for valid load signature RDSRec = Username RDSRec = IConv(SigDt, 'D') RDSRec = 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 CassNo = RDSRec WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X') RDSSig = RDSRec RDSReactor = RDSRec 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 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 USING @VM SETTING Pos THEN * CINo = ReactRunRec * Actions = obj_Clean_Insp('GetActions',CINo) * LOCATE 'Inspection' IN Actions USING @FM SETTING Pos THEN * IF Actions = '' 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 <> '' THEN IF RDSRec = '' 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 <> '' THEN IF RDSRec = '' 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, 'D'):' ':OConv(RDSRec, 'MT'), 'DT') LoadEx2DTM = IConv(OConv(RDSRec, 'D'):' ':OConv(RDSRec, 'MT'), 'DT') LoadEx3DTM = IConv(OConv(RDSRec, 'D'):' ':OConv(RDSRec, 'MT'), 'DT') UnloadEx1DTM = IConv(OConv(RDSRec, 'D'):' ':OConv(RDSRec, 'MT'), 'DT') UnloadEx2DTM = IConv(OConv(RDSRec, 'D'):' ':OConv(RDSRec, '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 UnloadExtra1Signature = RDSRec LoadExtra1Signature = RDSRec UnloadExtra2Signature = RDSRec LoadExtra2Signature = RDSRec StandardUnloadSignature = RDSRec 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 UnloadExtra1Signature = RDSRec LoadExtra1Signature = RDSRec UnloadExtra2Signature = RDSRec LoadExtra2Signature = RDSRec StandardUnloadSignature = RDSRec 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 UnloadExtra1Signature = RDSRec LoadExtra1Signature = RDSRec UnloadExtra2Signature = RDSRec LoadExtra2Signature = RDSRec StandardUnloadSignature = RDSRec 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 UnloadExtra1Signature = RDSRec LoadExtra1Signature = RDSRec UnloadExtra2Signature = RDSRec LoadExtra2Signature = RDSRec StandardUnloadSignature = RDSRec 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 CassNo = RDSRec WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X') RDSSig = RDSRec ReactType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X') IF RDSSig = '' THEN WOMatRec = XLATE('WO_MAT',WONo:'*':CassNo,'','X') LOCATE WOStep:'UNLOAD' IN WOMatRec USING @VM SETTING Pos THEN CurrSig = WOMatRec IF CurrSig NE '' THEN * Already signed off without saving the RDS signatures CurrSigDTM = WOMatRec CurrSigDate = CurrSigDTM[1, 'F.'] CurrSigTime = CurrSigDTM[-1, 'B.'] RDSRec = CurrSig RDSRec = CurrSigDate RDSRec = 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 CassNo = RDSRec WOStep = Xlate('RDS', RDSNo, 'WO_STEP', 'X') ReactorType = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X') UnloadDTM = SigDt:' ':SigTm Reactor = RDSRec 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 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 = SigBy RDSRec = IConv(SigDt, 'D') RDSRec = 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 * * Set Current Reactor Information on the RDS record ** * * Flag Graphite records if this PSN was using 1% Boron dopant * * PSNo = RDSRec 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 = CurrInjSetting RDSRec = TubeID RDSRec = TubeGrade RDSRec = 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 CassNo = RDSRec Reactor = RDSRec 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 = 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 SigAction = WOStep:'QA' LOCATE SigAction IN WOMatSigProfile USING @VM SETTING Pos THEN IF WOMatRec NE '' THEN CurrSig = WOMatRec CurrDTM = OCONV(WOMatRec,'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,@VM) + (WOMatRec NE '') FOR Index = 1 TO ProfileSignatureCnt-1 /* Minus -1 = Do not look at the last signature, which is Final QA */ IF (WOMatRec[1,1] = WOStep) THEN SigProfile = WOMatRec Signature = WOMatRec SigDTM = WOMatRec SigVer = WOMatRec 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 WaferFlatLengthMins = Oconv(CompanyRow, 'MD1') WaferFlatLengthMaxes = Oconv(CompanyRow, '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 EQ '') and (MetMax EQ '') Null Case (MetResult EQ '') ErrorMsg = 'Process Error: Required QA Metrology results have not been entered.' Error_Services('Add', ErrorMsg) return Case (MetResult LT MetMin) or (MetResult GT MetMax) ErrorMsg = 'Process Error: One or more QA Metrology results is out of specification.' Error_Services('Add', ErrorMsg) return Case (MetResult NE '') and (MetSig 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 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 MakeupBox = WOMatRec 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 = 'WO_MAT' EventParms = SigDt:' ':SigTm EventParms = WOStep:'QA' EventParms = 'CR' EventParms = 'QA' EventParms = WONo EventParms = CassNo EventParms = Username EventParms = '' EventParms = '' 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 = 'WO_MAT' EventParms = SigDt:' ':SigTmPlusTwo EventParms = 'RTU' EventParms = 'CR' EventParms = 'MU' EventParms = WONo EventParms = CassNo EventParms = Username EventParms = '' EventParms = '' 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 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