open-insight/LSL2/STPROC/SCAN_SERVICES_OLD.txt

1790 lines
118 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.
***********************************************************************************************************************/
#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
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
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
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
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)
ScansRow = ''
If ScanID NE '' then
ScansRow = Database_Services('ReadDataRow', 'SCANS', ScanID)
If ReturnJSON EQ True$ then
ScansRow = Scan_Services('ConvertMVScanToJSON', ScanID, ScansRow)
end
end else
Error_Services('Add', 'ScanID argument was missing in the ' : Service : ' service.')
end
Response = ScansRow
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)
If (ScanID NE '') AND (ScansRow NE '') then
Database_Services('WriteDataRow', 'SCANS', ScanID, ScansRow)
end else
Error_Services('Add', 'ScanID or ScansRow argument was missing in the ' : Service : ' service.')
end
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)
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
SupplierLotID = ScanData[3, 999]
If ( RowExists('RDS', SupplierLotID) EQ True$ AND ScanData[1, 2] EQ '1T') then
ScansRow<SCANS.CASSETTE_IDS$> = SupplierLotID
//If ( RowExists('RDS', SupplierLotID) EQ True$ ) then
// Error_Services('Add', 'Not a valid supplier lot')
end else
ScansRow<SCANS.SUPPLIER_LOT$> = SupplierLotID
end
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}
Error_Services('Add', EmployeeNotAuthorizedReason)
Case EmployeeActive NE True$
Error_Services('Add', 'Inactive employee.')
Case Otherwise$
ScansRow<SCANS.EMPLOYEE_ID$> = {EMPLOYEE_ID}
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}
Error_Services('Add', 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
Error_Services('Add', 'Invalid load lock value "':LoadLock:'".')
end
end else
Error_Services('Add', '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'
Error_Services('Add', '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$
Error_Services('Add', 'Tool type ':ToolType:' is not currently supported by the barcode application.')
End Case
end else
Error_Services('Add', 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
Error_Services('Add', 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
Error_Services('Add', 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
Error_Services('Add', 'Invalid password for user ':EmployeeID:'.')
end
end else
Error_Services('Add', 'An employee ID must be scanned before scanning a password.')
end
end else
Error_Services('Add', '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 = XLATE('REACTOR', Reactor, '', 'X')
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.EMPLOYEE_ID$>= ''
Result = 'Override Performed successfully'
end
end else
Error_Services('Add', '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 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
Error_Services('Add', CassetteID : ' is not a valid Cassette ID.')
end
End Case
If Error_Services('NoError') 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
Error_Services('Add', '(':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
// Ensure only one cassette is scanned
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 Error_Services('Add', 'Cassette ':CassetteID:' failed due to missing PTI.')
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 Error_Services('Add', 'Cassette ':CassetteID:' failed due to missing PTI.')
Next OutCassNo
end
If Error_Services('NoError') then
If CurrStatus NE 'HOLD' then
If (EmployeeID NE '') then
SupplInstAckReq = Xlate('RDS', RDSNo, 'SUPPL_ACK_REQ' , 'X')
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 (SupplInstAckReq EQ True$) then
SupplAck = SRP_JSON(hRDS, 'GetValue', 'supplInstAck')
If (SupplAck EQ True$) then
RDSRec<RDS_SUPPL_ACK$> = True$
RDSRec<RDS_SUPPL_SIG$> = ScansRow<SCANS.EMPLOYEE_ID$>
RDSRec<RDS_SUPPL_SIG_DATE$> = Date()
RDSRec<RDS_SUPPL_SIG_TIME$> = Time()
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')
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 'PRE'
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)
// Supplier lot verification
ScannedSuppLot = ScansRow<SCANS.SUPPLIER_LOT$>
RDSSuppLot = RDSRec<RDS_LOT_NUM$>
If (ScannedSuppLot NE '') then
If (ScannedSuppLot _EQC RDSSuppLot) 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)
ROTRBlock = XLATE('REACTOR', Reactor, 47, 'X')
ROTRBlockReason = XLATE('REACTOR', Reactor, REACTOR_ROTR_STATUS_REASON$, 'X')
ROTREnabled = XLATE('REACTOR', Reactor, 49, 'X')
If ROTRBlock NE 'P' AND ROTREnabled EQ true$ then
ScansRow<SCANS.OVERRIDE_REQD$> = 1
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$> = 0
ScansRow<SCANS.OVERRIDE_REASON$> = ''
ScansRow<SCANS.OVERRIDE_TYPE$> = ''
end
end
end else
Scan_Services('AddNotAcceptableReason', 'The cassette wafer count must be verified against the scheduled wafer count to proceed.')
end
end else
ScansRow<SCANS.SUPPLIER_LOT$> = ''
Error_Services('Add', '(':CassetteID:') Supplier lot mismatch.')
end
end else
Scan_Services('AddNotAcceptableReason', 'Supplier lot scan required in order to complete a tool scan.')
end
end else
Error_Services('Add', '(':CassetteID:') The first run must be completed using the OpenInsight user interface.')
end
end else
Error_Services('Add', '(':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
// Scheduled tool verification (only supports reactors at the moment)
SchedTool = Xlate('RDS', RDSNo, 'SCHED_REACTOR', 'X')
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)
ROTRBlock = XLATE('REACTOR', Reactor, 47, 'X')
ROTRBlockReason = XLATE('REACTOR', Reactor, REACTOR_ROTR_STATUS_REASON$, 'X')
ROTREnabled = XLATE('REACTOR', Reactor, 49, 'X')
If LoadStageReady EQ False$ AND ROTRBlock NE 'P' AND ROTREnabled EQ true$ then
ScansRow<SCANS.OVERRIDE_REQD$> = 1
ScansRow<SCANS.OVERRIDE_TYPE$> = 'ROTR'
ScansRow<SCANS.OVERRIDE_REASON$> = ROTRBlockReason
Error_Services('Clear')
Scan_Services('AddNotAcceptableReason', "ROTR Load Block Enabled")
end else
ScansRow<SCANS.OVERRIDE_REQD$> = 0
ScansRow<SCANS.OVERRIDE_REASON$> = ''
ScansRow<SCANS.OVERRIDE_TYPE$> = ''
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
ErrorMessage = 'RDS layer parameters must be reviewed for accuracy and acknowledged before the load operation can be signed.'
Scan_Services('AddNotAcceptableReason', ErrorMessage)
end
end else
Error_Services('Add', 'Scanned tool ':ScanTool:' does not match the scheduled tool ':SchedTool:'. (':RDSNo:')')
end
end else
ScansRow<SCANS.SUPPLIER_LOT$> = ''
Error_Services('Add', '(':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)
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 'LWIS'
Action = 'LWIS'
Error_Services('Add', '(':CassetteID:') The ':CurrStage:' is not currently supported by the barcode application. The OpenInsight user interface must be used in order to proceed.')
Case CurrStage _EQC 'LWII'
Action = 'LWII'
Error_Services('Add', '(':CassetteID:') The ':CurrStage:' is not currently supported by the barcode application. The OpenInsight user interface must be used in order to proceed.')
Case CurrStage _EQC 'FQA'
Action = 'FQA'
Error_Services('Add', '(':CassetteID:') The ':CurrStage:' is not currently supported by the barcode application. The OpenInsight user interface must be used in order to proceed.')
Case CurrStage _EQC 'COMP'
Action = 'COMP'
Error_Services('Add', '(':CassetteID:") Cassette has already been FQA'd.")
Case Otherwise$
End Case
end else
Scan_Services('AddNotAcceptableReason', 'EmployeeID required to complete a tool scan.')
end
end else
Error_Services('Add', '(':CassetteID:') Process Error: cassette is on Hold and may not be signed off.')
end
end
end else
Error_Services('Add', '(':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
// Currently we are only supporting loading one cassette onto a tool at a time.
Error_Services('Add', '(':CassetteID:') Only one cassette can be loaded onto a tool at a time.')
end
end else
Scan_Services('AddNotAcceptableReason', 'A cassette must be scanned in order to complete a tool scan.')
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 Error_Services('NoError') 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$> = Error_Services('GetMessage')
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
// Save error message if present as Database_Services will clear any errors.
ErrorMessage = ''
If Error_Services('HasError') then
ErrorMessage = Error_Services('GetMessage')
end
Database_Services('WriteDataRow', 'SCANS', ScanID, ScansRow, True$, False$, True$)
// Restore pre-existing error message if present.
If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
end
end else
Error_Services('Add', 'Unable to parse the JSON scan data in the ':Service:' service.')
end
SRP_JSON(hScanJSON, 'Release')
end else
Error_Services('Add', 'ScanID or ScanJSON argument was missing in the ' : Service : ' service.')
end
end service
//----------------------------------------------------------------------------------------------------------------------
// AcceptScan
//
//----------------------------------------------------------------------------------------------------------------------
Service AcceptScan(ScanID, ScanJSON)
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
Error_Services('Add', 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
ErrorMsg = 'Error calling obj_WO_Mat_Log("Create"). Error code: ':errCode
Error_Services('Add', ErrorMsg)
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>
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 'PRE'
Action = 'LOAD'
// Check if both the PRE and LOAD stages are ready to sign
PreStageSigned = False$
LoadStageSigned = False$
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)
Case CurrStage _EQC 'LOAD'
Action = 'LOAD'
// Check if LOAD stage is ready to sign
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)
Case CurrStage _EQC 'UNLOAD'
Action = 'UNLOAD'
// 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)
Case CurrStage _EQC 'LWIS'
Action = 'LWIS'
Error_Services('Set', '(':CassetteID:') The ':CurrStage:' is not currently supported by the barcode application.')
Case CurrStage _EQC 'LWII'
Action = 'LWII'
Error_Services('Set', '(':CassetteID:') The ':CurrStage:' is not currently supported by the barcode application.')
Case CurrStage _EQC 'FQA'
Action = 'FQA'
Error_Services('Set', '(':CassetteID:') The ':CurrStage:' is not currently supported by the barcode application.')
Case CurrStage _EQC 'COMP'
Action = 'COMP'
Error_Services('Set', '(':CassetteID:") Cassette has already been FQA'd.")
Case Otherwise$
End Case
End Case
end
If Error_Services('NoError') then
ScansRow<SCANS.NOT_ACCEPTABLE_REASON$> = 'This scan has already been accepted.'
end else
ScansRow<SCANS.NOT_ACCEPTABLE_REASON$> = Error_Services('GetMessage')
end
ScansRow<SCANS.ACCEPTABLE$> = False$
If Assigned(Result) then ScansRow<SCANS.RESULT$> = Result
// Save error message if present as Database_Services will clear any errors.
ErrorMessage = ''
If Error_Services('HasError') then
ErrorMessage = Error_Services('GetMessage')
end
Scan_Services('SetScansRow', ScanID, ScansRow)
// Restore pre-existing error message if present.
If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
end else
Error_Services('Add', 'The accepted.status field is missing from the JSON object in the ':Service:' service.')
end
SRP_JSON(hBody, 'Release')
end else
Error_Services('Add', 'Unable to parse the JSON scan resource in the ':Service:' service.')
end
end else
Error_Services('Add', 'ScanID or ScanJSON argument was missing in the ' : Service : ' service.')
end
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)
jsonScan = ''
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')
If SRP_JSON(arrayScanLog, 'New', 'Array') then
If {SCANNED_DATES} NE '' then
ScannedDates = {SCANNED_DATES}
For Each ScannedDate in scannedDates using @VM setting vPos
objScanSession = ''
If SRP_JSON(objScanSession, 'New', 'Object') then
SRP_JSON(objScanSession, 'SetValue', 'date', Oconv({SCANNED_DATES}<0, vPos>, 'D4/'), 'String')
SRP_JSON(objScanSession, 'SetValue', 'time', Oconv({SCANNED_TIMES}<0, vPos>, 'MTH'), 'String')
SRP_JSON(objScanSession, 'SetValue', 'data', {SCANNED_DATA}<0, vPos>, 'String')
SRP_JSON(arrayScanLog, 'Add', objScanSession)
SRP_JSON(objScanSession, 'Release')
end
Next ScannedDate
end
SRP_JSON(objScan, 'Set', 'scanLog', arrayScanLog)
SRP_JSON(arrayScanLog, 'Release')
end
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
// Created object.
objCreated = ''
If SRP_JSON(objCreated, 'New', 'Object') then
SRP_JSON(objCreated, 'SetValue', 'date', Oconv({CREATED_DATE}, 'D4/'), 'String')
SRP_JSON(objCreated, 'SetValue', 'time', Oconv({CREATED_TIME}, 'MTH'), 'String')
SRP_JSON(objJSONScan, 'Set', 'created', objCreated)
SRP_JSON(objCreated, 'Release')
end
// Employee object
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(objJSONScan, 'Set', 'employee', objEmployee)
SRP_JSON(objEmployee, 'Release')
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 'PRE') ) ) |
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')
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')
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')
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
If itemURL NE '' then
// The itemURL was passed in so add HAL+JSON properties.
objLinks = ''
// Create the _links property and then all link objects needed for this resource.
If SRP_JSON(objLinks, 'New', 'Object') then
// Create a self link.
objLink = ''
If SRP_JSON(objLink, 'New', 'Object') then
SRP_JSON(objLink, 'SetValue', 'href', ItemURL, 'String')
SRP_JSON(objLink, 'SetValue', 'title', 'Self', 'String')
SRP_JSON(objLinks, 'Set', 'self', objLink)
SRP_JSON(objLink, 'Release')
end
SRP_JSON(objJSONScan, 'Set', '_links', objLinks)
SRP_JSON(objLinks, 'Release')
end
objForm = ''
// Create the _form property to help UIs determine what to display.
If SRP_JSON(objForm, 'New', 'Object') then
arrayFields = ''
If SRP_JSON(arrayFields, 'New', 'Array') then
If {EMPLOYEE_ID} NE '' then
objField = ''
If SRP_JSON(objField, 'New', 'Object') then
SRP_JSON(objField, 'SetValue', 'label', 'Employee', 'String')
SRP_JSON(objField, 'SetValue', 'value', {EMPLOYEE_NAME}, 'String')
SRP_JSON(arrayFields, 'Add', objField)
SRP_JSON(objField, 'Release')
end
end
If {LOCATION_ID} NE '' then
If SRP_JSON(objField, 'New', 'Object') then
SRP_JSON(objField, 'SetValue', 'label', 'Location', 'String')
SRP_JSON(objField, 'SetValue', 'value', {LOCATION_NAME}, 'String')
SRP_JSON(arrayFields, 'Add', objField)
SRP_JSON(objField, 'Release')
end
end
If {TOOL_ID} NE '' then
If SRP_JSON(objField, 'New', 'Object') then
SRP_JSON(objField, 'SetValue', 'label', 'Tool', 'String')
ToolName = {TOOL_NAME}
If {LOAD_LOCK} NE '' then ToolName := ' ( ' : {LOAD_LOCK} : ' )'
SRP_JSON(objField, 'SetValue', 'value', ToolName, 'String')
SRP_JSON(arrayFields, 'Add', objField)
SRP_JSON(objField, 'Release')
end
* If {LOAD_LOCK} NE '' then
* If SRP_JSON(objField, 'New', 'Object') then
* SRP_JSON(objField, 'SetValue', 'label', 'Load Lock', 'String')
* SRP_JSON(objField, 'SetValue', 'value', {LOAD_LOCK}, 'String')
* SRP_JSON(arrayFields, 'Add', objField)
* SRP_JSON(objField, 'Release')
* end
* end
end
If {TRANSFER_TOOL_ID} NE '' then
If SRP_JSON(objField, 'New', 'Object') then
SRP_JSON(objField, 'SetValue', 'label', 'Transfer', 'String')
SRP_JSON(objField, 'SetValue', 'value', {TRANSFER_TOOL_NAME}, 'String')
SRP_JSON(arrayFields, 'Add', objField)
SRP_JSON(objField, 'Release')
end
end
If {CASSETTE_IDS} NE '' then
If SRP_JSON(objField, 'New', 'Object') then
SRP_JSON(objField, 'SetValue', 'label', 'Cassette ID', 'String')
If SRP_JSON(arrayCassetteIDs, 'New', 'Array') then
For Each CassetteID in CassetteIDs using @VM
SRP_JSON(arrayCassetteIDs, 'AddValue', CassetteID, 'String')
Next CassetteID
SRP_JSON(objField, 'Set', 'values', arrayCassetteIDs)
SRP_JSON(arrayCassetteIDs, 'Release')
end
SRP_JSON(arrayFields, 'Add', objField)
SRP_JSON(objField, 'Release')
end
end
SRP_JSON(objForm, 'Set', 'fields', arrayFields)
SRP_JSON(arrayFields, 'Release')
end
SRP_JSON(objJSONScan, 'Set', '_form', objForm)
SRP_JSON(objForm, 'Release')
end
// Create the _class property for this resource.
SRP_JSON(objJSONScan, 'SetValue', '_class', 'resource')
end
jsonScan = SRP_JSON(objJSONScan, 'Stringify', 'Styled')
SRP_JSON(objJSONScan, 'Release')
end else
Error_Services('Add', 'Unable to create JSON representation in the ' : Service : ' service.')
end
end
end else
Error_Services('Add', 'ScanID argument was missing in the ' : Service : ' service.')
end
Response = jsonScan
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')
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