Function Wafer_Counter_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 : Wafer_Counter_Services Description : Handler program for all module related services. Notes : The generic parameters should contain all the necessary information to process the services. Often this will be information like the data Record and Key ID. 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/26/23 djs Adapted from FlatFinder_Services. ***********************************************************************************************************************/ #pragma precomp SRP_PreCompiler $Insert LOGICAL $Insert SERVICE_SETUP $Insert RDS_EQUATES $Insert WO_LOG_EQUATES $Insert WM_OUT_EQUATES $Insert RLIST_EQUATES $Insert WAFER_COUNTER_EQUATES Equ Tab$ to \09\ Equ CRLF$ to \0D0A\ Equ LF$ to \0A\ Equ Comma$ to ',' * Reduce Modes Equ new_exist$ To 0 Equ next_cur$ To 1 Equ add_exist$ to 2 Declare subroutine Error_Services, Database_Services, Logging_Services, Httpclient_Services, Reduce, SRP_JSON Declare function Database_Services, Environment_Services, Logging_Services, Httpclient_Services Declare function RTI_CreateGuid, SRP_JSON LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\WaferCounter' LogDate = Oconv(Date(), 'D4/') LogTime = Oconv(Time(), 'MTS') LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Wafer Counter Import Log.csv' Headers = 'Logging DTM':@FM:'ToolID':@FM:'Operation':@FM:'Datetime':@FM:'LotID':@FM:'CassNo':@FM:'Wafer Count':@FM:'Import Result' objLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$) LoggingDTM = LogDate : ' ' : LogTime ; // Logging DTM GoToService else Error_Services('Set', Service : ' is not a valid service request within the ' : ServiceModule : ' services module.') end Return Response else '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Service Parameter Options //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Options BOOLEAN = True$, False$ Options WAFER_SIZES = '6INCH', '8INCH' Options LOCATIONS = 'MU', 'QA', 'EPR-EAST', 'EPR-WEST' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Services //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //---------------------------------------------------------------------------------------------------------------------- // ImportWaferCounterFiles // // Looks for available wafer counter CSV files that are ready to be imported into the RDS table. //---------------------------------------------------------------------------------------------------------------------- Service ImportWaferCounterFiles() hSysLists = Database_Services('GetTableHandle', 'SYSLISTS') Lock hSysLists, ServiceKeyID then DataPath = Environment_Services('GetApplicationRootPath') : '\WaferCounter\' BackupPath = Environment_Services('GetApplicationRootPath') : '\WaferCounter\BackupFiles\' InitDir DataPath : '*.csv' FileList = DirList() For Each File in FileList using @FM OSRead WaferCounterData from DataPath : File then BackupDate = Oconv(Date(), 'D4/') BackupTime = Oconv(Time(), 'MTS') Convert ':' to '-' in BackupTime BackupFileName = BackupDate[7, 4] : '-' : BackupDate[1, 2] : '-' : BackupDate[4, 2] : '-' : BackupTime : ' ' : File If Count(File, '-') LE 1 then OSWrite WaferCounterData to BackupPath : BackupFileName end Swap CRLF$ with '' in WaferCounterData Swap ',' with @FM in WaferCounterData ToolID = Trim(WaferCounterData<2>) Operation = Trim(WaferCounterData<3>) Datetime = IConv(Trim(WaferCounterData<4>), 'DT') ReferenceKeyID = Trim(WaferCounterData<5>) CassetteNo = Trim(WaferCounterData<6>) WaferCount = Trim(WaferCounterData<7>) Result = False$ LogData = LoggingDTM LogData<2> = ToolID LogData<3> = Operation LogData<4> = Trim(WaferCounterData<4>) LogData<5> = ReferenceKeyID LogData<6> = CassetteNo LogData<7> = WaferCount If ReferenceKeyID[1, 2] EQ '1T' or ReferenceKeyID[1, 1] EQ 'O' then Convert 'O' to '' in ReferenceKeyID Swap '1T' with '' in ReferenceKeyID If Index(ReferenceKeyID, '.', 1) then // A dot means this is a WorkOrderNo.Cassette value. Need to create a WM_OUT Key ID. WorkOrderNo = ReferenceKeyID[1, '.'] WOStepKeyID = ReferenceKeyID[Col2() + 1, '.'] CassetteNo = ReferenceKeyID[Col2() + 1, '.'] WMOutKeyID = WorkOrderNo : '*' : WOStepKeyID : '*' : CassetteNo WMOutRow = Database_Services('ReadDataRow', 'WM_OUT', WMOutKeyID) If Error_Services('NoError') then WMOutRow = DateTime WMOutRow = WaferCount Database_Services('WriteDataRow', 'WM_OUT', WMOutKeyID, WMOutRow, True$, False$, True$) end end else // No dot in the reference key means this is an RDS Key ID. Transfer ReferenceKeyID to RDSKeyID If Num(RDSKeyID) then RDSKeyID = RDSKeyID + 0 RDSRow = Database_Services('ReadDataRow', 'RDS', RDSKeyID) If Error_Services('NoError') then RDSRow = DateTime RDSRow = WaferCount Database_Services('WriteDataRow', 'RDS', RDSKeyID, RDSRow, True$, False$, True$) end end If Error_Services('NoError') then Result = 'Success' end else Result = 'Failure: ':Error_Services('GetMessage') end end else // Throw error - either invalid barcode scanned or user manually entered data Result = 'Failure: ReferenceKeyID "':ReferenceKeyID:'" is invalid.' end LogData<8> = Result Logging_Services('AppendLog', objLog, LogData, @RM, @FM) OSDelete DataPath : File end Next File Unlock hSysLists, ServiceKeyID else Null end end service Service ConvertWaferData(FileData) ErrorMsg = '' WaferArray = '' If FileData NE '' then Swap CRLF$ with @FM in FileData WaferTotal = FileData<1> WaferData = FileData<2> If ( (Len(WaferTotal) EQ 3) and (WaferTotal[1, 1] EQ 'T') and (Len(WaferData) EQ 8 ) and (WaferData[1, 1] EQ 'P') ) then Convert 'T' to '' in WaferTotal WaferArray = '' For CharIndex = 2 to 8 Val = IConv(WaferData[CharIndex, 1], 'MX') Val = OConv(Val, 'MB') If CharIndex GT 2 then Val = Fmt(Val, 'R(0)#4') WaferArray := Val Next CharIndex If (Len(WaferArray) NE 25) then ErrorMsg = 'Error in ':Service:' service. Invalid wafer array length.' end else // Reverse the array so it is in order from slot 1 to 25 WaferArray = WaferArray[-1, -25] // Return @VM delimited array for ease of use in OI BASIC+ DelimArray = '' For CharIndex = 1 to 25 DelimArray<0, CharIndex> = WaferArray[CharIndex, 1] Next CharIndex If (Sum(DelimArray) EQ WaferTotal) then Response = DelimArray end else ErrorMsg = 'Error in ':Service:' service. Wafer total does not match array total.' end end end else ErrorMsg = 'Error in ':Service:' service. Invalid data format passed in FileData variable.' end end else ErrorMsg = 'Error in ':Service:' service. Null FileData passed into service.' end If ErrorMsg NE '' then Error_Services('Add', ErrorMsg) end end service Service GetWaferCounterJSON(WaferSize, ToolLocation, CassID) If ( (WaferSize NE '') and (ToolLocation NE '') and (CassID NE '') ) then URL = 'https://oi-metrology-viewer-prod.mes.infineon.com:4438/api/v1/WaferCounter/{waferSize}/last-quantity-and-slot-map/?area={area}&text={cassID}' Swap '{waferSize}' with WaferSize in URL Swap '{area}' with ToolLocation in URL Swap '{cassID}' with CassID in URL // Set timeout to 24 seconds. This value was decided upon with Mike Phares to allow time // for EAF to receive the new wafer counter file from the tool and serve it up. Httpclient_Services('SetTimeoutDuration', 24) Response = Httpclient_Services('SendHTTPRequest', 'GET', URL, '', '', '', '', False$, False$, '') If Error_Services('NoError') then StatusCode = Httpclient_Services('GetResponseStatusCode') If StatusCode NE 200 then Begin Case Case Response EQ '' ErrorMsg = 'Unexpected server error.' Case Response EQ 'No files!' ErrorMsg = 'No data. Please re-seat cassette and try again.' Case Otherwise$ // Use message in response body to populate error services ErrorMsg = Response End Case Error_Services('Add', ErrorMsg) end end end end service Service AddScan(LotID, ScanQty, ScanDtm, ScanTool, ScanUser, ScanLocation, WaferMap) KeyID = RTI_CreateGuid() Record = '' Record = LotID Record = ScanQty If Not(Num(ScanDtm)) then ScanYear = ScanDtm[1, 'F-'] ScanMonth = ScanDtm[Col2() + 1, 'F-'] ScanDay = ScanDtm[Col2() + 1, 'F '] ScanTime = ScanDtm[Col2() + 1, 999] Convert ' ' to '' in ScanTime ScanDtm = ScanMonth:'/':ScanDay:'/':ScanYear:' ':ScanTime ScanDtm = IConv(ScanDtm, 'DT') end Record = ScanDtm Record = ScanTool Record = ScanUser If ScanLocation EQ 'FQA' then ScanLocation = 'QA' Record = ScanLocation Record = WaferMap Database_Services('WriteDataRow', 'WAFER_COUNTER', KeyID, Record) end service Service GetLastScan(LotID, ScanLocation=LOCATIONS) ErrorMsg = '' WCRec = '' If LotID NE '' then Tablename = "WAFER_COUNTER" Flag = "" Done = False$ CursorVar = "" GoSub ClearCursors SortList = "#SCAN_DTM" ReduceScript = 'WITH {LOT_ID} EQ ':Quote(LotID) If ScanLocation NE '' then ReduceScript := ' AND WITH {SCAN_LOCATION} EQ ':Quote(ScanLocation) end Mode = NEXT_CUR$ Reduce(ReduceScript, SortList, Mode, Tablename, Cursorvar, Flag) If Flag then Select Tablename By SortList Using Cursorvar then Open Tablename To hTable then KeyID = '' Loop ReadNext KeyID Using Cursorvar By AT Else Done = TRUE$ If KeyID NE '' then Done = True$ Read WCRec from hTable, KeyID then WCRec = OConv(WCRec, 'DT/^2H') end else ErrorMsg = 'Error in ':Service:' service. Error reading ':KeyID:' from ':Tablename:' table.' end end Until Done or (KeyID NE '') Repeat End Else ErrorMsg = 'Error in ':Service:' service. Error in opening ':Tablename:'.' End End Else ErrorMsg = 'Error in ':Service:' service. Error in selecting ':Tablename:'.' end End Else ErrorMsg = 'Error in ':Service:' service. Error in Reduce call.' End end else ErrorMsg = 'Error in ':Service:' service. Null LotID passed into service.' end If ErrorMsg NE '' then Error_Services('Add', ErrorMsg) end Response = WCRec end service Service GetProdWaferCounter(WaferSize=WAFER_SIZES, Location=LOCATIONS) ToolID = '' ErrorMsg = '' If ( (WaferSize NE '') and (Location NE '') ) then Query = "SELECT TOOL WITH @ID CONTAINING 'WC":WaferSize:"' AND WITH TOOL_LOC EQ '":Location:"' AND WITH CURR_MODE EQ 'PROD'" GoSub ClearCursors RList(Query, TARGET_ACTIVELIST$, '', '', '') ErrCode = '' If Not(Get_Status(ErrCode)) then Begin Case Case @RecCount EQ 0 ErrorMsg = 'Error in ':Service:' service. No production wafer counter found!' Case @RecCount EQ 1 ReadNext ToolID else ErrorMsg = 'Error in ':Service:' service. Error reading production wafer counter tool ID!' end Case @RecCount GT 1 ErrorMsg = 'Error in ':Service:' service. More than one production wafer counter found!' End Case GoSub ClearCursors end else ErrorMsg = 'Error in ':Service:' service. Error calling RList. Error code: ':ErrCode:'.' end end else ErrorMsg = 'Error in ':Service:' service. Null WaferSize or Location passed into service.' end If ErrorMsg NE '' then Error_Services('Add', ErrorMsg) end Response = ToolID end service Service GetWaferCounterToolID(WaferSize=WAFER_SIZES, Location=LOCATIONS) ToolID = '' ErrorMsg = '' If ( (WaferSize NE '') and (Location NE '') ) then Query = "SELECT TOOL WITH @ID CONTAINING 'WC":WaferSize:"' AND WITH TOOL_LOC EQ '":Location:"'" GoSub ClearCursors RList(Query, TARGET_ACTIVELIST$, '', '', '') ErrCode = '' If Not(Get_Status(ErrCode)) then Begin Case Case @RecCount EQ 0 ErrorMsg = 'Error in ':Service:' service. No wafer counter tool found for ':WaferSize:' ':Location:'.' Case @RecCount EQ 1 ReadNext ToolID else ErrorMsg = 'Error in ':Service:' service. Error reading wafer counter tool ID!' end Case @RecCount GT 1 ErrorMsg = 'Error in ':Service:' service. More than one wafer counter tool found for ':WaferSize:' ':Location:'.' End Case GoSub ClearCursors end else ErrorMsg = 'Error in ':Service:' service. Error calling RList. Error code: ':ErrCode:'.' end end else ErrorMsg = 'Error in ':Service:' service. Null WaferSize or Location passed into service.' end If ErrorMsg NE '' then Error_Services('Add', ErrorMsg) end Response = ToolID end service //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Internal GoSubs //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ClearCursors: For counter = 0 to 8 ClearSelect counter Next counter return