Error Message buffer variable mismatch. Errors were being set to ErrorMsg and it needed to be set to ErrMsg. LoadSignatureReady was being set to false, but there were no cases to handle that false response with the variable ErrMsg not getting set.
2425 lines
117 KiB
Plaintext
2425 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
|
|
|
|
|
|
// 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
|
|
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')
|
|
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
|
|
// 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 = '(':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')
|
|
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
|
|
ErrMsg = 'RDS layer parameters must be reviewed for accuracy and acknowledged before the load operation can be signed.'
|
|
Scan_Services('AddNotAcceptableReason', ErrMsg)
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|