Files
open-insight/LSL2/STPROC/SCAN_SERVICES.txt
Ouellette Jonathan (CSC FI SPS MESLEO) baa9af3e1a Merged PR 27477: Removed Error setting if ScanNotAcceptableReason is due to RDS Layer Params n...
Removed Error setting if ScanNotAcceptableReason is due to RDS Layer Params needing to be verified. Also added this same check for lots at the VER stage

Related work items: #330648
2025-10-01 16:46:26 +00:00

2428 lines
117 KiB
Plaintext

Function Scan_Services(@Service, @Params)
/***********************************************************************************************************************
This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
permission from SRP Computer Solutions, Inc.
Name : Scan_Services
Description : Handler program for all Scan services.
Notes :
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)
09/07/18 dmb Original programmer.
06/13/24 djm Add new stage-specific supplement system.
***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler
$insert LOGICAL
$insert SERVICE_SETUP
$insert SCANS_EQUATES
$insert RDS_EQUATES
$insert RDS_LAYER_EQUATES
$insert DICT_EQUATES
$insert WO_MAT_EQUATES
$insert OVERRIDE_LOG_EQUATES
$insert REACTOR_EQUATES
$INSERT REACT_LL_EQUATES
$insert RLIST_EQUATES
$Insert ROTR_OVERRIDE_COMMENT_OPTIONS_EQUATES
$Insert SUPPLEMENTS_EQUATES
$Insert Lot_Equates
$Insert Test_Run_Type_Equates
Common /ScanServices/ NotAcceptableReasons@, Unused2@, Unused3@, Unused4@, Unused5@, Unused6@, Unused7@, Unused8@
Declare Function Scan_Services, Memory_Services, Database_Services, SRP_JSON, RTI_CreateGUID, Rds_Services, Datetime
Declare Function QA_Services, Error_Services, Security_Services, SRP_Array, obj_WO_Mat, Memberof, Override_Log_Services
Declare Function Keyboard_Sim_Services, Environment_Services, Logging_Services, Reactor_Services, Supplement_Services
Declare function Test_Run_Services, Lot_Services, WO_MAT_Services, Reactor_Log_Services, Schedule_Services
Declare function GetTickCount
Declare Subroutine Scan_Services, Memory_Services, Database_Services, SRP_JSON, QA_Services, Error_Services
Declare Subroutine obj_WO_Mat_Log, Tool_Parms_Services, RDS_Services, Logging_Services, Supplement_Services
Declare Subroutine Test_Run_Services, WO_MAT_Services, Reactor_Log_Services, Schedule_Services, Mona_Services
Declare Subroutine Dialog_Response_Log_Services
Equ CRLF$ to \0D0A\
LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\ScanAPI'
LogDate = Oconv(Date(), 'D4/')
LogTime = Oconv(Time(), 'MTS')
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Load-Unload Log.csv'
Headers = 'Logging DTM' : @FM : 'RDS Key ID' : @FM : 'User' : @FM : 'CurrStage' : @FM : 'Scan ID' : @FM : 'Action' : @FM : 'Result'
objLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LoggingDTM = LogDate : ' ' : LogTime ; // Logging DTM
IsProd = Environment_Services('IsProd')
If IsProd EQ True$ then
MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_SCANSERVICES'
end else
MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_DEV_SCANSERVICES'
end
GoToService else
Error_Services('Add', Service : ' is not a valid service request within the ' : ServiceModule : ' module.')
end
Return Response OR ''
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options BOOLEAN = True$, False$
Options SCAN_TYPES = 'LOCATION', 'TOOL'
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Service AddNotAcceptableReason(Reason)
If Len(NotAcceptableReasons@) then
NotAcceptableReasons@ := @FM : Reason
end else
NotAcceptableReasons@ = Reason
end
end service
//----------------------------------------------------------------------------------------------------------------------
// GetNotAcceptableReason
//
// Returns the most current not acceptable reason.
//----------------------------------------------------------------------------------------------------------------------
Service GetNotAcceptableReason()
Response = NotAcceptableReasons@[-1, 'B' : @FM]
End Service
//----------------------------------------------------------------------------------------------------------------------
// GetNotAcceptableReasons
//
// Returns the stack of not acceptable reasons. This will be @FM delimited.
//----------------------------------------------------------------------------------------------------------------------
Service GetNotAcceptableReasons()
Response = NotAcceptableReasons@
End Service
//----------------------------------------------------------------------------------------------------------------------
// ClearNotAcceptableReasons
//
// Clears all not acceptable reasons.
//----------------------------------------------------------------------------------------------------------------------
Service ClearNotAcceptableReasons()
NotAcceptableReasons@ = ''
End Service
//----------------------------------------------------------------------------------------------------------------------
// HasNotAcceptableReason
//
// Returns True if there is an error condition, False if there is no error condition. Caller will still need to use
// the GetMessage or GetMessages service to determine what the error is. The HasError service allows the caller to
// embed the Error_Services service call inside of a conditional statement like this:
//
// If Error_Services('HasError') then
// * An error has occured. Proceed accordingly.
// ErrorMessage = Error_Services('GetMessage')
// end else
// * No error has occured.
// end
//----------------------------------------------------------------------------------------------------------------------
Service HasNotAcceptableReason()
If Len(NotAcceptableReasons@) then
Response = True$
end else
Response = False$
end
End Service
//----------------------------------------------------------------------------------------------------------------------
// NoNotAcceptableReason
//
// Returns True if there are no error conditions, False if there is an error condition. This is the opposite of the
// HasError service and exists for improved readability.
//----------------------------------------------------------------------------------------------------------------------
Service NoNotAcceptableReason()
If Len(NotAcceptableReasons@) then
Response = False$
end else
Response = True$
end
End Service
//----------------------------------------------------------------------------------------------------------------------
// CreateScansRow
//
// Creates a new database row in the SCANS table. Retruns the Key ID to the SCANS table if successful.
//----------------------------------------------------------------------------------------------------------------------
Service CreateScansRow(ScanType=SCAN_TYPES)
ScanID = RTI_CreateGUID()
If ScanID NE '' then
ScansRow = ''
ScansRow<SCANS.CREATED_DATE$> = Date()
ScansRow<SCANS.CREATED_TIME$> = Time()
ScansRow<SCANS.SCAN_TYPE$> = ScanType
ScansRow<SCANS.ACCEPTED$> = False$
Database_Services('WriteDataRow', 'SCANS', ScanID, ScansRow, True$, False$, True$)
end else
Error_Services('Add', 'ScanID was not created in the ' : Service : ' service.')
end
Response = ScanID
end service
//----------------------------------------------------------------------------------------------------------------------
// GetScansRow
//
// Returns the database row from the SCANS table for the indicated Scan ID. The default format is MultiValue.
//----------------------------------------------------------------------------------------------------------------------
Service GetScansRow(ScanID, ReturnJSON)
StartTick = GetTickCount()
MetricName = 'GetScansRow'
ScansRow = ''
ErrMsg = ''
If ScanID NE '' then
ScansRow = Database_Services('ReadDataRow', 'SCANS', ScanID)
If ReturnJSON EQ True$ then
ScansRow = Scan_Services('ConvertMVScanToJSON', ScanID, ScansRow)
end
end else
ErrMsg = 'ScanID argument was missing in the ' : Service : ' service.'
end
Response = ScansRow
EndTick = GetTickCount()
Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
If ErrMsg NE '' then Error_Services('Add', ErrMsg)
end service
//----------------------------------------------------------------------------------------------------------------------
// SetScansRow
//
// Returns the database row from the SCANS table for the indicated Scan ID. The default format is MultiValue.
//----------------------------------------------------------------------------------------------------------------------
Service SetScansRow(ScanID, ScansRow)
StartTick = GetTickCount()
MetricName = 'SetScansRow'
ErrMsg = ''
If (ScanID NE '') AND (ScansRow NE '') then
Database_Services('WriteDataRow', 'SCANS', ScanID, ScansRow)
end else
ErrMsg = 'ScanID or ScansRow argument was missing in the ' : Service : ' service.'
end
EndTick = GetTickCount()
Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
If ErrMsg NE '' then Error_Services('Add', ErrMsg)
end service
//----------------------------------------------------------------------------------------------------------------------
// ProcessScanData
//
// Processes new scan data and updates the SCANS database row for the indicated Scan ID. This service should parse the
// scan data and identify its intended field and then evaluate if the scan data is valid. Note: just because the scan
// resource is not ready to be accepted, it does not mean this scan data is invalid. Each scan data will have to be
// evaluated on a case by case basis.
//----------------------------------------------------------------------------------------------------------------------
Service ProcessScanData(ScanID, ScanJSON)
StartTick = GetTickCount()
MetricName = 'ProcessScanData'
ErrMsg = ''
If ( (ScanID NE '') AND (ScanJSON NE '') ) then
hScanJSON = ''
ParseResponse = SRP_JSON(hScanJSON, 'PARSE', ScanJSON)
If (ParseResponse EQ '') then
ScanData = SRP_JSON(hScanJSON, 'GetValue', 'scanData')
// Code 3of9 encodes the asterisk and underscore characters. These need to be decoded.
If ScanData NE '' then Scan_Services('DecodeScanData', ScanData)
// Confirm that this is a valid Scan ID.
ScansRow = Database_Services('ReadDataRow', 'SCANS', ScanID)
If Error_Services('NoError') then
// Use the system variables to evaluate calculated columns easily.
@DICT = Database_Services('GetTableHandle', 'DICT.SCANS')
@ID = ScanID
@RECORD = ScansRow
Action = ''
ColumnIndex = ''
ColumnValue = ''
// Identify the scan data based on the data identifier prefix (if any). Otherwise, assume this is a
// type of cassette ID (i.e., RDS or WMO).
Begin Case
//DPC - 3/18/20 - added validation to allow 1T scans to be interpreted as RDS or Supplier Lot code
//1T could be supplier lot (Global Wafer and Sumco Japan) or could be IFX internal lot (RDS)
//2T is always supplier lot
Case ( (ScanData[1, 2] EQ '1T') or (ScanData[1, 2] EQ '2T') )
// Supplier lot scan.
Convert '_' to '-' in ScanData
LotID = ScanData[3, 999]
Begin Case
Case ( RowExists('RDS', LotId) EQ True$ AND ScanData[1, 2] EQ '1T')
ScansRow<SCANS.CASSETTE_IDS$> = LotID
Case ScanData[1, 4] EQ '1TTW'
ValidTWLot = False$
If RowExists('LOT', LotId) then
TWLots = ScansRow<SCANS.TW_LOT_ID$>
Locate LotId in TWLots using @VM setting twPos then
ErrMsg = LotID : ' has already been added as a test wafer lot being used.'
end else
ScansRow<SCANS.TW_LOT_CHANGED$> = True$
LotCurrentOperation = Lot_Services('GetLotCurrOperationName', LotId)
If LotCurrentOperation NE 'TW_CREATE' AND LotCurrentOperation NE 'TW_CLOSE' then
LotCurrWfrQty = Database_Services('ReadDataColumn', 'LOT', LotId, LOT_WAFER_QTY$, True$, 0, False$)
If LotCurrWfrQty GT 0 then
If ScansRow<SCANS.TW_LOT_ID$> EQ '' AND ScansRow<SCANS.CASSETTE_IDS$> NE '' then
ScansRow<SCANS.TEST_RUN_TYPE_ID$> = 4;//Standard Sampling
end
ScansRow<SCANS.TW_LOT_ID$, -1> = LotID
ThisScanTWLots = ScansRow<SCANS.TW_LOT_ID$>
Locate LotID in ThisScanTWLots using @VM setting twPOS then
ScansRow<SCANS.TW_LOT_QTY$, twPos> = 1
end
end else
ErrMsg = LotID : ' does not have enough wafers to be used.'
end
end else
ErrMsg = LotID : ' is not at a valid operation for use.'
end
end
end else
ErrMsg = LotID : ' is not a valid test wafer lot ID.'
end
Case Otherwise$
ScansRow<SCANS.SUPPLIER_LOT$> = LotID
End Case
Case ScanData[1, 5] EQ 'RESET'
ScansRow<SCANS.SCAN_TYPE$> = 'RESET'
ScansRow<SCANS.EMPLOYEE_ID$> = ''
ScansRow<SCANS.LOCATION_ID$> = ''
ScansRow<SCANS.TOOL_ID$> = ''
ScansRow<SCANS.LOAD_LOCK$> = ''
Case ScanData[1, 2] EQ '1H'
// Employee ID scan. Set the EMPLOYEE_ID column and check the
// EMPLOYEE_AUTHORIZED and EMPLOYEE_NOT_AUTHORIZED_REASON column values to determine if the
// scan data is valid.
{EMPLOYEE_ID} = ScanData[3, 999]
EmployeeAuthorized = {EMPLOYEE_AUTHORIZED}
EmployeeActive = Xlate('LSL_USERS', {EMPLOYEE_ID}, 'ACTIVE', 'X')
ScansRow<SCANS.AUTHENTICATED$> = 0
Begin Case
Case EmployeeAuthorized NE True$
// Regardless of the not authorized reason, the scan data will be considered invalid.
EmployeeNotAuthorizedReason = {EMPLOYEE_NOT_AUTHORIZED_REASON}
ErrMsg = EmployeeNotAuthorizedReason
Case EmployeeActive NE True$
ErrMsg = 'Inactive employee.'
Case Otherwise$
ScansRow<SCANS.EMPLOYEE_ID$> = {EMPLOYEE_ID}
Username = {EMPLOYEE_ID}
Member = False$
Groups = 'LEAD':@VM:'SUPERVISOR':@VM:'ENGINEER':@VM:'ENG_TECH':@VM:'ROTR_OVERRIDE'
For each Group in Groups using @VM
Member = MemberOf(Username, Group)
Until Member EQ True$
Next Group
ScansRow<SCANS.IS_AUTHORIZED_TO_OVERRIDE_ROTR$> = Member
ScansRow<SCANS.EMPLOYEE_CHANGED$> = True$
End Case
Case ScanData[1, 3] EQ '10S'
// Tool ID scan. A determination needs to be made to see if this is a transfer tool or a process
// tool. Transfer tools do not require employee authorization, but process tools do.
//
// If this is a non-existent tool or if the current Employee ID is not authorized
// will the scan data be considered invalid. However, if there is no Employee ID, then do not
// use this to invalidate the scan data.
ScanData = ScanData[4, 999]
ToolID = ScanData[1, '.']
LoadLock = ScanData[Col2() + 1, 1]
ToolRow = Database_Services('ReadDataRow', 'TOOL', ToolID)
If Error_Services('NoError') then
ScansRow<SCANS.SCAN_TYPE$> = 'TOOL'
ToolType = ToolRow<2>
Begin Case
Case ToolType _EQC 'Reactor'
// Clear the Location ID and set the Tool ID column to determine if the current Employee ID
// is authorized.
{LOCATION_ID} = ''
{TOOL_ID} = ToolID
EmployeeAuthorized = {EMPLOYEE_AUTHORIZED}
If (EmployeeAuthorized NE True$) AND ({EMPLOYEE_ID} NE '') then
// Regardless of the not authorized reason, the scan data will be considered invalid.
EmployeeNotAuthorizedReason = {EMPLOYEE_NOT_AUTHORIZED_REASON}
ErrMsg = EmployeeNotAuthorizedReason
end else
* If LoadLock EQ '' then LoadLock = 'NA' ; // NA means Not Applicable.
ReactorID = ToolID[2, 999]
LoadLockReq = Xlate('REACTOR', ReactorID, 'LOAD_LOCK_REQ', 'X')
//LoadLockReq = Xlate('REACTOR', ReactorID, REACTOR_PICK_PLACE$, 'X')
Begin Case
Case (LoadLockReq EQ True$)
If (LoadLock NE '') then
If ( (LoadLock EQ 'L') or (LoadLock EQ 'R') ) then
If ScansRow<SCANS.LOCATION_ID$> NE '' then
ScansRow<SCANS.CASSETTE_IDS$> = ''
end else
ScansRow<SCANS.CASSETTE_IDS$> = ScansRow<SCANS.CASSETTE_IDS$, 1> ; // Make sure only the first Cassette ID is tracked.
end
ScansRow<SCANS.LOCATION_ID$> = '' ; // Make sure the Location ID is cleared.
ScansRow<SCANS.TOOL_ID$> = ToolID
ScansRow<SCANS.LOAD_LOCK$> = LoadLock
end else
ErrMsg = 'Invalid load lock value "':LoadLock:'".'
end
end else
ErrMsg = 'A load lock side "L" or "R" is required for reactor ':ReactorID:'.'
end
Case (LoadLockReq EQ False$)
If ScansRow<SCANS.LOCATION_ID$> NE '' then
ScansRow<SCANS.CASSETTE_IDS$> = ''
end else
ScansRow<SCANS.CASSETTE_IDS$> = ScansRow<SCANS.CASSETTE_IDS$, 1> ; // Make sure only the first Cassette ID is tracked.
end
ScansRow<SCANS.LOCATION_ID$> = '' ; // Make sure the Location ID is cleared.
ScansRow<SCANS.TOOL_ID$> = ToolID
ScansRow<SCANS.LOAD_LOCK$> = LoadLock
End Case
end
Case ToolType _EQC 'Transfer'
ErrMsg = 'Tool type ':ToolType:' is not currently supported by the barcode application.'
// Code below may be implemented down the road.
// This is a transfer tool. Just update the scan resource.
* ScansRow<SCANS.LOCATION_ID$> = '' ; // Make sure the Location ID is cleared.
* ScansRow<SCANS.CASSETTE_IDS$> = ScansRow<SCANS.CASSETTE_IDS$, 1> ; // Make sure only the first Cassette ID is tracked.
* ScansRow<SCANS.TRANSFER_TOOL_ID$> = ToolID
Case Otherwise$
ErrMsg = 'Tool type ':ToolType:' is not currently supported by the barcode application.'
End Case
end else
ErrMsg = ToolID : ' is not a valid tool ID.'
end
Case ScanData[1, 2] EQ '1L'
// Location ID scan. Only if this is a non-existent location will the scan data be considered
// invalid.
LocationID = ScanData[3, 999]
Convert '.' to '*' in LocationID
LocationRow = Database_Services('ReadDataRow', 'LOCATION', LocationID)
Convert '*' to '.' in LocationID
If Error_Services('NoError') then
ScansRow<SCANS.SCAN_TYPE$> = 'LOCATION'
If ScansRow<SCANS.TOOL_ID$> NE '' then ScansRow<SCANS.CASSETTE_IDS$> = ''
ScansRow<SCANS.TOOL_ID$> = '' ; // Make sure the Tool ID is cleared.
ScansRow<SCANS.LOAD_LOCK$> = '' ; // Make sure the Tool Load Lock is cleared.
ScansRow<SCANS.TRANSFER_TOOL_ID$> = '' ; // Make sure the Transfer Tool ID is cleared.
ScansRow<SCANS.LOCATION_ID$> = LocationID
end else
ErrMsg = LocationID : ' is not a valid location ID.'
end
Case ScanData[1, 2] EQ '1B'
// Carrier/boat ID scan. Only if this is a non-existent carrier will the scan data be considered
// invalid.
ScanData = ScanData[3, 999]
PLNo = ScanData[1, '.']
BoatID = ScanData[Col2() + 1, '.']
If BoatID NE '' then
ScansRow<SCANS.LOCATION_ID$> = '' ; // Make sure the Location ID is cleared.
ScansRow<SCANS.CASSETTE_IDS$> = ScansRow<SCANS.CASSETTE_IDS$, 1> ; // Make sure only the first Cassette ID is tracked.
ScansRow<SCANS.BOAT_ID$> = BoatID
ScansRow<SCANS.PL_NUMBER$> = PLNo
end else
ErrMsg = ScanData : ' is not a valid boat ID.'
end
Case ScanData[1, 3] EQ 'PWD'
// Password/Signature scan. If the scan is not ready to be accepted or the password does
// not match the password of the employee ID scanned then this will be an invalid scan,
// otherwise the scan will be accepted.
Password = ScanData[4, 999]
If Password NE '' then
EmployeeID = ScansRow<SCANS.EMPLOYEE_ID$>
If EmployeeID NE '' then
Authenticated = Security_Services('AuthenticateLSLCredentials', EmployeeID, Password)
If Authenticated EQ True$ then
ScansRow<SCANS.AUTHENTICATED$> = 1
end else
ScansRow<SCANS.AUTHENTICATED$> = 0
ErrMsg = 'Invalid password for user ':EmployeeID:'.'
end
ScansRow<SCANS.EMPLOYEE_CHANGED$> = True$
end else
ErrMsg = 'An employee ID must be scanned before scanning a password.'
end
end else
ErrMsg = 'Invalid password scanned.'
end
Case ScanData[1, 8] EQ 'OVERRIDE'
Type = ScansRow<SCANS.OVERRIDE_TYPE$>
if {EMPLOYEE_ID} NE '' AND ScansRow<SCANS.AUTHENTICATED$> EQ 1 then
Username = {EMPLOYEE_ID}
Member = False$
Groups = 'LEAD':@VM:'SUPERVISOR':@VM:'ENGINEER':@VM:'ENG_TECH':@VM:'ROTR_OVERRIDE'
For each Group in Groups using @VM
Member = MemberOf(Username, Group)
Until Member EQ True$
Next Group
if Member EQ True$ then
IF Type EQ 'ROTR' then
Reactor = {TOOL_ID}[2,999]
RDSNo = {CASSETTE_IDS}
Comment = ScanData[13, 999]
//Perform the Override here
overrideLogTable = 'RDS':@VM:'REACTOR'
overrideLogKey = RDSNo:@VM:Reactor
overrideLogUser = {EMPLOYEE_ID}
overrideLogComment = Comment
overrideLogCategory = 'ROTR_BLOCK'
overrideLogCause = Xlate('REACTOR', Reactor, REACTOR_ROTR_STATUS_REASON$, 'X')
orKey = Override_Log_Services('Create', overrideLogTable, overrideLogKey, overrideLogUser, overrideLogComment, overrideLogCategory, overrideLogCause)
//Add override key to RDS
RDSRec = Xlate('RDS', RDSNo, '', 'X')
RDSRec = Insert(RDSRec, RDS_OVERRIDE_KEYS$, 1, 0, orKey)
//Clear Set ROTR status to Passing and clear reason
ReactorRec = Database_Services('ReadDataRow', 'REACTOR', Reactor, True$, 0, False$)
ReactorRec<REACTOR_ROTR_STATUS$> = 'P'
rotrStatusReason = ReactorRec<REACTOR_ROTR_STATUS_REASON$>
ReactorRec<REACTOR_PREVIOUS_ROTR_STATUS_REASON$> = rotrStatusReason
ReactorRec<REACTOR_ROTR_STATUS_REASON$> = ''
ReactorRec<REACTOR_PREVIOUS_ROTR_OVERRIDE_RDS$> = RDSNo
rotrOverrideCount = ReactorRec<REACTOR_ROTR_OVERRIDE_COUNT$>
ReactorRec<REACTOR_ROTR_OVERRIDE_COUNT$> = rotrOverrideCount + 1
Database_Services('WriteDataRow', 'REACTOR', Reactor, ReactorRec, True$, False$, True$)
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
ScansRow<SCANS.OVERRIDE_REQD$> = 0
ScansRow<SCANS.OVERRIDE_TYPE$> = ''
ScansRow<SCANS.OVERRIDE_REASON$> = ''
ScansRow<SCANS.EMPLOYEE_ID$>= ''
ScansRow<SCANS.AUTHENTICATED$> = False$
ScansRow<SCANS.IS_AUTHORIZED_TO_OVERRIDE_ROTR$> = False$
ScansRow<SCANS.EMPLOYEE_CHANGED$> = True$
Result = 'Override Performed successfully'
end
end else
ErrMsg = 'Selected Employee is not authorized to perform an override'
end
end
Case ScanData[1, 2] EQ '5T'
//Tencor Scan - Note this is only for loading cassette ID into Tencor tool
ScansRow<SCANS.SCAN_TYPE$> = 'TENCOR_LOAD'
ScansRow<SCANS.TOOL_ID$>= ScanData[3,99]
Case ScanData[1, 16] EQ 'TWQUANTITYUPDATE'
TWLot = Field(ScanData, '|', 2)
TWLotQuantity = Field(ScanData, '|', 3)
ThisScanTWLots = ScansRow<SCANS.TW_LOT_ID$>
Locate TWLot in ThisScanTWLots using @VM setting twPOS then
ScansRow<SCANS.TW_LOT_QTY$, twPos> = TWLotQuantity
ScansRow<SCANS.TW_LOT_CHANGED$> = True$
end
Case ScanData[1, 17] EQ 'TESTRUNTYPEUPDATE'
TestRunTypeId = Field(ScanData, '|', 2)
ScansRow<SCANS.TEST_RUN_TYPE_ID$> = TestRunTypeId
Case Len(ScanData) GE 22 and ScanData[1, 22] EQ 'LAST_CASSETTE_TEST_ACK'
RDSNo = {CASSETTE_IDS}
Reactor = {TOOL_ID}[2,999]
WO = Xlate('RDS', RDSNo, RDS_WO$, 'X')
CassNo = Xlate('RDS', RDSNo, 'RUN_ORDER_NUM', 'X')
WoMatKey = WO:'*':CassNo
WoMatRecord = Database_Services('ReadDataRow', 'WO_MAT', WoMatKey)
WoMatRecord<WO_MAT_LAST_CASSETTE_TEST_WAFER_ACKED$> = True$
Database_Services('WriteDataRow', 'WO_MAT', WoMatKey, WoMatRecord, True$, False$, False$)
ConfirmNote = 'Test wafer requirement acknowledged. [RDS:' : RDSNo : '], [WO:' : WO : '], [Cassette:' : CassNo : ']'
Reactor_Log_Services('AddComment', Reactor, ConfirmNote, {EMPLOYEE_ID})
Result = 'Test wafer requirement acknowledged'
Case ScanData EQ ''
// Most likely receiving RDS data, so nothing to do here.
Null
Case Otherwise$
// Assume this is intended to be a Cassette ID scan (either WMO or RDS). Only if this is a
// non-existent carrier will the scan data be considered invalid.
ColumnIndex = SCANS.CASSETTE_IDS$
CassetteID = ScanData
ValidCassetteID = False$ ; // Assume Cassette ID is not valid for now.
If Num(CassetteID) then
// Might be an RDS number.
RDSRow = Database_Services('ReadDataRow', 'RDS', CassetteID)
If Error_Services('NoError') then
ValidCassetteID = True$
end
end else
// Might be a WMO number.
If DCount(CassetteID, '.') EQ 3 then
// Passes the format test.
If ( (CassetteID[1, 1] EQ 'O') or (CassetteID[1, 1] EQ 'I') ) then
IOFlag = CassetteID[1, 1]
CassetteID = CassetteID[2, 999]
end else
IOFlag = ''
end
Convert '.' to '*' in CassetteID
WMOutRow = Database_Services('ReadDataRow', 'WM_OUT', CassetteID)
Convert '*' to '.' in CassetteID
If Error_Services('NoError') then
ValidCassetteID = True$
end
end
end
If ValidCassetteID EQ True$ then
// This is a valid Cassette ID that needs to be added to this resource. If the Tool
// ID is not populated, then append this Cassette ID to the array, otherwise replace
// the existing Cassette ID with this one.
If ScansRow<SCANS.TOOL_ID$> EQ '' then
// Only append if it doesn't already exists.
Locate CassetteID in ScansRow<SCANS.CASSETTE_IDS$> using @VM setting vPos else
ScansRow<SCANS.CASSETTE_IDS$, -1> = CassetteID
end
end else
ScansRow<SCANS.CASSETTE_IDS$> = CassetteID
end
end else
ErrMsg = CassetteID : ' is not a valid Cassette ID.'
end
End Case
If ErrMsg EQ '' then
// Process Scan row data here to determine the type of scan (i.e. Location, Pre-Epi + Load, or Unload).
ScanType = ScansRow<SCANS.SCAN_TYPE$>
CassetteIDs = ScansRow<SCANS.CASSETTE_IDS$>
NumCass = DCount(CassetteIDs, @VM)
EmployeeID = ScansRow<SCANS.EMPLOYEE_ID$>
Begin Case
Case ScanType EQ 'RESET'
Action = 'RESET'
Case ScanType EQ 'TENCOR_LOAD'
Action = 'TENCOR_LOAD'
If (CassetteIDs EQ '') then
Scan_Services('AddNotAcceptableReason', 'At least one cassette must be scanned to complete a Tencor scan.')
end
Case ScanType EQ 'LOCATION'
Action = 'PLACE'
If (EmployeeID EQ '') then
Scan_Services('AddNotAcceptableReason', 'EmployeeID required to complete a location scan.')
end
If (CassetteIDs EQ '') then
Scan_Services('AddNotAcceptableReason', 'At least one cassette must be scanned to complete a location scan.')
end
LastCassScanned = CassetteIDs<0, NumCass>
CurrStage = Xlate('RDS', LastCassScanned, 'CURR_STAGE', 'X')
If CurrStage EQ 'UNLOAD' then
ErrMsg = '(':LastCassScanned:') Cassette is currently loaded on a tool and is ineligble for a location scan.'
end
Case ScanType EQ 'TOOL'
// Check if cassette field is populated. If so, then check the cassette's current status to
// determine what the next action will be (i.e. Pre-Epi+Load, Unload, etc.). Ensure we are
// working with an RDS and not a WM_IN or WM_OUT cassette ID. They are not used during the
// "RDS" process.
If (CassetteIDs NE '') then
TestWaferLotData = ScansRow<SCANS.TW_LOT_ID$>
TestWaferLotChanged = ScansRow<SCANS.TW_LOT_CHANGED$>
Abort = False$
TestWaferLotIsValid = False$
If TestWaferLotData NE '' and TestWaferLotChanged NE False$ then
ThisTestRunType = ScansRow<SCANS.TEST_RUN_TYPE_ID$>
If ThisTestRunType NE '' then
for each TWLot in TestWaferLotData using @VM setting twPOS
If ScansRow<SCANS.TW_LOT_QTY$, twPOS> NE '' OR ScansRow<SCANS.TW_LOT_QTY$, twPOS> GT 0 then
TWLotCurrQty = Database_Services('ReadDataColumn', 'LOT', TWLot, LOT_WAFER_QTY$, True$, 0, False$)
ThisCurrUsageQty = ScansRow<SCANS.TW_LOT_QTY$, twPOS>
If TWLotCurrQty GT 0 then
If TWLotCurrQty GE ThisCurrUsageQty then
TWLotCurrOperation = Lot_Services('GetLotCurrOperationName', TWLot)
If TWLotCurrOperation NE 'TW_CREATE' AND TWLotCurrOperation NE 'TW_CLOSE' then
TWLotCurrOpen = Database_Services('ReadDataColumn', 'LOT', TWLot, LOT_OPEN$, True$, 0, False$)
If Not(TWLotCurrOpen) then
Abort = True$
Scan_Services('AddNotAcceptableReason', 'Lot ':TWLot:' is closed and cannot be used at this time.')
end else
TestWaferLotIsValid = True$
end
end else
Abort = True$
Scan_Services('AddNotAcceptableReason', 'Lot ':TWLot:' is currently at ':TWLotCurrOperation:' and cannot be used at this time.')
end
end else
Scan_Services('AddNotAcceptableReason', 'Test wafer Lot ':TWLot:' does not have enough wafers. Please select ' : TWLotCurrQty : ' wafers or less.')
end
end else
Scan_Services('AddNotAcceptableReason', 'Test wafer Lot ':TWLot:' has no wafers and cannot be logged for usage.')
end
end else
Abort = True$
Scan_Services('AddNotAcceptableReason', 'Test Wafer Lot ':TWLot:' must have a quantity associated with it.')
end
Until Abort
Next TWLot
end else
Scan_Services('AddNotAcceptableReason', 'A test run type is required to log test wafer usage.')
end
end
// Ensure only one cassette is scanned
If Not(Abort) then
If (NumCass EQ 1) then
CassetteID = CassetteIDs<0, NumCass>
// Ensure we are working with an RDS and not a WM_OUT/WM_IN cassette
If (Count(CassetteID, '.') EQ 0) then
RDSNo = CassetteID
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
WONo = RDSRec<RDS_WO$>
CassNo = RDSRec<RDS_CASS_NO$>
CurrStatus = obj_WO_Mat('CurrStatus', WONo:'*':CassNo)
// RDS Format
IsEpiPro = RDS_Services('IsEpiPro', CassetteID)
If IsEpiPro EQ False$ then
// Non-EpiPro RDS
WONo = Xlate('RDS', CassetteID, 'WO', 'X')
CassNo = Xlate('RDS', CassetteID, 'CASS_NO', 'X')
WOMatKey = WONo:'*':CassNo
WOMatLocs = Xlate('WO_MAT', WOMatKey, WO_MAT_INV_LOCATION$, 'X')
If Index(WOMatLocs,'PTI',1) else
ErrMsg = 'Cassette ':CassetteID:' failed due to missing PTI.'
end
end else
// EpiPro RDS
OutCassNos = Xlate('RDS', CassetteID, 'OUT_CASS_NO', 'X')
OutCassNos = SRP_Array('Clean', OutCassNos, 'TrimAndMakeUnique', @VM)
For each OutCassNo in OutCassNos using @VM setting oPos
WONo = Xlate('RDS', CassetteID, 'WO', 'X')
CassNo = OutCassNo
WOMatKey = WONo:'*':CassNo
WOMatLocs = Xlate('WO_MAT', WOMatKey, WO_MAT_INV_LOCATION$, 'X')
If Index(WOMatLocs,'PTI',1) else
ErrMsg = 'Cassette ':CassetteID:' failed due to missing PTI.'
end
Next OutCassNo
end
If ErrMsg EQ '' then
If CurrStatus NE 'HOLD' then
If (EmployeeID NE '') then
* SupplInstAckReq = Xlate('RDS', RDSNo, 'SUPPL_ACK_REQ' , 'X')
CurrStage = Xlate('RDS', RDSNo, 'CURR_STAGE', 'X')
If CurrStage EQ 'VER' then
VerSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'VER')
If VerSupplID NE False$ then
SupplVerInstAckReq = True$
end else
SupplVerInstAckReq = False$
end
end else
SupplVerInstAckReq = False$
end
If CurrStage EQ 'LOAD' OR CurrStage EQ 'VER' then
LoadSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'LOAD')
If LoadSupplID NE False$ then
SupplLoadInstAckReq = True$
end else
SupplLoadInstAckReq = False$
end
end else
SupplLoadInstAckReq = False$
end
If CurrStage EQ 'UNLOAD' then
UnloadSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'UNLOAD')
If UnloadSupplID NE False$ then
SupplUnloadInstAckReq = True$
end else
SupplUnloadInstAckReq = False$
end
end else
SupplUnloadInstAckReq = False$
end
If CurrStage EQ 'POST' then
PostSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'POST')
If PostSupplID NE False$ then
SupplPostInstAckReq = True$
end else
SupplPostInstAckReq = False$
end
end else
SupplPostInstAckReq = False$
end
RDSLayerInstAckReq = Xlate('RDS', RDSNo, 'RDS_LAYER_ACK_REQ' , 'X')
PreInstAckReq = Xlate('RDS', RDSNo, 'PRE_INST_ACK_REQ' , 'X')
FwiInstAckReq = Xlate('RDS', RDSNo, 'FWI_INST_ACK_REQ' , 'X')
LwiInstAckReq = Xlate('RDS', RDSNo, 'LWI_INST_ACK_REQ' , 'X')
QAInstAckReq = Xlate('RDS', RDSNo, 'QA_INST_ACK_REQ' , 'X')
LoadInstAckReq = Xlate('RDS', RDSNo, 'LOAD_INST_ACK_REQ' , 'X')
UnloadInstAckReq = Xlate('RDS', RDSNo, 'UNLOAD_INST_ACK_REQ' , 'X')
PostInstAckReq = Xlate('RDS', RDSno, 'POST_INST_ACK_REQ' , 'X')
WaferCountInstAckReq = Xlate('RDS', RDSNo, 'WAFER_COUNT_ACK_REQ' , 'X')
// Parse JSON objects
hRDS = SRP_JSON(hScanJSON, 'Get', 'rds')
If (hRDS NE 0) then
If (SupplVerInstAckReq NE False$) then
SupplAck = SRP_JSON(hRDS, 'GetValue', 'supplVerInstAck')
If (SupplAck EQ True$) then
Supplement_Services('AcknowledgeSupplement', VerSupplID, ScansRow<SCANS.EMPLOYEE_ID$>)
end
end
If (SupplLoadInstAckReq NE False$) then
SupplAck = SRP_JSON(hRDS, 'GetValue', 'supplLoadInstAck')
If (SupplAck EQ True$) then
Supplement_Services('AcknowledgeSupplement', LoadSupplID, ScansRow<SCANS.EMPLOYEE_ID$>)
end
end
If (SupplUnloadInstAckReq NE False$) then
SupplAck = SRP_JSON(hRDS, 'GetValue', 'supplUnloadInstAck')
If (SupplAck EQ True$) then
Supplement_Services('AcknowledgeSupplement', UnloadSupplID, ScansRow<SCANS.EMPLOYEE_ID$>)
end
end
If (SupplPostInstAckReq NE False$) then
SupplAck = SRP_JSON(hRDS, 'GetValue', 'supplPostInstAck')
If (SupplAck EQ True$) then
Supplement_Services('AcknowledgeSupplement', PostSupplID, ScansRow<SCANS.EMPLOYEE_ID$>)
end
end
If RDSLayerInstAckReq then RDSRec<RDS_RDS_LAYER_ACK$> = SRP_JSON(hRDS, 'GetValue', 'rdsLayerInstAck')
If PreInstAckReq then RDSRec<RDS_PRE_INST_ACK$> = SRP_JSON(hRDS, 'GetValue', 'preInstAck')
If FwiInstAckReq then RDSRec<RDS_FWI_INST_ACK$> = SRP_JSON(hRDS, 'GetValue', 'fwiInstAck')
If LwiInstAckReq then RDSRec<RDS_LWI_INST_ACK$> = SRP_JSON(hRDS, 'GetValue', 'lwiInstAck')
If QAInstAckReq then RDSRec<RDS_QA_INST_ACK$> = SRP_JSON(hRDS, 'GetValue', 'qaInstAck')
If LoadInstAckReq then RDSRec<RDS_LOAD_INST_ACK$> = SRP_JSON(hRDS, 'GetValue', 'loadInstAck')
If UnloadInstAckReq then RDSRec<RDS_UNLOAD_INST_ACK$> = SRP_JSON(hRDS, 'GetValue', 'unloadInstAck')
If PostInstAckReq then RDSRec<RDS_POST_INST_ACK$> = SRP_JSON(hRDS, 'GetValue', 'postInstAck')
If WaferCountInstAckReq then RDSRec<RDS_WAFER_COUNT_ACK$> = SRP_JSON(hRDS, 'GetValue', 'waferCountInstAck')
Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
* SupplInstAckReq = Xlate('RDS', RDSNo, 'SUPPL_ACK_REQ' , 'X')
CurrStage = Xlate('RDS', RDSNo, 'CURR_STAGE', 'X')
If CurrStage EQ 'VER' then
VerSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'VER')
If VerSupplID NE False$ then
SupplVerInstAckReq = True$
end else
SupplVerInstAckReq = False$
end
end else
SupplVerInstAckReq = False$
end
If CurrStage EQ 'LOAD' OR CurrStage EQ 'VER' then
LoadSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'LOAD')
If LoadSupplID NE False$ then
SupplLoadInstAckReq = True$
end else
SupplLoadInstAckReq = False$
end
end else
SupplLoadInstAckReq = False$
end
If CurrStage EQ 'UNLOAD' then
UnloadSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'UNLOAD')
If UnloadSupplID NE False$ then
SupplUnloadInstAckReq = True$
end else
SupplUnloadInstAckReq = False$
end
end else
SupplUnloadInstAckReq = False$
end
If CurrStage EQ 'POST' then
PostSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'POST')
If PostSupplID NE False$ then
SupplPostInstAckReq = True$
end else
SupplPostInstAckReq = False$
end
end else
SupplPostInstAckReq = False$
end
RDSLayerInstAckReq = Xlate('RDS', RDSNo, 'RDS_LAYER_ACK_REQ' , 'X')
PreInstAckReq = Xlate('RDS', RDSNo, 'PRE_INST_ACK_REQ' , 'X')
FwiInstAckReq = Xlate('RDS', RDSNo, 'FWI_INST_ACK_REQ' , 'X')
LwiInstAckReq = Xlate('RDS', RDSNo, 'LWI_INST_ACK_REQ' , 'X')
QAInstAckReq = Xlate('RDS', RDSNo, 'QA_INST_ACK_REQ' , 'X')
LoadInstAckReq = Xlate('RDS', RDSNo, 'LOAD_INST_ACK_REQ' , 'X')
UnloadInstAckReq = Xlate('RDS', RDSNo, 'UNLOAD_INST_ACK_REQ' , 'X')
PostInstAckReq = Xlate('RDS', RDSNo, 'POST_INST_ACK_REQ' , 'X')
WaferCountInstAckReq = Xlate('RDS', RDSNo, 'WAFER_COUNT_ACK_REQ' , 'X')
SRP_JSON(hRDS, 'Release')
end
ToolID = ScansRow<SCANS.TOOL_ID$>
Reactor = ToolID[-1, 'BR']
Username = ScansRow<SCANS.EMPLOYEE_ID$>
WaferCountAck = RDSRec<RDS_WAFER_COUNT_ACK$>
LLSide = ScansRow<SCANS.LOAD_LOCK$>
WaferQty = ''
If WaferCountAck EQ True$ then
WaferQty = RDSRec<RDS_CASS_WAFER_QTY$>
end else
WaferQty = RDSRec<RDS_VERIFY_QTY$>
end
// Check signature fields to determine where the lot is in the RDS process. The service should
// use the signatures in the RDS table, not the WO_MAT table, so that we can support both
// EpiPro and non-EpiPro lots.
WONo = RDSRec<RDS_WO$>
CassNo = RDSRec<RDS_CASS_NO$>
CurrStatus = obj_WO_Mat('CurrStatus', WONo:'*':CassNo)
CurrStage = Xlate('RDS', RDSNo, 'CURR_STAGE', 'X')
Begin Case
Case CurrStage _EQC 'VER'
Action = 'LOAD'
// Check if both the PRE and LOAD stages are ready to sign
// If a Pre Clean is required, then the OpenInsight UI must be used for now until support for cleans operations
// is added to the barcode web application.
LotNo = RDSRec<RDS_LOT_NUM$>
PreCleanReq = False$
CleanInspKeyID = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
* If CleanInspKeyID NE '' then PreCleanReq = Xlate('CLEAN_INSP', CleanInspKeyID, 'SPEC_CLEAN_REQ', 'X')
PSN = RDSRec<RDS_PROD_SPEC_ID$>
PrePRSStageKey = PSN:'*PRE'
PreCleanReq = Xlate('PRS_STAGE', PrePRSStageKey, 'CLEAN_SIG_REQ', 'X')
If ( (PreCleanReq EQ False$) or (PreCleanReq EQ '') ) then
// The first run on a reactor requires that RDS Layer parameters be entered manually. This is when Prove-in
// occurs.
RunOrderNo = Xlate('RDS', RDSNo, 'RUN_ORDER_NUM', 'X')
If RunOrderNo GT 1 then
// Wafer is eligible to have recipe parameters automatically applied from the previous cassette.
// Only copy RDS layer parameters if they do not already exist. This way we do not overwrite
// manually entered parameters.
LSParmsComp = Xlate('RDS', RDSNo, 'LS_PARMS_COMP', 'X')
FirstParmsComp = LSParmsComp<1,1>
If (FirstParmsComp EQ False$) then RDS_Services('CopyRDSLayerParameters', RDSNo)
//Test Wafer logging verification
If Error_Services('NoError') then
// Supplier lot verification
ScannedSuppLot = ScansRow<SCANS.SUPPLIER_LOT$>
RDSSuppLot = RDSRec<RDS_LOT_NUM$>
If (ScannedSuppLot NE '') then
If (ScannedSuppLot _EQC RDSSuppLot) then
IsTWLoggingReqd = RDS_Services('IsTWLoggingReqd', RDSNo)
If IsTWLoggingReqd NE True$ OR TestWaferLotData NE '' then
WaferCountAckReq = Xlate('RDS', RDSNo, 'WAFER_COUNT_ACK_REQ', 'X')
If WaferCountAckReq EQ False$ then
PreStageReady = ''
LoadStageReady = ''
PreStageReady = QA_Services('PreEpiSignatureReady', RDSNo, Username, WaferQty, Reactor)
If PreStageReady EQ True$ then
RDSLayerAckReq = Xlate('RDS', RDSNo, 'RDS_LAYER_ACK_REQ', 'X')
If RDSLayerAckReq EQ False$ then
LoadStageReady = QA_Services('LoadSignatureReady', RDSNo, Username, WaferQty, LLSide, True$, Reactor)
If (LoadStageReady NE True$) then
// Why is it not ready?
ErrMsg = Error_Services('GetMessage')
Begin Case
Case IndexC(ErrMsg, 'supplement', 1)
// Clear the error to return a JSON payload and handle the acknowledgements.
Error_Services('Clear')
Scan_Services('AddNotAcceptableReason', ErrMsg)
Case IndexC(ErrMsg, 'ROTR', 1)
ROTRBlock = Database_Services('ReadDataColumn', 'REACTOR', Reactor, REACTOR_ROTR_STATUS$, True$, 0, False$)
ROTRBlockReason = Database_Services('ReadDataColumn', 'REACTOR', Reactor, REACTOR_ROTR_STATUS_REASON$, True$, 0, False$)
ROTREnabled = Database_Services('ReadDataColumn', 'REACTOR', Reactor, REACTOR_ENABLE_ROTR$, True$, 0, False$)
If ( (ROTRBlock NE 'P') AND (ROTREnabled EQ True$) ) then
// Clear the error to return a JSON payload and allow for OVERRIDE scan.
ScansRow<SCANS.OVERRIDE_REQD$> = True$
ScansRow<SCANS.OVERRIDE_REASON$> = ROTRBlockReason
ScansRow<SCANS.OVERRIDE_TYPE$> = 'ROTR'
Error_Services('Clear')
ErrMsg = ''
Scan_Services('AddNotAcceptableReason', "ROTR Load Block Enabled")
end else
ScansRow<SCANS.OVERRIDE_REQD$> = False$
ScansRow<SCANS.OVERRIDE_REASON$> = ''
ScansRow<SCANS.OVERRIDE_TYPE$> = ''
end
End Case
end
end else
Scan_Services('AddNotAcceptableReason', 'RDS layer parameters must be reviewed for accuracy and acknowledged before the load operation can be signed.')
end
end else
// Why is it not ready?
ErrMsg = Error_Services('GetMessage')
Begin Case
Case IndexC(ErrMsg, 'supplement', 1)
Error_Services('Clear')
Scan_Services('AddNotAcceptableReason', ErrMsg)
End Case
end
end else
Scan_Services('AddNotAcceptableReason', 'The cassette wafer count must be verified against the scheduled wafer count to proceed.')
end
end else
Scan_Services('AddNotAcceptableReason', 'Test wafers are required to be logged with this run.')
end
end else
ScansRow<SCANS.SUPPLIER_LOT$> = ''
ErrMsg = '(':CassetteID:') Supplier lot mismatch.'
end
end else
Scan_Services('AddNotAcceptableReason', 'Supplier lot scan required in order to complete a tool scan.')
end
end else
ErrMsg = Error_Services('GetMessage')
end
end else
ErrMsg = '(':CassetteID:') The first run must be completed using the OpenInsight user interface.'
end
end else
ErrMsg = '(':CassetteID:') A pre-clean is required for this RDS. The OpenInsight user interface must be used to proceed.'
end
Case CurrStage _EQC 'LOAD'
Action = 'LOAD'
ScannedSuppLot = ScansRow<SCANS.SUPPLIER_LOT$>
RDSSuppLot = RDSRec<RDS_LOT_NUM$>
If (ScannedSuppLot NE '') then
If (ScannedSuppLot _EQC RDSSuppLot) then
IsTWLoggingReqd = RDS_Services('IsTWLoggingReqd', RDSNo)
If IsTWLoggingReqd NE True$ OR TestWaferLotData NE '' then
// Scheduled tool verification (only supports reactors at the moment)
ClearSelect
SchedTool = Xlate('RDS', RDSNo, 'SCHED_REACTOR', 'X')
ClearSelect
SchedTool = 'R':SchedTool
ScanTool = ScansRow<SCANS.TOOL_ID$>
If SchedTool EQ ScanTool then
// Check if LOAD stage is ready to sign
RDSLayerAckReq = Xlate('RDS', RDSNo, 'RDS_LAYER_ACK_REQ', 'X')
If RDSLayerAckReq EQ False$ then
If LWIInstAckReq EQ False$ then
If LoadInstAckReq EQ False$ then
LoadStageReady = QA_Services('LoadSignatureReady', RDSNo, Username, WaferQty, LLSide)
If (LoadStageReady NE True$) then
// Why is it not ready?
ErrMsg = Error_Services('GetMessage')
Begin Case
Case IndexC(ErrMsg, 'supplement', 1)
// Clear the error to return a JSON payload and handle the acknowledgements.
Error_Services('Clear')
Scan_Services('AddNotAcceptableReason', ErrMsg)
Case IndexC(ErrMsg, 'ROTR', 1)
ROTRBlock = Database_Services('ReadDataColumn', 'REACTOR', Reactor, REACTOR_ROTR_STATUS$, True$, 0, False$)
ROTRBlockReason = Database_Services('ReadDataColumn', 'REACTOR', Reactor, REACTOR_ROTR_STATUS_REASON$, True$, 0, False$)
ROTREnabled = Database_Services('ReadDataColumn', 'REACTOR', Reactor, REACTOR_ENABLE_ROTR$, True$, 0, False$)
If ( (ROTRBlock NE 'P') AND (ROTREnabled EQ True$) ) then
// Clear the error to return a JSON payload and allow for OVERRIDE scan.
ScansRow<SCANS.OVERRIDE_REQD$> = True$
ScansRow<SCANS.OVERRIDE_REASON$> = ROTRBlockReason
ScansRow<SCANS.OVERRIDE_TYPE$> = 'ROTR'
Error_Services('Clear')
ErrMsg = ''
Scan_Services('AddNotAcceptableReason', "ROTR Load Block Enabled")
end else
ScansRow<SCANS.OVERRIDE_REQD$> = False$
ScansRow<SCANS.OVERRIDE_REASON$> = ''
ScansRow<SCANS.OVERRIDE_TYPE$> = ''
end
End Case
end
end else
Scan_Services('AddNotAcceptableReason', 'The LOAD stage engineering instructions must be acknowledged before the load operation can be signed.')
end
end else
Scan_Services('AddNotAcceptableReason', 'The LWI stage engineering instructions must be acknowledged before the load operation can be signed.')
end
end else
Scan_Services('AddNotAcceptableReason', 'RDS layer parameters must be reviewed for accuracy and acknowledged before the load operation can be signed.')
end
end else
ErrMsg = 'Scanned tool ':ScanTool:' does not match the scheduled tool ':SchedTool:'. (':RDSNo:')'
end
end else
Scan_Services('AddNotAcceptableReason', 'Test wafers are required to be logged with this run.')
end
end else
ScansRow<SCANS.SUPPLIER_LOT$> = ''
ErrMsg = '(':CassetteID:') Supplier lot mismatch.'
end
end else
Scan_Services('AddNotAcceptableReason', 'Supplier lot scan required in order to complete a tool scan.')
end
Case CurrStage _EQC 'UNLOAD'
Action = 'UNLOAD'
// Check if UNLOAD stage is ready to sign
If ( (LWIInstAckReq EQ False$) or (LWIInstAckReq EQ '') ) then
If ( (UnloadInstAckReq EQ False$) or (UnloadInstAckReq EQ '') ) then
If ( (PostInstAckReq EQ False$) or (PostInstAckReq EQ '') ) then
UnloadStageReady = QA_Services('UnloadSignatureReady', RDSNo, Username, Reactor)
If Not(UnloadStageReady) then
// Why is it not ready?
ErrMsg = Error_Services('GetMessage')
Begin Case
Case IndexC(ErrMsg, 'supplement', 1)
Error_Services('Clear')
Scan_Services('AddNotAcceptableReason', ErrMsg)
Case Otherwise$
// Keep error on Error_Services stack and return 400 level error.
End Case
end else
ReactorNo = Xlate('RDS', RDSNo, RDS_REACTOR$, 'X')
ReactorType = Xlate('REACTOR', ReactorNo, REACTOR_REACT_TYPE$, 'X')
If Len(ReactorType) GE 3 and ReactorType[1, 3] _EQC 'HTR' then
WO = Xlate('RDS', RDSNo, RDS_WO$, 'X')
CassNo = Xlate('RDS', RDSNo, 'RUN_ORDER_NUM', 'X')
WoMatKey = WO:'*':CassNo
WoMatRec = Database_Services('ReadDataRow', 'WO_MAT', WoMatKey)
if Error_Services('NoError') then
If WoMatRec NE '' then
LastCassInWoTestWaferAckReq = WoMatRec<WO_MAT_LAST_CASSETTE_TEST_WAFER_ACK_REQ$>
LastCassInWoTestWaferAcked = WoMatRec<WO_MAT_LAST_CASSETTE_TEST_WAFER_ACKED$>
LastCassInWoTestWaferReqMsg = ''
If LastCassInWoTestWaferAcked EQ False$ or LastCassInWoTestWaferAcked EQ '' then
LastCassInWo = WO_MAT_Services('CassetteIsLastInWo', WoMatKey)
If LastCassInWo then
NextEventScheduled = False$
NextEventHasSamePsn = True$
NextEventIsBlock = False$
NextScheduledEvent = Schedule_Services('GetNextEvent', ReactorNo)
If Error_Services('NoError') then
NextEventScheduled = True$
NextEventHasSamePsn = Schedule_Services('NextEventIsSamePsn', ReactorNo)
NextEventIsBlock = Schedule_Services('NextEventIsBlock', ReactorNo)
end
If NextEventScheduled EQ False$ or NextEventHasSamePsn EQ False$ or NextEventIsBlock EQ True$ then
LastCassInWoTestWaferAckReq = True$
WoMatRec<WO_MAT_LAST_CASSETTE_TEST_WAFER_ACK_REQ$> = LastCassInWoTestWaferAckReq
TestWaferRanSinceLoad = WoMatRec<WO_MAT_LAST_CASSETTE_TEST_WAFER_RAN$> or TestWaferLotIsValid EQ True$
If TestWaferLotIsValid NE True$ and TestWaferRanSinceLoad NE True$ then
TestWaferRanSinceLoad = Rds_Services('TestWaferRanAfterLoad', RDSNo)
If TestWaferRanSinceLoad NE True$ then
LastCassInWoTestWaferReqMsg = ''
If NextEventScheduled EQ False$ or NextEventIsBlock EQ True$ then
LastCassInWoTestWaferReqMsg = 'No material scheduled. Test wafer required.'
Dialog_Response_Log_Services('AddDialogResponseLog', RDSNo, 'RDS', @User4, 'HTR_LAST_BOX_TW', 'No test wafer ran with box when one was required. Unload signature was blocked. User was notified.')
end else If NextEventHasSamePsn EQ False$ then
LastCassInWoTestWaferReqMsg = 'Next work order is a NEW PSN number. Test wafer required.'
Dialog_Response_Log_Services('AddDialogResponseLog', RDSNo, 'RDS', @User4, 'HTR_LAST_BOX_TW', 'No test wafer ran with box when one was required. Unload signature was blocked. User was notified.')
end
Scan_Services('AddNotAcceptableReason', LastCassInWoTestWaferReqMsg)
end
end else
Dialog_Response_Log_Services('AddDialogResponseLog', RDSNo, 'RDS', @User4, 'HTR_LAST_BOX_TW', 'Test wafer was already logged with box. Unload signature was allowed. User was not prompted.')
end
WoMatRec<WO_MAT_LAST_CASSETTE_TEST_WAFER_RAN$> = TestWaferRanSinceLoad
Database_Services('WriteDataRow', 'WO_MAT', WoMatKey, WoMatRec, True$, False$, False$)
end
end
end
end else
ErrMsg = 'WO_MAT record was null.'
end
end else
ErrMsg = 'Failure to read cassette record. ' : Error_Services('GetMessage')
end
end
end
end else
ScanMsg = 'The POST stage engineering instructions must be acknowledged before the load operation can be signed.'
Scan_Services('AddNotAcceptableReason', ScanMsg)
end
end else
ScanMsg = 'The UNLOAD stage engineering instructions must be acknowledged before the load operation can be signed.'
Scan_Services('AddNotAcceptableReason', ScanMsg)
end
end else
ScanMsg = 'The LWI stage engineering instructions must be acknowledged before the load operation can be signed.'
Scan_Services('AddNotAcceptableReason', ScanMsg)
end
Case CurrStage _EQC 'COMP'
Action = 'COMP'
ErrMsg = '(':CassetteID:") Cassette has already been FQA'd."
Case Otherwise$
Action = CurrStage
ErrMsg = '(':CassetteID:') The ':CurrStage:' is not currently supported by the barcode application.'
End Case
end else
Scan_Services('AddNotAcceptableReason', 'EmployeeID required to complete a tool scan.')
end
end else
ErrMsg = '(':CassetteID:') Process Error: cassette is on Hold and may not be signed off.'
end
end
end else
ErrMsg = '(':CassetteID:') WMI/WMO cassette are not currently supported for tool scans. The OpenInsight user interface must be used in order to proceed.'
end
end else
ErrMsg = '(':CassetteID:') Only one cassette can be loaded onto a tool at a time.'
end
end
end else
If ScansRow<SCANS.TW_LOT_ID$> NE '' then
TestWaferLotChanged = ScansRow<SCANS.TW_LOT_CHANGED$>
ThisTestRunType = ScansRow<SCANS.TEST_RUN_TYPE_ID$>
ThisUser = ScansRow<SCANS.EMPLOYEE_ID$>
If TestWaferLotChanged NE False$ then
If ThisTestRunType NE '' then
If RowExists('LSL_USERS', ThisUser) then
TestWaferLotData = ScansRow<SCANS.TW_LOT_ID$>
Abort = False$
for each TWLot in TestWaferLotData using @VM setting twPOS
If ScansRow<SCANS.TW_LOT_QTY$, twPOS> NE '' OR ScansRow<SCANS.TW_LOT_QTY$, twPOS> GT 0 then
TWLotCurrQty = Database_Services('ReadDataColumn', 'LOT', TWLot, LOT_WAFER_QTY$, True$, 0, False$)
ThisCurrUsageQty = ScansRow<SCANS.TW_LOT_QTY$, twPOS>
If TWLotCurrQty GT 0 then
If TWLotCurrQty GE ThisCurrUsageQty then
TWLotCurrOperation = Lot_Services('GetLotCurrOperationName', TWLot)
If TWLotCurrOperation NE 'TW_CREATE' AND TWLotCurrOperation NE 'TW_CLOSE' then
TWLotCurrOpen = Database_Services('ReadDataColumn', 'LOT', TWLot, LOT_OPEN$, True$, 0, False$)
If Not(TWLotCurrOpen) then
Abort = True$
Scan_Services('AddNotAcceptableReason', 'Lot ':TWLot:' is closed and cannot be used at this time.')
end
end else
Abort = True$
Scan_Services('AddNotAcceptableReason', 'Lot ':TWLot:' is currently at ':TWLotCurrOperation:' and cannot be used at this time.')
end
end else
Scan_Services('AddNotAcceptableReason', 'Test wafer Lot ':TWLot:' does not have enough wafers. Please select ' : TWLotCurrQty : ' wafers or less.')
end
end else
Scan_Services('AddNotAcceptableReason', 'Test wafer Lot ':TWLot:' has no wafers and cannot be logged for usage.')
end
end else
Abort = True$
Scan_Services('AddNotAcceptableReason', 'Lot ':TWLot:' must have a quantity associated with it.')
end
Until Abort
Next TWLot
end else
Scan_Services('AddNotAcceptableReason', 'A User ID Scan is required to log test wafer usage.')
end
end else
Scan_Services('AddNotAcceptableReason', 'A test run type is required to log test wafer usage.')
end
end
end else
Scan_Services('AddNotAcceptableReason', 'A cassette must be scanned in order to complete a tool scan.')
end
end
Case Otherwise$
Scan_Services('AddNotAcceptableReason', 'A location ID or tool ID must be scanned to proceed.')
End Case
end
// If the the scan data is valid, update the scan log before returning to the calling process.
If ErrMsg EQ '' then
If Scan_Services('NoNotAcceptableReason') then
ScansRow<SCANS.ACCEPTABLE$> = True$
ScansRow<SCANS.NOT_ACCEPTABLE_REASON$> = ''
end else
ScansRow<SCANS.ACCEPTABLE$> = False$
ScansRow<SCANS.NOT_ACCEPTABLE_REASON$> = Scan_Services('GetNotAcceptableReason')
end
end else
ScansRow<SCANS.ACCEPTABLE$> = False$
ScansRow<SCANS.NOT_ACCEPTABLE_REASON$> = ErrMsg
end
If ScanData NE '' then
ScansRow<SCANS.SCANNED_DATES$, -1> = Date()
ScansRow<SCANS.SCANNED_TIMES$, -1> = Time()
ScansRow<SCANS.SCANNED_DATA$, -1> = ScanData
end
ScansRow<SCANS.ACTION$> = Action
Database_Services('WriteDataRow', 'SCANS', ScanID, ScansRow, True$, False$, True$)
end
end else
ErrMsg = 'Unable to parse the JSON scan data in the ':Service:' service.'
end
SRP_JSON(hScanJSON, 'Release')
end else
ErrMsg = 'ScanID or ScanJSON argument was missing in the ' : Service : ' service.'
end
EndTick = GetTickCount()
Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
If ErrMsg NE '' then Error_Services('Add', ErrMsg)
end service
//----------------------------------------------------------------------------------------------------------------------
// AcceptScan
//
//----------------------------------------------------------------------------------------------------------------------
Service AcceptScan(ScanID, ScanJSON)
StartTick = GetTickCount()
MetricName = 'AcceptScan'
ErrMsg = ''
If ( (ScanID NE '') and (ScanJSON NE '') ) then
hBody = ''
ParseResponse = SRP_JSON(hBody, 'PARSE', ScanJSON)
If ParseResponse EQ '' then
AcceptedStatus = SRP_JSON(hBody, 'GetValue', 'accepted.status')
If AcceptedStatus NE '' then
ScansRow = Scan_Services('GetScansRow', ScanID, False$)
ScansRow<SCANS.ACCEPTED$> = AcceptedStatus
Result = ''
If AcceptedStatus EQ True$ then
// Scan is acceptable and has been accepted on the web app. Sign stage now if tool scan.
AcceptedDate = Date()
AcceptedTime = Time()
LogDate = OCONV( AcceptedDate, 'D2/' )
LogTime = OCONV( AcceptedTime, 'MTS' )
LogDTM = LogDate:' ':LogTime
ScansRow<SCANS.ACCEPTED_DATE$> = AcceptedDate
ScansRow<SCANS.ACCEPTED_TIME$> = AcceptedTime
CassetteIDs = ScansRow<SCANS.CASSETTE_IDS$>
ScanType = ScansRow<SCANS.SCAN_TYPE$>
Begin Case
Case ScanType _EQC 'RESET'
Scan_Services('ResetDevData', CassetteIDs)
If Error_Services('NoError') then Result = 'Reset scan complete'
Case ScanType _EQC 'TENCOR_LOAD'
Tencor= ScansRow<SCANS.TOOL_ID$>
RDSNo= ScansRow<SCANS.CASSETTE_IDS$>
SendStatus = Keyboard_Sim_Services('SendTencor', Tencor, RDSNo)
If Error_Services('NoError') AND SendStatus EQ 'Success' then
Result = 'Tencor Data Sent Successfully'
end else
ErrMsg = SendStatus
end
Case ScanType _EQC 'LOCATION'
//DPC - 2/10/20 - need to check for existence of 1K*PTI scan before location scan is allowed
LocID = ScansRow<SCANS.LOCATION_ID$>
ErrorCassIDs = ''
WONos = ''
CassNos = ''
For each CassetteID in CassetteIDs using @VM setting vPos
Begin Case
Case Count(CassetteID, '.') EQ 0
// RDS Format
IsEpiPro = RDS_Services('IsEpiPro', CassetteID)
If IsEpiPro EQ False$ then
// Non-EpiPro RDS
WONo = Xlate('RDS', CassetteID, 'WO', 'X')
CassNo = Xlate('RDS', CassetteID, 'CASS_NO', 'X')
WOMatKey = WONo:'*':CassNo
WOMatLocs = Xlate('WO_MAT', WOMatKey, WO_MAT_INV_LOCATION$, 'X')
If Index(WOMatLocs,'PTI',1) then
WONos<0, -1> = WONo
CassNos<0, -1> = CassNo
end else
// Cassette has not yet been through passthrough (PTI), so it is not eligible for location scanning.
// Add it to the invalid cassette list (error list).
ErrorCassIDs<0, -1> = CassetteID
end
end else
// EpiPro RDS
OutCassNos = Xlate('RDS', CassetteID, 'OUT_CASS_NO', 'X')
OutCassNos = SRP_Array('Clean', OutCassNos, 'TrimAndMakeUnique', @VM)
For each OutCassNo in OutCassNos using @VM setting oPos
WONo = Xlate('RDS', CassetteID, 'WO', 'X')
CassNo = OutCassNo
WOMatKey = WONo:'*':CassNo
WOMatLocs = Xlate('WO_MAT', WOMatKey, WO_MAT_INV_LOCATION$, 'X')
If Index(WOMatLocs,'PTI',1) then
WONos<0, -1> = WONo
CassNos<0, -1> = CassNo
end else
// Cassette has not yet been through passthrough (PTI), so it is not eligible for location scanning.
// Add it to the invalid cassette list (error list).
ErrorCassIDs<0, -1> = CassetteID
end
Next OutCassNo
end
Case DCount(CassetteID, '.') EQ 3
// WM_IN / WM_OUT Format
WONo = Field(CassetteID, '.', 1, 1)
CassNo = Field(CassetteID, '.', 3, 1)
WOMatKey = WONo:'*':CassNo
WOMatLocs = Xlate('WO_MAT', WOMatKey, WO_MAT_INV_LOCATION$, 'X')
If Index(WOMatLocs,'PTI',1) then
WONos<0, -1> = WONo
CassNos<0, -1> = CassNo
end else
// Cassette has not yet been through passthrough (PTI), so it is not eligible for location scanning.
// Add it to the invalid cassette list (error list).
ErrorCassIDs<0, -1> = CassetteID
end
Case Otherwise$
// Unsupported format
Null
End Case
Next CassetteID
For each CassetteID in ErrorCassIDs using @VM
// Remove erroneous cassettes from the valid cassette list
Locate CassetteID in CassetteIDs using @VM setting vPos then
CassetteIDs = Delete(CassetteIDS, 0, vPos, 0)
end
Next CassetteID
If ( (WONos NE '') and (CassNos NE '') ) then
// Move the cassettes (i.e. log their movement in the material log)
LogFile = 'WO_MAT'
LogDTM = LogDate:' ':LogTime
Action = 'PLACE'
WhCd = Field(LocID, '.', 1, 1)
LocCd = Field(LocID, '.', 2, 1)
UserID = ScansRow<SCANS.EMPLOYEE_ID$>
Tags = ''
ToolID = ''
ScanEntry = 1
WOMLParms = LogFile:@RM
WOMLParms := LogDTM:@RM
WOMLParms := Action:@RM
WOMLParms := WhCd:@RM
WOMLParms := LocCd:@RM
WOMLParms := WONos:@RM
WOMLParms := CassNos:@RM
WOMLParms := UserID:@RM
WOMLParms := Tags:@RM
WOMLParms := ToolID:@RM
WOMLParms := ScanEntry
obj_WO_Mat_Log('Create',WOMLParms)
errCode = ''
end
IF Get_Status(errCode) THEN
ErrMsg = 'Error calling obj_WO_Mat_Log("Create"). Error code: ':errCode
END else
NumCass = DCount(CassetteIDs, @VM)
If NumCass EQ 1 then
CassetteID = CassetteIDs<0, 1>
Result = '(':CassetteID:') Location scan complete. '
end else
Result = '(Multi) Location scan complete. '
end
If ErrorCassIDs NE '' then
Convert @VM to ',' in ErrorCassIDs
Result := 'Cassette(s) ':ErrorCassIDs:' failed due to missing PTI.'
end
end
Case ScanType _EQC 'TOOL'
// Check if cassette field is populated. If so, then check the cassette's current status to
// determine what the next action will be (i.e. Pre-Epi+Load, Unload, etc.)
CassetteID = CassetteIDs<0, 1>
*if CassetteId EQ 1005860 then debug
TestWaferLots = ScansRow<SCANS.TW_LOT_ID$>
Begin Case
Case CassetteID NE ''
//Tool Cassette scan, check for test wafers after everything is signed.
RDSNo = CassetteID
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
ToolID = ScansRow<SCANS.TOOL_ID$>
Reactor = ToolID[-1, 'BR']
Username = ScansRow<SCANS.EMPLOYEE_ID$>
LLSide = ScansRow<SCANS.LOAD_LOCK$>
WaferQty = RDSRec<RDS_CASS_WAFER_QTY$>
// Check signature fields to determine where the lot is in the RDS process. The service should
// use the signatures in the RDS table, not the WO_MAT table, so that we can support both
// EpiPro and non-EpiPro lots.
CurrStage = Xlate('RDS', RDSNo, 'CURR_STAGE', 'X')
CurrDTM = Datetime()
LogData = ''
LogData<1> = OConv(CurrDTM, 'DT')
LogData<2> = RDSNo
LogData<3> = Username
LogData<4> = CurrStage
LogData<5> = ScanID
Begin Case
Case CurrStage _EQC 'VER'
Action = 'LOAD'
// Check if both the PRE and LOAD stages are ready to sign
PreStageSigned = False$
LoadStageSigned = False$
//Log test wafer usage here
If TestWaferLots NE '' then
Continue = False$
ToolID = ScansRow<SCANS.TOOL_ID$>
Reactor = ToolID[-1, 'BR']
Username = ScansRow<SCANS.EMPLOYEE_ID$>
TestWaferLotQtys = ScansRow<SCANS.TW_LOT_QTY$>
TestRunType = ScansRow<SCANS.TEST_RUN_TYPE_ID$>
PSNo = Database_Services('ReadDataColumn', 'RDS', RDSNo, RDS_PROD_SPEC_ID$, True$, 0, False$)
NewTestRunId = Test_Run_Services('CreateTestRunRecord', TestRunType, 'R', Reactor, PSNo, RDSNo,Username , TestWaferLots, TestWaferLotQtys)
If Error_Services('NoError') then
Continue = True$
end else
ErrMsg = Error_Services('GetMessage')
Continue = False$
end
If Continue then
*if RDSNo EQ 1006721 then debug
PreStageSigned = QA_Services('SignPreEpiStage', RDSNo, Username, WaferQty, Reactor, 1)
If PreStageSigned EQ True$ then
LoadStageSigned = QA_Services('SignLoadStage', RDSNo, USername, WaferQty, LLSide, 1)
end
If ( (PreStageSigned EQ True$) and (LoadStageSigned EQ True$) ) then
Result = '(':CassetteID:') Load stage signed.'
end
LogData<6> = Action
LogData<7> = Result
Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
end
end else
PreStageSigned = QA_Services('SignPreEpiStage', RDSNo, Username, WaferQty, Reactor, 1)
If PreStageSigned EQ True$ then
LoadStageSigned = QA_Services('SignLoadStage', RDSNo, USername, WaferQty, LLSide, 1)
end
If ( (PreStageSigned EQ True$) and (LoadStageSigned EQ True$) ) then
Result = '(':CassetteID:') Load stage signed.'
end
LogData<6> = Action
LogData<7> = Result
Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
end
Case CurrStage _EQC 'LOAD'
Action = 'LOAD'
If TestWaferLots NE '' then
Continue = False$
ToolID = ScansRow<SCANS.TOOL_ID$>
Reactor = ToolID[-1, 'BR']
Username = ScansRow<SCANS.EMPLOYEE_ID$>
TestWaferLotQtys = ScansRow<SCANS.TW_LOT_QTY$>
TestRunType = ScansRow<SCANS.TEST_RUN_TYPE_ID$>
PSNo = Database_Services('ReadDataColumn', 'RDS', RDSNo, RDS_PROD_SPEC_ID$, True$, 0, False$)
NewTestRunId = Test_Run_Services('CreateTestRunRecord', TestRunType, 'R', Reactor, PSNo, RDSNo,Username , TestWaferLots, TestWaferLotQtys)
If Error_Services('NoError') then
Continue = True$
end else
ErrMsg = Error_Services('GetMessage')
Continue = False$
end
If Continue then
LoadStageSigned = QA_Services('SignLoadStage', RDSNo, Username, WaferQty, LLSide, 1)
If LoadStageSigned EQ True$ then
Result = '(':CassetteID:') Load stage signed.'
end
LogData<6> = Action
LogData<7> = Result
Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
end
end else
LoadStageSigned = QA_Services('SignLoadStage', RDSNo, Username, WaferQty, LLSide, 1)
If LoadStageSigned EQ True$ then
Result = '(':CassetteID:') Load stage signed.'
end
LogData<6> = Action
LogData<7> = Result
Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
end
// Check if LOAD stage is ready to sign
Case CurrStage _EQC 'UNLOAD'
Action = 'UNLOAD'
Continue = True$
If TestWaferLots NE '' then
ToolID = ScansRow<SCANS.TOOL_ID$>
Reactor = ToolID[-1, 'BR']
Username = ScansRow<SCANS.EMPLOYEE_ID$>
TestWaferLotQtys = ScansRow<SCANS.TW_LOT_QTY$>
TestRunType = ScansRow<SCANS.TEST_RUN_TYPE_ID$>
PSNo = Xlate('RDS', RDSNo, RDS_PROD_SPEC_ID$, True$, 'X')
NewTestRunId = Test_Run_Services('CreateTestRunRecord', TestRunType, 'R', Reactor, PSNo, RDSNo,Username , TestWaferLots, TestWaferLotQtys)
If Error_Services('HasError') then
ErrMsg = Error_Services('GetMessage')
Continue = False$
end
end
If Continue EQ True$ then
// Check if UNLOAD stage is ready to sign
UnloadStageSigned = QA_Services('SignUnloadStage', RDSNo, Username, 1)
If UnloadStageSigned EQ True$ then
Result = '(':CassetteID:') Unload stage signed.'
end
LogData<6> = Action
LogData<7> = Result
Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
end
Case CurrStage _EQC 'COMP'
Action = 'COMP'
ErrMsg = '(':CassetteID:") Cassette has already been FQA'd."
Case Otherwise$
Action = CurrStage
ErrMsg = '(':CassetteID:') The ':CurrStage:' is not currently supported by the barcode application.'
End Case
Case CassetteID EQ '' AND TestWaferLots NE ''
//Tool scan with ONLY test wafers being logged.
ToolID = ScansRow<SCANS.TOOL_ID$>
Reactor = ToolID[-1, 'BR']
Username = ScansRow<SCANS.EMPLOYEE_ID$>
TestWaferLotQtys = ScansRow<SCANS.TW_LOT_QTY$>
NewTestRunId = Test_Run_Services('CreateTestRunRecord', 3, 'R', Reactor, '', '',Username , TestWaferLots, TestWaferLotQtys)
If Error_Services('NoError') then
If NewTestRunId NE '' then
Result = 'Test run logged successfully'
end else
ErrMsg = 'Error while logging creating test run.'
end
end else
ErrMsg = Error_Services('GetMessage')
end
Case Otherwise$
//null
End Case
End Case
end
If ErrMsg EQ '' then
ScansRow<SCANS.NOT_ACCEPTABLE_REASON$> = 'This scan has already been accepted.'
end else
ScansRow<SCANS.NOT_ACCEPTABLE_REASON$> = ErrMsg
end
ScansRow<SCANS.ACCEPTABLE$> = False$
If Assigned(Result) then ScansRow<SCANS.RESULT$> = Result
Scan_Services('SetScansRow', ScanID, ScansRow)
end else
ErrMsg = 'The accepted.status field is missing from the JSON object in the ':Service:' service.'
end
SRP_JSON(hBody, 'Release')
end else
ErrMsg = 'Unable to parse the JSON scan resource in the ':Service:' service.'
end
end else
ErrMsg = 'ScanID or ScanJSON argument was missing in the ' : Service : ' service.'
end
EndTick = GetTickCount()
Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
If ErrMsg NE '' then Error_Services('Add', ErrMsg)
end service
//----------------------------------------------------------------------------------------------------------------------
// ConvertMVScanToJSON
//
// Converts a MultiValue formatted SCANS row into a serialized JSON object and returns the result. If the mvScan
// argument is empty, the service will attempt to get it from the SCANS table. If the itemURL argument is not empty,
// HAL+JSON properties will be added to the JSON object.
//----------------------------------------------------------------------------------------------------------------------
Service ConvertMVScanToJSON(ScanID, mvScan, itemURL)
StartTick = GetTickCount()
MetricName = 'ConvertMVScanToJSON'
jsonScan = ''
mvScanChanged = False$
ErrMsg = ''
If ScanID NE '' then
If mvScan EQ '' then mvScan = Database_Services('ReadDataRow', 'SCANS', ScanID)
If Error_Services('NoError') then
@DICT = Database_Services('GetTableHandle', 'DICT.SCANS')
@ID = ScanID
@RECORD = mvScan
objJSONScan = ''
If SRP_JSON(objJSONScan, 'New', 'Object') then
// Scan object.
If SRP_JSON(objScan, 'New', 'Object') then
SRP_JSON(objScan, 'SetValue', 'ID', @ID, 'String')
SRP_JSON(objScan, 'SetValue', 'type', {SCAN_TYPE}, 'String')
SRP_JSON(objScan, 'SetValue', 'action', {ACTION}, 'String')
SRP_JSON(objScan, 'SetValue', 'acceptable', {ACCEPTABLE}, 'Boolean')
SRP_JSON(objScan, 'SetValue', 'notAcceptableReason', {NOT_ACCEPTABLE_REASON}, 'String')
SRP_JSON(objScan, 'SetValue', 'result', {RESULT}, 'String')
SRP_JSON(objScan, 'SetValue', 'testRunTypeId', {TEST_RUN_TYPE_ID}, 'Integer')
SRP_JSON(objScan, 'SetValue', 'testRunTypeName', XLate('TEST_RUN_TYPE', {TEST_RUN_TYPE_ID}, TEST_RUN_TYPE_RUN_TYPE$, 'X'), 'String')
SRP_JSON(objJSONScan, 'Set', 'scan', objScan)
SRP_JSON(objScan, 'Release')
end
// Supplier Lot object.
objSupplierLot = ''
If SRP_JSON(objSupplierLot, 'New', 'Object') then
SRP_JSON(objSupplierLot, 'SetValue', 'ID', {SUPPLIER_LOT}, 'String')
SRP_JSON(objJSONScan, 'Set', 'supplierLot', objSupplierLot)
SRP_JSON(objSupplierLot, 'Release')
end
// TestWafer Lot Object.
If SRP_JSON(hTWLotArray, 'New', 'Array') then
Abort = False$
TestWaferLotsChanged = {TW_LOT_CHANGED}
TW_Lot_Json = {TW_LOT_JSON}
ParsingResult = ''
TestWaferLots = {TW_LOT_ID}
If TestWaferLots NE '' then
If TestWaferLotsChanged NE True$ and TW_Lot_Json NE '' then
If TW_Lot_Json NE '' then
ParsingResult = SRP_JSON(TWLotJSON, 'Parse', TW_Lot_JSON)
If ParsingResult EQ '' then
SRP_JSON(objJSONScan, 'Set', 'testWaferLots', TWLotJSON)
SRP_JSON(TWLotJSON, 'Release')
end
end
end
If TW_Lot_Json EQ '' or TestWaferLotsChanged EQ True$ or ParsingResult NE '' then
for each TestWaferLotId in TestWaferLots using @VM setting twPos
thisTWLotJson = ''
If SRP_JSON(thisTWLotJson, 'New', 'Object') then
SRP_JSON(thisTWLotJson, 'SetValue', 'testWaferLotId', {TW_LOT_ID}<1, twPos>)
SRP_JSON(thisTWLotJson, 'SetValue', 'quantity', {TW_LOT_QTY}<1, twPos>, 'Integer')
SRP_JSON(hTWLotArray, 'Add', thisTWLotJson)
SRP_JSON(thisTWLotJson, 'Release')
end
Next TestWaferLotId
TW_Lot_Json = SRP_JSON(hTWLotArray, 'Stringify', 'Fast')
mvScan<SCANS.TW_LOT_JSON$> = TW_Lot_Json
mvScan<SCANS.TW_LOT_CHANGED$> = False$
mvScanChanged = True$
SRP_JSON(objJSONScan, 'Set', 'testWaferLots', hTWLotArray)
end
end
SRP_JSON(hTWLotArray, 'Release')
end
SRP_JSON(objScan, 'SetValue', 'testRunTypeId', {TEST_RUN_TYPE_ID}, 'Integer')
// TestRun Type Option Array
twRunTypeListCacheKey$ = 'TW_RUN_TYPE_OPTIONS'
jsonField$ = 1
lastTickField$ = 2
oneHourTicks$ = 3600000
TestRunTypeOptionsJson = Xlate('APP_INFO', twRunTypeListCacheKey$, jsonField$, 'X')
LastTick = Xlate('APP_INFO', 'TW_RUN_TYPE_OPTIONS', lastTickField$, 'X')
TickDiff = oneHourTicks$ + oneHourTicks$
ParsingResponse = ''
If TestRunTypeOptionsJson NE '' and LastTick NE '' and Num(LastTick) then
TickDiff = GetTickCount() - LastTick
If TestRunTypeOptionsJson NE '' and TickDiff LT oneHourTicks$ then
ParsingResponse = SRP_JSON(hTRTypeOpts, 'Parse', TestRunTypeOptionsJson)
If ParsingResponse EQ '' then
SRP_JSON(objJSONScan, 'Set', 'testRunTypeOptions', hTRTypeOpts)
SRP_JSON(hTRTypeOpts, 'Release')
end
end
end
If LastTick EQ '' or Num(LastTick) EQ False$ or TestRunTypeOptionsJson EQ '' or TickDiff GE oneHourTicks$ or ParsingResponse NE '' then
If SRP_JSON(hTestRunTypeArray, 'New', 'Array') then
TestRunTypeList = Test_Run_Services('GetAllTestRunTypes')
For each TestRunTypeId in TestRunTypeList<1> using @VM setting fPos
hTestRunTypeObj = ''
If SRP_JSON(hTestRunTypeObj, 'New', 'Object') then
SRP_JSON(hTestRunTypeObj, 'SetValue', 'testRunTypeId', TestRunTypeId, 'Number')
SRP_JSON(hTestRunTypeObj, 'SetValue', 'testRunTypeName', TestRunTypeList<2, fPos>, 'String')
SRP_JSON(hTestRunTypeArray, 'Add', hTestRunTypeObj)
SRP_JSON(hTestRunTypeObj, 'Release')
end else
Abort = True$
end
Until Abort
Next TestRunTypeId
If Abort EQ False$ then
SRP_JSON(objJSONScan, 'Set', 'testRunTypeOptions', hTestRunTypeArray)
TRTypeListJson = SRP_JSON(hTestRunTypeArray, 'Stringify', 'Fast')
LastTick = GetTickCount()
twRunTypeOptionsRec = ''
twRunTypeOptionsRec<jsonField$> = TRTypeListJson
twRunTypeOptionsRec<lastTickField$> = LastTick
Database_Services('WriteDataRow', 'APP_INFO', twRunTypeListCacheKey$, twRunTypeOptionsRec, True$, False$, False$)
end
end
SRP_JSON(hTestRunTypeArray, 'Release')
end
// Employee object
EmployeeChanged = {EMPLOYEE_CHANGED}
EmployeeJson = {EMPLOYEE_JSON}
ParsingResult = ''
If EmployeeChanged NE True$ and EmployeeJson NE '' then
ParsingResult = SRP_JSON(hEmployeeJson, 'Parse', EmployeeJson)
If ParsingResult EQ '' then
SRP_JSON(objJSONScan, 'Set', 'employee', hEmployeeJson)
SRP_JSON(hEmployeeJson, 'Release')
end
end
If EmployeeJson EQ '' or EmployeeChanged EQ True$ or ParsingResult NE '' then
objEmployee = ''
If SRP_JSON(objEmployee, 'New', 'Object') then
SRP_JSON(objEmployee, 'SetValue', 'ID', {EMPLOYEE_ID}, 'String')
SRP_JSON(objEmployee, 'SetValue', 'name', {EMPLOYEE_NAME}, 'String')
SRP_JSON(objEmployee, 'SetValue', 'authorized', {EMPLOYEE_AUTHORIZED}, 'Boolean')
SRP_JSON(objEmployee, 'SetValue', 'notAuthorizedReason', {EMPLOYEE_NOT_AUTHORIZED_REASON}, 'String')
SRP_JSON(objEmployee, 'SetValue', 'authenticated' , @Record<SCANS.AUTHENTICATED$> , 'Boolean');//JRO Change
SRP_JSON(objEmployee, 'SetValue', 'isAuthorizedToOverrideROTR', @Record<SCANS.IS_AUTHORIZED_TO_OVERRIDE_ROTR$>, 'Boolean')
SRP_JSON(objJSONScan, 'Set', 'employee', objEmployee)
EmployeeJson = SRP_JSON(objEmployee, 'Stringify', 'Fast')
mvScan<SCANS.EMPLOYEE_JSON$> = EmployeeJson
mvScan<SCANS.EMPLOYEE_CHANGED$> = False$
mvScanChanged = True$
SRP_JSON(objEmployee, 'Release')
end
end
// Cassettes object.
arrayCassetteIDs = ''
If SRP_JSON(arrayCassetteIDs, 'New', 'Array') then
If {CASSETTE_IDS} NE '' then
CassetteIDs = {CASSETTE_IDS}
For Each CassetteID in CassetteIDs using @VM
SRP_JSON(arrayCassetteIDs, 'AddValue', CassetteID, 'String')
Next CassetteID
end
SRP_JSON(objJSONScan, 'Set', 'cassetteIDs', arrayCassetteIDs)
SRP_JSON(arrayCassetteIDs, 'Release')
end
// Location object.
objLocation = ''
If SRP_JSON(objLocation, 'New', 'Object') then
SRP_JSON(objLocation, 'SetValue', 'ID', {LOCATION_ID}, 'String')
SRP_JSON(objLocation, 'SetValue', 'name', {LOCATION_NAME}, 'String')
SRP_JSON(objJSONScan, 'Set', 'location', objLocation)
SRP_JSON(objLocation, 'Release')
end
// Tool object.
objTool = ''
If SRP_JSON(objTool, 'New', 'Object') then
SRP_JSON(objTool, 'SetValue', 'ID', {TOOL_ID}, 'String')
SRP_JSON(objTool, 'SetValue', 'name', {TOOL_NAME}, 'String')
SRP_JSON(objTool, 'SetValue', 'loadLock', {LOAD_LOCK}, 'String')
SRP_JSON(objJSONScan, 'Set', 'tool', objTool)
SRP_JSON(objTool, 'Release')
end
// Accepted object.
objAccepted = ''
If SRP_JSON(objAccepted, 'New', 'Object') then
SRP_JSON(objAccepted, 'SetValue', 'status', {ACCEPTED}, 'Boolean')
SRP_JSON(objAccepted, 'SetValue', 'date', Oconv({ACCEPTED_DATE}, 'D4/'), 'String')
SRP_JSON(objAccepted, 'SetValue', 'time', Oconv({ACCEPTED_TIME}, 'MTH'), 'String')
SRP_JSON(objEmployee, 'SetValue', 'notAcceptedReason', {NOT_ACCEPTABLE_REASON}, 'String')
SRP_JSON(objJSONScan, 'Set', 'accepted', objAccepted)
SRP_JSON(objAccepted, 'Release')
end
// Wafer Count object.
objWaferCount = ''
If SRP_JSON(objWaferCount, 'New', 'Object') then
SRP_JSON(objWaferCount, 'SetValue', 'scheduler', {SCHEDULER_WAFER_COUNT}, 'Number')
SRP_JSON(objWaferCount, 'SetValue', 'confirmed', {WAFER_COUNT_CONFIRMED}, 'Boolean')
SRP_JSON(objJSONScan, 'Set', 'waferCount', objWaferCount)
SRP_JSON(objWaferCount, 'Release')
end
// Transfer object.
objTransfer = ''
If SRP_JSON(objTransfer, 'New', 'Object') then
// Transfer Tool sub-object.
objTool = ''
If SRP_JSON(objTool, 'New', 'Object') then
SRP_JSON(objTool, 'SetValue', 'ID', {TRANSFER_TOOL_ID}, 'String')
SRP_JSON(objTool, 'SetValue', 'name', {TRANSFER_TOOL_NAME}, 'String')
SRP_JSON(objTransfer, 'Set', 'tool', objTool)
SRP_JSON(objTool, 'Release')
end
SRP_JSON(objTransfer, 'SetValue', 'boatID', {BOAT_ID}, 'String')
SRP_JSON(objTransfer, 'SetValue', 'plNumber', {PL_NUMBER}, 'String')
SRP_JSON(objTransfer, 'SetValue', 'unloadplConfirmed', {UNLOAD_PL_CONFIRMED}, 'Boolean')
SRP_JSON(objJSONScan, 'Set', 'transfer', objTransfer)
SRP_JSON(objTransfer, 'Release')
end
SRP_JSON(objJSONScan, 'SetValue', 'lastModified', {LAST_MODIFIED}, 'String')
If {SCAN_TYPE} EQ 'TOOL' then
CassIDs = {CASSETTE_IDS}
RDSNo = CassIDs<0, 1>
CurrStage = Xlate('RDS', RDSNo, 'CURR_STAGE', 'X')
If ( ({EMPLOYEE_ID} NE '') and ({TOOL_ID} NE '') and ({SUPPLIER_LOT} NE '') and ({CASSETTE_IDS} NE '') and ( (CurrStage EQ 'LOAD') or (CurrStage EQ 'VER') ) ) |
or ( ({EMPLOYEE_ID} NE '') and ({TOOL_ID} NE '') and ({CASSETTE_IDS} NE '') and (CurrStage EQ 'UNLOAD') ) then
// RDS object
objRDS = ''
If SRP_JSON(objRDS, 'New', 'Object') then
RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
* SupplInstAckReq = Xlate('RDS', RDSNo, 'SUPPL_ACK_REQ' , 'X')
If CurrStage EQ 'VER' then
VerSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'VER')
If VerSupplID NE False$ then
SupplVerInstAckReq = True$
end else
SupplVerInstAckReq = False$
end
end else
SupplVerInstAckReq = False$
end
If CurrStage EQ 'VER' or CurrStage EQ 'LOAD' then
LoadSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'LOAD')
If LoadSupplID NE False$ then
SupplLoadInstAckReq = True$
end else
SupplLoadInstAckReq = False$
end
end else
SupplLoadInstAckReq = False$
end
If CurrStage EQ 'UNLOAD' then
UnloadSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'UNLOAD')
If UnloadSupplID NE False$ then
SupplUnloadInstAckReq = True$
end else
SupplUnloadInstAckReq = False$
end
end else
SupplUnloadInstAckReq = False$
end
If CurrStage EQ 'POST' then
PostSupplID = Supplement_Services('UnacknowledgedSupplementCheck', 'RDS', RDSNo, 'POST')
If PostSupplID NE False$ then
SupplPostInstAckReq = True$
end else
SupplPostInstAckReq = False$
end
end else
SupplPostInstAckReq = False$
end
@DICT = Database_Services('GetTableHandle', 'DICT.SCANS')
@ID = ScanID
@RECORD = mvScan
RDSLayerInstAckReq = Xlate('RDS', RDSNo, 'RDS_LAYER_ACK_REQ' , 'X')
RDSLayerIDs = Xlate('RDS', RDSNo, 'RDS_LS_ID' , 'X')
PreInstAckReq = Xlate('RDS', RDSNo, 'PRE_INST_ACK_REQ' , 'X')
PreInst = Xlate('RDS', RDSNo, 'PRE_INST' , 'X')
FwiInstAckReq = Xlate('RDS', RDSNo, 'FWI_INST_ACK_REQ' , 'X')
FwiInst = Xlate('RDS', RDSNo, 'FWI_INST' , 'X')
LwiInstAckReq = Xlate('RDS', RDSNo, 'LWI_INST_ACK_REQ' , 'X')
LwiInst = Xlate('RDS', RDSNo, 'LWI_INST' , 'X')
QAInstAckReq = Xlate('RDS', RDSNo, 'QA_INST_ACK_REQ' , 'X')
QAInst = Xlate('RDS', RDSNo, 'QA_INST' , 'X')
LoadInstAckReq = Xlate('RDS', RDSNo, 'LOAD_INST_ACK_REQ' , 'X')
LoadInst = Xlate('RDS', RDSNo, 'LOAD_INST' , 'X')
UnloadInstAckReq = Xlate('RDS', RDSNo, 'UNLOAD_INST_ACK_REQ' , 'X')
UnloadInst = Xlate('RDS', RDSNo, 'UNLOAD_INST' , 'X')
PostInstAckReq = Xlate('RDS', RDSNo, 'POST_INST_ACK_REQ' , 'X')
PostInst = Xlate('RDS', RDSNo, 'POST_INST' , 'X')
WaferCountInstAckReq = Xlate('RDS', RDSNo, 'WAFER_COUNT_ACK_REQ' , 'X')
* SchedWfrCnt = Xlate('SCANS', ScanID, 'SCHEDULER_WAFER_COUNT', 'X')
WaferCountInst = 'Please verify the wafer quantity in the cassette matches the ' |
: 'scheduled wafer quantity, ':{SCHEDULER_WAFER_COUNT}:'.'
* SRP_JSON(objRDS, 'SetValue', 'supplInstAckReq' , SupplInstAckReq , 'Boolean')
* SRP_JSON(objRDS, 'SetValue', 'supplInstAck' , RDSRec<RDS_SUPPL_ACK$> , 'Boolean')
* SRP_JSON(objRDS, 'SetValue', 'supplInst' , RDSRec<RDS_SUPPL_INST$> , 'String')
If supplLoadInstAckReq EQ TRUE$ then
supplLoadInstAck = Xlate('SUPPLEMENTS', LoadSupplID, SUPPLEMENTS_SUPPL_ACK$, 'X', '')
supplLoadInst = Xlate('SUPPLEMENTS', LoadSupplID, SUPPLEMENTS_SUPPL_TEXT$, 'X', '')
end else
supplLoadInstAck = ''
supplLoadInst = ''
end
SRP_JSON(objRDS, 'SetValue', 'supplLoadInstAckReq' , supplLoadInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'supplLoadInstAck' , supplLoadInstAck , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'supplLoadInst' , supplLoadInst , 'String')
If supplVerInstAckReq EQ TRUE$ then
supplVerInstAck = Xlate('SUPPLEMENTS', VerSupplID, SUPPLEMENTS_SUPPL_ACK$, 'X', '')
supplVerInst = Xlate('SUPPLEMENTS', VerSupplID, SUPPLEMENTS_SUPPL_TEXT$, 'X', '')
end else
supplVerInstAck = ''
supplVerInst = ''
end
SRP_JSON(objRDS, 'SetValue', 'supplVerInstAckReq' , supplVerInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'supplVerInstAck' , supplVerInstAck , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'supplVerInst' , supplVerInst , 'String')
If supplUnloadInstAckReq EQ TRUE$ then
supplUnloadInstAck = Xlate('SUPPLEMENTS', UnloadSupplID, SUPPLEMENTS_SUPPL_ACK$, 'X', '')
supplUnloadInst = Xlate('SUPPLEMENTS', UnloadSupplID, SUPPLEMENTS_SUPPL_TEXT$, 'X', '')
end else
supplUnloadInstAck = ''
supplUnloadInst = ''
end
SRP_JSON(objRDS, 'SetValue', 'supplUnloadInstAckReq' , supplUnloadInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'supplUnloadInstAck' , supplUnloadInstAck , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'supplUnloadInst' , supplUnloadInst , 'String')
If supplPostInstAckReq EQ TRUE$ then
supplPostInstAck = Xlate('SUPPLEMENTS', PostSupplID, SUPPLEMENTS_SUPPL_ACK$, 'X', '')
supplPostInst = Xlate('SUPPLEMENTS', PostSupplID, SUPPLEMENTS_SUPPL_TEXT$, 'X', '')
end else
supplPostInstAck = ''
supplPostInst = ''
end
SRP_JSON(objRDS, 'SetValue', 'supplPostInstAckReq' , supplPostInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'supplPostInstAck' , supplPostInstAck , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'supplPostInst' , supplPostInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'preInstAckReq' , PreInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'preInstAck' , RDSRec<RDS_PRE_INST_ACK$> , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'preInst' , PreInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'fwiInstAckReq' , FwiInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'fwiInstAck' , RDSRec<RDS_FWI_INST_ACK$> , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'fwiInst' , FwiInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'lwiInstAckReq' , LwiInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'lwiInstAck' , RDSRec<RDS_LWI_INST_ACK$> , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'lwiInst' , LwiInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'qaInstAckReq' , QAInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'qaInstAck' , RDSRec<RDS_QA_INST_ACK$> , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'qaInst' , QAInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'loadInstAckReq' , LoadInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'loadInstAck' , RDSRec<RDS_LOAD_INST_ACK$> , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'loadInst' , LoadInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'unloadInstAckReq' , UnloadInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'unloadInstAck' , RDSRec<RDS_UNLOAD_INST_ACK$> , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'unloadInst' , UnloadInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'postInstAckReq' , PostInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'postInstAck' , RDSRec<RDS_POST_INST_ACK$> , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'postInst' , PostInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'waferCountInstAckReq' , WaferCountInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'waferCountInstAck' , RDSRec<RDS_WAFER_COUNT_ACK$> , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'waferCountInst' , WaferCountInst , 'String')
SRP_JSON(objRDS, 'SetValue', 'rdsLayerInstAckReq' , RDSLayerInstAckReq , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'rdsLayerInstAck' , RDSRec<RDS_RDS_LAYER_ACK$> , 'Boolean')
OverrideRequired = @Record<SCANS.OVERRIDE_REQD$>
OverrideType = @Record<SCANS.OVERRIDE_TYPE$>
OverrideReason = @Record<SCANS.OVERRIDE_REASON$>
SRP_JSON(objRDS, 'SetValue', 'overRideAckReq' , OverrideRequired , 'Boolean')
SRP_JSON(objRDS, 'SetValue', 'overRideType' , OverrideType , 'string')
SRP_JSON(objRDS, 'SetValue', 'overRideReason', @Record<SCANS.OVERRIDE_REASON$>, 'string')
If ((OverrideRequired EQ True$) and (OverrideType EQ 'ROTR')) then
Swap " " with "" in OverrideReason
OverrideComments = Xlate("ROTR_OVERRIDE_COMMENT_OPTIONS", OverrideReason, ROTR_OVERRIDE_COMMENT_OPTIONS_COMMENT_OPTIONS$, 'X')
SRP_JSON(objRds, "SetValueArray", "overRideComments", OverrideComments, @VM)
Reactor = {TOOL_ID}[2,999]
rotrOverrideCount = Xlate('REACTOR', Reactor, REACTOR_ROTR_OVERRIDE_COUNT$, 'X')
if rotrOverrideCount EQ '' then rotrOverrideCount = 0
SRP_JSON(objRds, "SetValue", "rotrOverrideCount", rotrOverrideCount, 'Number')
end
arrayRDSLayers = ''
// Add RDSLayers (recipe parameters) array
If SRP_JSON(arrayRDSLayers, 'New', 'Array') then
For each RDSLayerID in RDSLayerIDs using @VM setting vPos
arrayParameters = ''
If SRP_JSON(arrayParameters, 'New', 'Array') then
RDSLayerKey = RDSNo:'*':RDSLayerID
ColNames = 'LS_ID,RECIPE_NO,RECIPE_NAME,DOPANT,EPI_DILUENT,EPI_TIME,' |
: 'DILUENT_ADJ_PARAM,DOPANT_FLOW,HCL_FLOW,BAKE_TIME,EPI_H2_FLOW,' |
: 'TCS_FLOW,DCS_FLOW,AUX1,AUX2,F_OFFSET,S_OFFSET,R_OFFSET,' |
: 'SUSC_ETCH,ETCH1,ETCH2,ETCH3,UL_TEMP'
ParamList = 'Layer ID,Recipe,Recipe Name,Dopant,Epi Diluent,Deposit,' |
: 'Diluent,Dopant Flow,HCL Flow,Bake Time,H2 Flow,TCS Flow,' |
: 'DCS Flow,Aux 1 Flow,Aux 2 flow,F Offset,S Offset,R Offset,' |
: 'Susc Etch,Etch 1,Etch 2,Etch 3,Unload Temp'
pos = ''
For each Parameter in ParamList using ',' setting pos
ColName = Field(ColNames, ',', pos)
ParamData = Xlate('RDS_LAYER', RDSLayerKey, ColName, 'X')
Conversion = Xlate('DICT.RDS_LAYER', ColName, DICT_CONV$, 'X')
If ParamData NE '' then
If Conversion NE '' then ParamData = OConv(ParamData, Conversion)
objParameter = ''
If SRP_JSON(objParameter, 'New', 'Object') then
Success = SRP_JSON(objParameter, 'SetValue', 'Parameter', Parameter)
Success = SRP_JSON(objParameter, 'SetValue', 'Value', ParamData)
// Add parameter object to parameter array
Success = SRP_JSON(arrayParameters, 'Add', objParameter)
SRP_JSON(objParameter, 'Release')
end
end
Next Value
end
// Add parameter array to RDS Layer array
Success = SRP_JSON(arrayRDSLayers, 'Add', arrayParameters)
SRP_JSON(arrayParameters, 'Release')
Next RDSLayerID
Success = SRP_JSON(objRDS, 'Set', 'rdsLayers', arrayRDSLayers)
SRP_JSON(arrayRDSLayers, 'Release')
end
SRP_JSON(objJSONScan, 'Set', 'rds', objRDS)
SRP_JSON(objRDS, 'Release')
end
end
end
jsonScan = SRP_JSON(objJSONScan, 'Stringify', 'Fast')
SRP_JSON(objJSONScan, 'Release')
If mvScanChanged EQ True$ then
Database_Services('WriteDataRow', 'SCANS', {SCAN_ID}, mvScan, True$, False$, False$)
end
end else
ErrMsg = 'Unable to create JSON representation in the ' : Service : ' service.'
end
end
end else
ErrMsg = 'ScanID argument was missing in the ' : Service : ' service.'
end
Response = jsonScan
EndTick = GetTickCount()
Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
If ErrMsg NE '' then Error_Services('Add', ErrMsg)
end service
//----------------------------------------------------------------------------------------------------------------------
// ConvertJSONScanToMV
//
// Converts a serialized JSON scan object into a MultiValue formatted SCANS row and returns the result.
//----------------------------------------------------------------------------------------------------------------------
Service ConvertJSONScanToMV(jsonScan)
mvScan = ''
If jsonScan NE '' then
If SRP_JSON(objJSONScan, 'Parse', jsonScan) EQ '' then
mvScan<SCANS.CREATED_DATE$> = SRP_JSON(objJSONScan, 'GetValue', 'created.date')
mvScan<SCANS.CREATED_TIME$> = SRP_JSON(objJSONScan, 'GetValue', 'created.time')
mvScan<SCANS.SCAN_TYPE$> = SRP_JSON(objJSONScan, 'GetValue', 'scan.type')
mvScan<SCANS.EMPLOYEE_ID$> = SRP_JSON(objJSONScan, 'GetValue', 'employee.ID')
mvScan<SCANS.LOCATION_ID$> = SRP_JSON(objJSONScan, 'GetValue', 'location.ID')
mvScan<SCANS.TOOL_ID$> = SRP_JSON(objJSONScan, 'GetValue', 'tool.ID')
mvScan<SCANS.LOAD_LOCK$> = SRP_JSON(objJSONScan, 'GetValue', 'tool.loadLock')
mvScan<SCANS.ACCEPTED$> = SRP_JSON(objJSONScan, 'GetValue', 'accepted.status', False$)
mvScan<SCANS.ACCEPTED_DATE$> = SRP_JSON(objJSONScan, 'GetValue', 'accepted.date')
mvScan<SCANS.ACCEPTED_TIME$> = SRP_JSON(objJSONScan, 'GetValue', 'accepted.time')
mvScan<SCANS.ACTION$> = SRP_JSON(objJSONScan, 'GetValue', 'action')
mvScan<SCANS.WAFER_COUNT_CONFIRMED$> = SRP_JSON(objJSONScan, 'GetValue', 'waferCount.confirmed', False$)
mvScan<SCANS.BOAT_ID$> = SRP_JSON(objJSONScan, 'GetValue', 'transfer.boatID')
mvScan<SCANS.PL_NUMBER$> = SRP_JSON(objJSONScan, 'GetValue', 'transfer.plNumber')
mvScan<SCANS.UNLOAD_PL_CONFIRMED$> = SRP_JSON(objJSONScan, 'GetValue', 'transfer.unloadplConfirmed', False$)
mvScan<SCANS.TRANSFER_TOOL_ID$> = SRP_JSON(objJSONScan, 'GetValue', 'transfer.tool.ID')
mvScan<SCANS.TEST_RUN_TYPE_ID$> = SRP_JSON(objJSONScan, 'GetValue', 'scan.testRunTypId')
mvScan<SCANS.TEST_RUN_TYPE_NAME$> = SRP_JSON(objJSONScan, 'GetValue', 'scan.testRunTypeName')
arrayCassettes = SRP_JSON(objJSONScan, 'Get', 'cassetteIDs')
NumCassettes = SRP_JSON(arrayCassettes, 'GetCount')
For CassetteCnt = 1 to NumCassettes
mvScan<SCANS.CASSETTE_IDS$, CassetteCnt> = SRP_JSON(arrayCassettes, 'GetValue', CassetteCnt)
Next CassetteCnt
SRP_JSON(arrayCassettes, 'Release')
arrayScanLog = SRP_JSON(objJSONScan, 'Get', 'scan.scanLog')
NumScanSessions = SRP_JSON(arrayScanLog, 'GetCount')
For ScanSessionCnt = 1 to NumScanSessions
objScan = SRP_JSON(arrayScanLog, 'Get', ScanSessionCnt)
mvScan<SCANS.SCANNED_DATES$, ScanSessionCnt> = SRP_JSON(objScan, 'GetValue', 'date')
mvScan<SCANS.SCANNED_TIMES$, ScanSessionCnt> = SRP_JSON(objScan, 'GetValue', 'time')
mvScan<SCANS.SCANNED_DATA$, ScanSessionCnt> = SRP_JSON(objScan, 'GetValue', 'data')
SRP_JSON(objScan, 'Release')
Next ScanSessionCnt
SRP_JSON(arrayScanLog, 'Release')
SRP_JSON(objJSONScan, 'Release')
end else
Error_Services('Add', 'Error parsing jsonScan in the ' : Service : ' service.')
end
end else
Error_Services('Add', 'jsonScan argument was missing in the ' : Service : ' service.')
end
Response = mvScan
end service
//----------------------------------------------------------------------------------------------------------------------
// DecodeScanData
//
// ScanData - The scan data containing encoded characters. - [Required]
//
// Decodes the scan data so that encoded characters are restored to their regular form. This returns the decoded scan
// data.
//----------------------------------------------------------------------------------------------------------------------
Service DecodeScanData(ScanData)
DecodedScanData = ''
If Len(ScanData) then
DecodedScanData = ScanData
Swap '/A' with '!' in DecodedScanData
Swap '/B' with '"' in DecodedScanData
Swap '/C' with '#' in DecodedScanData
Swap '/D' with '$' in DecodedScanData
Swap '/E' with '%' in DecodedScanData
Swap '/F' with '&' in DecodedScanData
Swap '/G' with "'" in DecodedScanData
Swap '/H' with '(' in DecodedScanData
Swap '/I' with ')' in DecodedScanData
Swap '/J' with '*' in DecodedScanData
Swap '/K' with '+' in DecodedScanData
Swap '/L' with ',' in DecodedScanData
Swap '/O' with '/' in DecodedScanData
Swap '/Z' with ':' in DecodedScanData
Swap '%F' with ';' in DecodedScanData
Swap '%G' with '<' in DecodedScanData
Swap '%H' with '=' in DecodedScanData
Swap '%I' with '>' in DecodedScanData
Swap '%J' with '?' in DecodedScanData
Swap '%O' with '_' in DecodedScanData
end else
Error_Services('Add', 'The ScanData argument was missing in the ' : Service : ' service.')
end
Response = DecodedScanData
end service
Service ResetDevData(CassetteIDs)
DevCassettes = '277147,277425,277074,277265,277107,277106,276833,277006,277005,276833,276832,276895,276701,276700,277308,277118,277117,276604'
Swap ',' with @VM in DevCassettes
If CassetteIDs EQ '' then CassetteIDs = DevCassettes
For each CassetteID in CassetteIDs using @VM
// Reset RDS record
RDSRec = Database_Services('ReadDataRow', 'RDS', CassetteID)
If Error_Services('NoError') then
If CassetteID EQ 277147 then
RDSRec<RDS_DATE_OUT$> = ''
RDSRec<RDS_TIME_OUT$> = ''
RDSRec<RDS_OPERATOR_OUT$> = ''
RDSRec<RDS_WAFERS_OUT$> = ''
RDSRec<RDS_LWI_INST_ACK$> = ''
RDSRec<RDS_UNLOAD_INST_ACK$> = ''
Database_Services('WriteDataRow', 'RDS', CassetteID, RDSRec, True$, False$, True$)
If Error_Services('NoError') then
WOMatKey = Xlate('RDS', CassetteID, 'WO_MAT_KEY', 'X')
WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
If Error_Services('NoError') then
WOMatRec<WO_MAT_SIGNATURE$, 3> = ''
WOMatRec<WO_MAT_SIG_DTM$, 3> = ''
Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$)
end
end
end else
RDSRec<RDS_REACTOR$> = ''
RDSRec<RDS_DATE_IN$> = ''
RDSRec<RDS_DATE_OUT$> = ''
RDSRec<RDS_TIME_IN$> = ''
RDSRec<RDS_TIME_OUT$> = ''
RDSRec<RDS_OPERATOR_IN$> = ''
RDSRec<RDS_OPERATOR_OUT$> = ''
RDSRec<RDS_WAFERS_IN$> = ''
RDSRec<RDS_WAFERS_OUT$> = ''
RDSRec<RDS_VERIFY_QTY$> = ''
RDSRec<RDS_PRE_EPI_SIG$> = ''
RDSRec<RDS_PRE_EPI_SIG_DATE$> = ''
RDSRec<RDS_PRE_EPI_SIG_TIME$> = ''
RDSRec<RDS_LOAD_LOCK_SIDE$> = ''
RDSRec<RDS_SUPPL_ACK$> = ''
RDSRec<RDS_RDS_LAYER_ACK$> = ''
RDSRec<RDS_PRE_INST_ACK$> = ''
RDSRec<RDS_FWI_INST_ACK$> = ''
RDSRec<RDS_LWI_INST_ACK$> = ''
RDSRec<RDS_QA_INST_ACK$> = ''
RDSRec<RDS_LOAD_INST_ACK$> = ''
RDSRec<RDS_UNLOAD_INST_ACK$> = ''
RDSRec<RDS_WAFER_COUNT_ACK$> = ''
RDSRec<RDS_SUPPL_SIG$> = ''
RDSRec<RDS_SUPPL_SIG_DATE$> = ''
RDSRec<RDS_SUPPL_SIG_TIME$> = ''
Database_Services('WriteDataRow', 'RDS', CassetteID, RDSRec, True$, False$, True$)
If Error_Services('NoError') then
WOMatKey = Xlate('RDS', CassetteID, 'WO_MAT_KEY', 'X')
WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
If Error_Services('NoError') then
WOMatRec<WO_MAT_SIGNATURE$> = ''
WOMatRec<WO_MAT_SIG_DTM$> = ''
Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$)
end
end
RDSLayerKeys = RDSRec<RDS_RDS_LAYER_KEYS$>
For each RDSLayerKey in RDSLayerKeys using @VM
RDSLayerRec = Database_Services('ReadDataRow', 'RDS_LAYER', RDSLayerKey)
* RDSLayerRec<RDS_LAYER_DOPANT$> = ''
* RDSLayerRec<RDS_LAYER_EPI_DILUENT$> = ''
RDSLayerRec<RDS_LAYER_EPI_TIME$> = ''
RDSLayerRec<RDS_LAYER_DILUENT_ADJ_PARAM$> = ''
RDSLayerRec<RDS_LAYER_DOPANT_FLOW$> = ''
RDSLayerRec<RDS_LAYER_HCL_FLOW$> = ''
RDSLayerRec<RDS_LAYER_BAKE_TIME$> = ''
RDSLayerRec<RDS_LAYER_EPI_H2_FLOW$> = ''
RDSLayerRec<RDS_LAYER_TCS_FLOW$> = ''
RDSLayerRec<RDS_LAYER_DCS_FLOW$> = ''
RDSLayerRec<RDS_LAYER_AUX1$> = ''
RDSLayerRec<RDS_LAYER_AUX2$> = ''
RDSLayerRec<RDS_LAYER_F_OFFSET$> = ''
RDSLayerRec<RDS_LAYER_S_OFFSET$> = ''
RDSLayerRec<RDS_LAYER_R_OFFSET$> = ''
RDSLayerRec<RDS_LAYER_ETCH1$> = ''
RDSLayerRec<RDS_LAYER_ETCH2$> = ''
RDSLayerRec<RDS_LAYER_ETCH3$> = ''
RDSLayerRec<RDS_LAYER_OVERGROW_REQ$> = ''
RDSLayerRec<RDS_LAYER_SUSC_ETCH$> = ''
RDSLayerRec<RDS_LAYER_UL_TEMP$> = ''
Database_Services('WriteDataRow', 'RDS_LAYER', RDSLayerKey, RDSLayerRec, True$, False$, True$)
Next RDSLayerKey
end
end
Next CassetteID
end service
Service RemoveOldScans()
CleanDate = OConv((Date() - 30), 'D4/')
Query = 'SELECT SCANS WITH CREATED_DATE LT ':Quote(CleanDate)
GoSub ClearCursors
RList(Query, TARGET_ACTIVELIST$, '', '', '')
If Not(Get_Status(errCode)) then
Done = False$
Loop
Readnext KeyID else Done = True$
Until Done
Database_Services('DeleteDataRow', 'SCANS', KeyID)
Repeat
end
end service
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ClearCursors:
For counter = 0 to 8
ClearSelect counter
Next counter
return