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 = Date() ScansRow = Time() ScansRow = ScanType ScansRow = 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 = SupplierLotID //If ( RowExists('RDS', SupplierLotID) EQ True$ ) then // Error_Services('Add', 'Not a valid supplier lot') end else ScansRow = SupplierLotID end Case ScanData[1, 5] EQ 'RESET' ScansRow = 'RESET' ScansRow = '' ScansRow = '' ScansRow = '' ScansRow = '' 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 = 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 = {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 = '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 NE '' then ScansRow = '' end else ScansRow = ScansRow ; // Make sure only the first Cassette ID is tracked. end ScansRow = '' ; // Make sure the Location ID is cleared. ScansRow = ToolID ScansRow = 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 NE '' then ScansRow = '' end else ScansRow = ScansRow ; // Make sure only the first Cassette ID is tracked. end ScansRow = '' ; // Make sure the Location ID is cleared. ScansRow = ToolID ScansRow = 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 = '' ; // Make sure the Location ID is cleared. * ScansRow = ScansRow ; // Make sure only the first Cassette ID is tracked. * ScansRow = 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 = 'LOCATION' If ScansRow NE '' then ScansRow = '' ScansRow = '' ; // Make sure the Tool ID is cleared. ScansRow = '' ; // Make sure the Tool Load Lock is cleared. ScansRow = '' ; // Make sure the Transfer Tool ID is cleared. ScansRow = 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 = '' ; // Make sure the Location ID is cleared. ScansRow = ScansRow ; // Make sure only the first Cassette ID is tracked. ScansRow = BoatID ScansRow = 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 If EmployeeID NE '' then Authenticated = Security_Services('AuthenticateLSLCredentials', EmployeeID, Password) If Authenticated EQ True$ then ScansRow = 1 end else ScansRow = 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 if {EMPLOYEE_ID} NE '' AND ScansRow 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 = 'P' rotrStatusReason = ReactorRec ReactorRec = rotrStatusReason ReactorRec = '' ReactorRec = RDSNo rotrOverrideCount = ReactorRec ReactorRec = rotrOverrideCount + 1 Database_Services('WriteDataRow', 'REACTOR', Reactor, ReactorRec, True$, False$, True$) Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$) ScansRow= '' 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 = 'TENCOR_LOAD' ScansRow= 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 EQ '' then // Only append if it doesn't already exists. Locate CassetteID in ScansRow using @VM setting vPos else ScansRow = CassetteID end end else ScansRow = 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 CassetteIDs = ScansRow NumCass = DCount(CassetteIDs, @VM) EmployeeID = ScansRow 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 CassNo = RDSRec 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 = True$ RDSRec = ScansRow RDSRec = Date() RDSRec = Time() end end If RDSLayerInstAckReq then RDSRec = SRP_JSON(hRDS, 'GetValue', 'rdsLayerInstAck') If PreInstAckReq then RDSRec = SRP_JSON(hRDS, 'GetValue', 'preInstAck') If FwiInstAckReq then RDSRec = SRP_JSON(hRDS, 'GetValue', 'fwiInstAck') If LwiInstAckReq then RDSRec = SRP_JSON(hRDS, 'GetValue', 'lwiInstAck') If QAInstAckReq then RDSRec = SRP_JSON(hRDS, 'GetValue', 'qaInstAck') If LoadInstAckReq then RDSRec = SRP_JSON(hRDS, 'GetValue', 'loadInstAck') If UnloadInstAckReq then RDSRec = SRP_JSON(hRDS, 'GetValue', 'unloadInstAck') If PostInstAckReq then RDSRec = SRP_JSON(hRDS, 'GetValue', 'postInstAck') If WaferCountInstAckReq then RDSRec = 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 Reactor = ToolID[-1, 'BR'] Username = ScansRow WaferCountAck = RDSRec LLSide = ScansRow WaferQty = '' If WaferCountAck EQ True$ then WaferQty = RDSRec end else WaferQty = RDSRec 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 CassNo = RDSRec 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 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 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 RDSSuppLot = RDSRec 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 = 1 ScansRow = ROTRBlockReason ScansRow = 'ROTR' Error_Services('Clear') Scan_Services('AddNotAcceptableReason', "ROTR Load Block Enabled") end else ScansRow = 0 ScansRow = '' ScansRow = '' 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 = '' 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 RDSSuppLot = RDSRec 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 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 = 1 ScansRow = 'ROTR' ScansRow = ROTRBlockReason Error_Services('Clear') Scan_Services('AddNotAcceptableReason', "ROTR Load Block Enabled") end else ScansRow = 0 ScansRow = '' ScansRow = '' 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 = '' 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 = True$ ScansRow = '' end else ScansRow = False$ ScansRow = Scan_Services('GetNotAcceptableReason') end end else ScansRow = False$ ScansRow = Error_Services('GetMessage') end If ScanData NE '' then ScansRow = Date() ScansRow = Time() ScansRow = ScanData end ScansRow = 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 = 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 = AcceptedDate ScansRow = AcceptedTime CassetteIDs = ScansRow ScanType = ScansRow 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 RDSNo= ScansRow 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 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 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 Reactor = ToolID[-1, 'BR'] Username = ScansRow LLSide = ScansRow WaferQty = RDSRec // 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 = 'This scan has already been accepted.' end else ScansRow = Error_Services('GetMessage') end ScansRow = False$ If Assigned(Result) then ScansRow = 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 , '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 , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'supplInst' , RDSRec , 'String') SRP_JSON(objRDS, 'SetValue', 'preInstAckReq' , PreInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'preInstAck' , RDSRec , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'preInst' , PreInst , 'String') SRP_JSON(objRDS, 'SetValue', 'fwiInstAckReq' , FwiInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'fwiInstAck' , RDSRec , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'fwiInst' , FwiInst , 'String') SRP_JSON(objRDS, 'SetValue', 'lwiInstAckReq' , LwiInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'lwiInstAck' , RDSRec , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'lwiInst' , LwiInst , 'String') SRP_JSON(objRDS, 'SetValue', 'qaInstAckReq' , QAInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'qaInstAck' , RDSRec , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'qaInst' , QAInst , 'String') SRP_JSON(objRDS, 'SetValue', 'loadInstAckReq' , LoadInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'loadInstAck' , RDSRec , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'loadInst' , LoadInst , 'String') SRP_JSON(objRDS, 'SetValue', 'unloadInstAckReq' , UnloadInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'unloadInstAck' , RDSRec , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'unloadInst' , UnloadInst , 'String') SRP_JSON(objRDS, 'SetValue', 'postInstAckReq' , PostInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'postInstAck' , RDSRec , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'postInst' , PostInst , 'String') SRP_JSON(objRDS, 'SetValue', 'waferCountInstAckReq' , WaferCountInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'waferCountInstAck' , RDSRec , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'waferCountInst' , WaferCountInst , 'String') SRP_JSON(objRDS, 'SetValue', 'rdsLayerInstAckReq' , RDSLayerInstAckReq , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'rdsLayerInstAck' , RDSRec , 'Boolean') OverrideRequired = @Record OverrideType = @Record OverrideReason = @Record SRP_JSON(objRDS, 'SetValue', 'overRideAckReq' , OverrideRequired , 'Boolean') SRP_JSON(objRDS, 'SetValue', 'overRideType' , OverrideType , 'string') SRP_JSON(objRDS, 'SetValue', 'overRideReason', @Record, '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 = SRP_JSON(objJSONScan, 'GetValue', 'created.date') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'created.time') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'scan.type') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'employee.ID') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'location.ID') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'tool.ID') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'tool.loadLock') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'accepted.status', False$) mvScan = SRP_JSON(objJSONScan, 'GetValue', 'accepted.date') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'accepted.time') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'action') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'waferCount.confirmed', False$) mvScan = SRP_JSON(objJSONScan, 'GetValue', 'transfer.boatID') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'transfer.plNumber') mvScan = SRP_JSON(objJSONScan, 'GetValue', 'transfer.unloadplConfirmed', False$) mvScan = SRP_JSON(objJSONScan, 'GetValue', 'transfer.tool.ID') arrayCassettes = SRP_JSON(objJSONScan, 'Get', 'cassetteIDs') NumCassettes = SRP_JSON(arrayCassettes, 'GetCount') For CassetteCnt = 1 to NumCassettes mvScan = 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 = SRP_JSON(objScan, 'GetValue', 'date') mvScan = SRP_JSON(objScan, 'GetValue', 'time') mvScan = 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 = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' 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 = '' WOMatRec = '' Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$) end end end else RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' RDSRec = '' 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 = '' WOMatRec = '' Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$) end end RDSLayerKeys = RDSRec For each RDSLayerKey in RDSLayerKeys using @VM RDSLayerRec = Database_Services('ReadDataRow', 'RDS_LAYER', RDSLayerKey) * RDSLayerRec = '' * RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' RDSLayerRec = '' 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