Compile function Met_Test_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 : Met_Test_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 History : (Date, Initials, Notes) 05/13/25 djs Original developer ***********************************************************************************************************************/ #pragma precomp SRP_PreCompiler $Insert SERVICE_SETUP $Insert APP_INSERTS $Insert MET_TEST_EQUATES $Insert MET_TEST_DATA_EQUATES $Insert MET_TEST_INSERTS $Insert DICT_EQUATES $Insert TOOL_EQUATES $Insert LOT_OPERATION_EQUATES Declare function RTI_CreateGuid, Error_Services, Tool_Services, Database_Services, SRP_Array, SRP_JSON, Datetime Declare function Met_Test_Services, Date_Services Declare subroutine Database_Services, Error_Services, Btree.Extract, SRP_JSON, Extract_Si_Keys, Lot_Event_Services GoToService Return Response or "" //---------------------------------------------------------------------------------------------------------------------- // Service Parameter Options //---------------------------------------------------------------------------------------------------------------------- Options LAYERS = '1', '2', '3' Options ZONES = '1', '2' Options INSPECTION_RECIPES = 'PRE', 'LOAD', 'FWI', 'UNLOAD', 'LWI', 'POST', 'QA' //---------------------------------------------------------------------------------------------------------------------- // Services //---------------------------------------------------------------------------------------------------------------------- Service ManualMetTestEntry(ToolId, LotId, Recipe, Pattern, RunDataLayer, RunDataZone, Positions, RawDataPoints) ToolClass = XLATE('TOOL', ToolId, 'CLASS', 'X') RDSKeyId = '' MetTestId = Met_Test_Services('CreateMetTest', LotId, RDSKeyId, ToolClass, Recipe, Pattern, Layer, RunDataZone) Met_Test_Services('SetPropsAndLimits', MetTestId) Met_Test_Services('SetMetTestTool', MetTestId, ToolId) Met_Test_Services('SetMetTestDtm', MetTestId, DateTime()) for each DataPoint in RawDataPoints using @VM setting vPos position = Positions<0, vPos> Met_Test_Services('AddMetTestData', MetTestId, Position, DataPoint) Next DataPoint end service //---------------------------------------------------------------------------------------------------------------------- // CreateMetTest // // Input: // LotId [Required] - Key of LOT_ID record to relate MET_TEST record to. // LegacyLotId [Optional] - Key of legacy lot id (e.g., RDS, WO_MAT, WM_OUT) to relate MET_TEST record to // ToolClass [Optional] - TOOL_CLASS of metrology test. Used for PRS_PROP spec lookup. // Required parameter for RDS_TEST metrology. // ToolRecipe [Optional] - Recipe of metrology test prescribed in PSN and selected on metrology tool. // This can be a recipe from an RDS_TEST, CLEAN_INSP, or WO_MAT_QA metrology test. // Required parameter for metrology tests (i.e., not visual inspections)! // ToolPattern [Optional] - Pattern of metrology test prescribed in PSN and selected on metrology tool. // This will drive the sample size (i.e., number of data points) to require in order // for the test to be considered "complete". // Layer [Optional] - Layer of metrology test. Required for RDS_TEST metrology. // Note: This should be 1, 2, or 3 to match PRS_PROP keys. Not L1, L2, or 2. // Zone [Optional] - Zone of metrology test. Should be null (""), 1, or 2 // InspectionRecipe [Optional] - PRS_STAGE to retrieve visual inspection specs from. // Required parameter for CLEAN_INSP visual inspections. // // Output: // If successful, returns the key of the MET_TEST record created. // // Note: // The SAMPLE_SIZE field derived from the ToolPattern may refer to the number of data points on a // single wafer (e.g., RDS_TEST metrology) or the number of wafers in a cassette (e.g., surfscan). // // Creates a parent MET_TEST record related to a LOT_ID record. //---------------------------------------------------------------------------------------------------------------------- Service CreateMetTest(LotId, LegacyLotId, ToolClass, ToolRecipe, ToolPattern, Layer=LAYERS, Zone=ZONES, InspectionRecipe=INSPECTION_RECIPES) MetTestRec = '' MetTestId = '' ErrorMsg = '' If (LotId NE '') then If RowExists('LOT', LotId) then If Layer NE '' then Swap 2 with 3 in Layer Swap 'L2' with 2 in Layer Swap 'L1' with 1 in Layer end // Create MET_TEST key and write record MetTestId = RTI_CreateGuid() MetTestRec = LotId MetTestRec = LegacyLotId MetTestRec = ToolClass MetTestRec = ToolRecipe MetTestRec = ToolPattern MetTestRec = Layer MetTestRec = Zone MetTestRec = InspectionRecipe Database_Services('WriteDataRow', 'MET_TEST', MetTestId, MetTestRec) If Error_Services('HasError') then ErrorMsg = 'Error in ':Service:' service. Error writing MET_TEST record. Error message: ':Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. LotId "':LotId:'" does not exist in the LOT table.' end end else ErrorMsg = 'Error in ':Service:' service. Null LotId passed into service.' end If ErrorMsg EQ '' then Response = MetTestId end else Error_Services('Add', ErrorMsg) end End Service //---------------------------------------------------------------------------------------------------------------------- // AddMetTestData // // Description: // Creates a child MET_TEST_DATA record related to a parent MET_TEST record. // // Input: // MetTestId [Required] - Key of MET_TEST parent record // Position [Optional] - Integer indicating an order or values (e.g., wafer slot or point value of point map) // PropertyVal1 [Optional] - Metrology value associated with PROPERTY_1 of parent MET_TEST record // ... // ... // ... // PropertyVal15 [Optional] - Metrology value associated with PROPERTY_15 of parent MET_TEST record // // Output: // Returns true if successful, false otherwise. // // Notes: // At least one property value is required // Extend PropertyVal input fields as MET_TEST PropertyVal fields are added (e.g., PropertyVal16, PropertyVal17, ...) //---------------------------------------------------------------------------------------------------------------------- Service AddMetTestData(MetTestId, Position, PropertyVal1, PropertyVal2, PropertyVal3, PropertyVal4, PropertyVal5, PropertyVal6, PropertyVal7, PropertyVal8, PropertyVal9, PropertyVal10, PropertyVal11, PropertyVal12, PropertyVal13, PropertyVal14, PropertyVal15) ErrorMsg = '' If ( (MetTestId NE '') and (PropertyVal1 NE '') or (PropertyVal2 NE '') or (PropertyVal3 NE '') or (PropertyVal4 NE '') | or (PropertyVal5 NE '') or (PropertyVal6 NE '') or (PropertyVal7 NE '') or (PropertyVal8 NE '') or (PropertyVal9 NE '') | or (PropertyVal10 NE '') or (PropertyVal11 NE '') or (PropertyVal12 NE '') or (PropertyVal13 NE '') or (PropertyVal14 NE '') | or (PropertyVal15 NE '') ) then If RowExists('MET_TEST', MetTestId) then MetTestDataKey = RTI_CreateGuid() MetTestDataRec = '' MetTestDataRec = MetTestId MetTestDataRec = Position MetTestDataRec = PropertyVal1 MetTestDataRec = PropertyVal2 MetTestDataRec = PropertyVal3 MetTestDataRec = PropertyVal4 MetTestDataRec = PropertyVal5 MetTestDataRec = PropertyVal6 MetTestDataRec = PropertyVal7 MetTestDataRec = PropertyVal8 MetTestDataRec = PropertyVal9 MetTestDataRec = PropertyVal10 MetTestDataRec = PropertyVal11 MetTestDataRec = PropertyVal12 MetTestDataRec = PropertyVal13 MetTestDataRec = PropertyVal14 MetTestDataRec = PropertyVal15 Database_Services('WriteDataRow', 'MET_TEST_DATA', MetTestDataKey, MetTestDataRec) If Error_Services('HasError') then ErrorMsg = 'Error in ':Service:' service. Failed to write MET_TEST_DATA record. Error message: ':Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. Parent MET_TEST record "':MetTestId:'" does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId, PropertyVal1, PropertyVal2, PropertyVal3, or PropertyVal4 passed into service' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // SetPropsAndLimits // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // // Output: // Returns true if successful, false otherwise. // // Searches PROD_SPEC, PRS_PROP, and PRS_STAGe for matching property names and limits to apply to columns // PROPERTY_1, PROPERTY_1_MIN, PROPERTY_1_MAX, ... PROPERTY_N, PROPERTY_N_MIN, PROPERTY_N_MAX. //---------------------------------------------------------------------------------------------------------------------- Service SetPropsAndLimits(MetTestId) ErrorMsg = '' If (MetTestId NE '') then If RowExists('MET_TEST', MetTestId) then Open 'MET_TEST' to hTable then Met_Test_Services('ClearPropsAndLimits', MetTestId) If Error_Services('NoError') then MetTestRec = Database_Services('ReadDataRow', 'MET_TEST', MetTestId) If Error_Services('NoError') then LotId = MetTestRec If (LotId NE '') then If RowExists('LOT', LotId) then // Find specs from provided recipe, pattern, tool class, layer, etc. PSNo = Xlate('LOT', LotId, 'PROD_SPEC_ID', 'X') If PSNo NE '' then If RowExists('PROD_SPEC', PSNo) then SpecFound = False$ Layer = MetTestRec ToolRecipe = MetTestRec ToolPattern = MetTestRec Tool = MetTestRec ToolClass = MetTestRec InspectionRecipe = MetTestRec If InspectionRecipe NE '' then // Get Inspection values from PRS_STAGE record PRSStageKey = PSNo:'*':InspectionRecipe If RowExists('PRS_STAGE', PRSStageKey) then MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'LPD', 'X') If MetTestRec NE '' then MetTestRec = 'LPD' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'SCRATCHES', 'X') If MetTestRec NE '' then MetTestRec = 'Scratches' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'SCRATCH_LEN', 'X') If MetTestRec NE '' then MetTestRec = 'Scratch Len' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'PITS', 'X') If MetTestRec NE '' then MetTestRec = 'Pits' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'MOUNDS', 'X') If MetTestRec NE '' then MetTestRec = 'Mounds' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'STACK_FAULTS', 'X') If MetTestRec NE '' then MetTestRec = 'Stack Faults' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'SPIKES', 'X') If MetTestRec NE '' then MetTestRec = 'Spikes' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'SPOTS', 'X') If MetTestRec NE '' then MetTestRec = 'Spots' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'FOV', 'X') If MetTestRec NE '' then MetTestRec = 'FOV' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'BL_DEFECTS', 'X') If MetTestRec NE '' then MetTestRec = 'BL Defects' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'BSIDE_SCRATCHES', 'X') If MetTestRec NE '' then MetTestRec = 'Backside Scratches' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'BSIDE_SCRATCH_LEN', 'X') If MetTestRec NE '' then MetTestRec = 'Backside Scratch Len' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'BSIDE_NODULES', 'X') If MetTestRec NE '' then MetTestRec = 'Backside Nodules' MetTestRec = Xlate('PRS_PROP', PRSPropKey, 'BSIDE_SPIKES', 'X') If MetTestRec NE '' then MetTestRec = 'Backside Spikes' If ( Xlate('PRS_PROP', PRSPropKey, 'EDGE', 'X') EQ 1 ) then MetTestRec = 0 If MetTestRec NE '' then MetTestRec = 'Edge Defects' MetTestRec = 1 end else ErrorMsg = 'Error in ':Service:' service. Error finding inspection specs. PRS_STAGE record "':PRSStageKey:'" does not exist.' end end else Open 'DICT.PRS_PROP' to dPRSProp then Open 'DICT.PRS_STAGE' to dPRSStage then // Search PRS_PROP for matching layer, recipe, tool class, and tool pattern Query = 'PS_NO':@VM:PSNo:@FM If Layer NE '' then Query := 'LAYER_NO':@VM:Layer:@FM If ToolRecipe NE '' then Query := 'MET_RECIPE':@VM:ToolRecipe:@FM If ToolClass NE '' then Query := 'TOOL':@VM:ToolClass:@FM If ToolPattern NE '' then Query := 'MET_RECIPE_PATTERN':@VM:ToolPattern:@FM PRSPropKey = '' PRSPropKeys = '' Flag = '' Btree.Extract(Query, 'PRS_PROP', dPRSProp, PRSPropKeys, 'E', Flag) If Flag EQ 0 then If (DCount(PRSPropKeys, @VM) EQ 1) then SpecFound = True$ PRSPropKey = PRSPropKeys // Get RAW_MIN and RAW_MAX from PRS_PROP record (MD5) MetTestRec = Field(PRSPropKey, '*', 3, 1) MetTestRec = OConv(Xlate('PRS_PROP', PRSPropKey, 'RAW_MIN', 'X'), 'MD5') MetTestRec = OConv(Xlate('PRS_PROP', PRSPropKey, 'RAW_MAX', 'X'), 'MD5') SpecToolPattern = Xlate('PRS_PROP', PRSPropKey, 'MET_RECIPE_PATTERN', 'X') If SpecToolPattern NE '' then SpecToolClass = Xlate('PRS_PROP', PRSPropKey, 'TOOL', 'X') If SpecToolClass NE '' then SampleSize = Tool_Services('GetNumPoints', SpecToolClass, SpecToolPattern) If Error_Services('NoError') then If SampleSize EQ '' then SampleSize = 1 MetTestRec = SampleSize end else ErrorMsg = Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. TOOL (i.e., TOOL_CLASS) in PRS_PROP "':PRSPropKey:'" not set.' end end else ErrorMsg = 'Error in ':Service:' service. MET_RECIPE_PATTERN in PRS_PROP "':PRSPropKey:'" not set.' end end end If Not(SpecFound) then // Search PRS Stage for matching SURFSCAN_RECIPE Query = 'PS_NO':@VM:PSNo:@FM If ToolRecipe NE '' then Query := 'SURFSCAN_RECIPE':@VM:ToolRecipe:@FM PRSStageKey = '' PRSStageKeys = '' Flag = '' Btree.Extract(Query, 'PRS_STAGE', dPRSStage, PRSStageKeys, 'E', Flag) If Flag EQ 0 then If (DCount(PRSStageKeys, @VM) EQ 1) then PRSStageKey = PRSStageKeys PRSStageSpecSurfscanRecipes = Xlate('PRS_STAGE', PRSStageKey, 'SURFSCAN_RECIPE', 'X') If PRSStageSpecSurfscanRecipes NE '' then For each PRSStageSpecSurfscanRecipe in PRSStageSpecSurfscanRecipes using @VM Setting SpecSurfIndex SpecFound = (PRSStageSpecSurfscanRecipe EQ ToolRecipe) If SpecFound then // Get sum of defects max (SURF_DEFECTS - MD0) and haze max (SURF_HAZE - MD2) from PRS_STAGE record (there is no min spec) MetTestRec = 'SURFACE_SCAN_SUM_OF_DEFECTS_MIN' MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'SURF_DEFECTS', 'X')<0, SpecSurfIndex> MetTestRec = 'SURFACE_SCAN_SUM_OF_DEFECTS_MAX' MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'SURF_DEFECTS', 'X')<0, SpecSurfIndex> MetTestRec = 'SURFACE_SCAN_SUM_OF_DEFECTS_AVG' MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'SURF_DEFECTS', 'X')<0, SpecSurfIndex> MetTestRec = 'SURFACE_SCAN_SUM_OF_DEFECTS' MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'SURF_DEFECTS', 'X')<0, SpecSurfIndex> MetTestRec = 'SURFACE_SCAN_HAZE_AVG' MetTestRec = IConv(Xlate('PRS_STAGE', PRSStageKey, 'SURF_HAZE', 'X')<0, SpecSurfIndex>, 'MD2') MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'SS_SAMP_QTY', 'X')<0, SpecSurfIndex> end Until SpecFound Next PRSStageSpecSurfscanRecipe end end end else ErrorMsg = 'Error in ':Service:' service. Error calling Btree.Extract on PRS_STAGE table.' end end If Not(SpecFound) then // Search PRS Stage for matching MET_RECIPE and optionally, matching MET_PATTERN Query = 'PS_NO':@VM:PSNo:@FM If ToolRecipe NE '' then Query := 'MET_RECIPE':@VM:ToolRecipe:@FM If ToolPattern NE '' then Query := 'MET_RECIPE_PATTERN':@VM:ToolPattern:@FM PRSStageKey = '' PRSStageKeys = '' Flag = '' Btree.Extract(Query, 'PRS_STAGE', dPRSStage, PRSStageKeys, 'E', Flag) If Flag EQ 0 then If (DCount(PRSStageKeys, @VM) EQ 1) then PRSStageKey = PRSStageKeys PRSStageSpecMetRecipes = Xlate('PRS_STAGE', PRSStageKey, 'MET_RECIPE', 'X') PRSStageSpecMetPatterns = Xlate('PRS_STAGE', PRSStageKey, 'MET_RECIPE_PATTERN', 'X') If PRSStageSpecMetRecipes NE '' then For each PRSStageSpecMetRecipe in PRSStageSpecMetRecipes using @VM setting SpecMetIndex If ToolPattern NE '' then PRSStageSpecMetPattern = PRSStageSpecMetPatterns<0, SpecMetIndex> SpecFound = ( (PRSStageSpecMetRecipe EQ ToolRecipe) and (PRSStageSpecMetPattern = ToolPattern) ) end else SpecFound = (PRSStageSpecMetRecipe EQ ToolRecipe) end If SpecFound then // Get min and max from MET_MIN and MET_MAX in PRS_STAGE record (MD0) MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'MET_PROP', 'X')<0, SpecMetIndex> MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'MET_MIN', 'X')<0, SpecMetIndex> MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'MET_MAX', 'X')<0, SpecMetIndex> If MetTestRec EQ 'CRES' then MetTestRec = 'PHASE' MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'MET_PHASE_MIN', 'X')<0, SpecMetIndex> end SpecToolPattern = Xlate('PRS_STAGE', PRSStageKey, 'MET_RECIPE_PATTERN', 'X')<0, SpecMetIndex> If SpecToolPattern NE '' then SpecToolClass = Xlate('PRS_STAGE', PRSStageKey, 'MET_TOOL_CLASS', 'X')<0, SpecMetIndex> If SpecToolClass NE '' then SampleSize = Tool_Services('GetNumPoints', SpecToolClass, SpecToolPattern) If Error_Services('NoError') then If SampleSize EQ '' then SampleSize = 1 MetTestRec = SampleSize end else ErrorMsg = Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. MET_TOOL_CLASS in PRS_STAGE "':PRSStageKey:'" not set.' end end else ErrorMsg = 'Error in ':Service:' service. MET_RECIPE_PATTERN in PRS_STAGE "':PRSStageKey:'" not set.' end end Until SpecFound Next PRSStageSpecMetRecipe end end end else ErrorMsg = 'Error in ':Service:' service. Error calling Btree.Extract on PRS_STAGE table.' end end If Not(SpecFound) then // Last resort - look up matching spec limits by PSN and tool class Query = 'PS_NO':@VM:PSNo:@FM If ToolClass NE '' then Query := 'MET_TOOL_CLASS':@VM:ToolClass:@FM PRSStageKey = '' PRSStageKeys = '' Flag = '' Btree.Extract(Query, 'PRS_STAGE', dPRSStage, PRSStageKeys, 'E', Flag) If Flag EQ 0 then If (DCount(PRSStageKeys, @VM) EQ 1) then PRSStageKey = PRSStageKeys PRSStageSpecToolClasses = Xlate('PRS_STAGE', PRSStageKey, 'MET_TOOL_CLASS', 'X') If PRSStageSpecToolClasses NE '' then For each PRSStageSpecToolClass in PRSStageSpecToolClasses using @VM setting SpecMetIndex SpecFound = (PRSStageSpecToolClass EQ ToolClass) If SpecFound then // Get min and max from MET_MIN and MET_MAX in PRS_STAGE record (MD0) MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'MET_PROP', 'X')<0, SpecMetIndex> MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'MET_MIN', 'X')<0, SpecMetIndex> MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'MET_MAX', 'X')<0, SpecMetIndex> If MetTestRec EQ 'CRES' then MetTestRec = 'PHASE' MetTestRec = Xlate('PRS_STAGE', PRSStageKey, 'MET_PHASE_MIN', 'X')<0, SpecMetIndex> end SpecToolPattern = Xlate('PRS_STAGE', PRSStageKey, 'MET_RECIPE_PATTERN', 'X')<0, SpecMetIndex> If SpecToolPattern NE '' then SpecToolClass = Xlate('PRS_STAGE', PRSStageKey, 'MET_TOOL_CLASS', 'X')<0, SpecMetIndex> If SpecToolClass NE '' then SampleSize = Tool_Services('GetNumPoints', SpecToolClass, SpecToolPattern) If Error_Services('NoError') then If SampleSize EQ '' then SampleSize = 1 MetTestRec = SampleSize end else ErrorMsg = Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. MET_TOOL_CLASS in PRS_STAGE "':PRSStageKey:'" not set.' end end else ErrorMsg = 'Error in ':Service:' service. MET_RECIPE_PATTERN in PRS_STAGE "':PRSStageKey:'" not set.' end end Until SpecFound Next PRSStageSpecMetRecipe end end end else ErrorMsg = 'Error in ':Service:' service. Error calling Btree.Extract on PRS_STAGE table.' end If Not(SpecFound) then // Search PRS_PROP for matching PSN, layer, and tool class Query = 'PS_NO':@VM:PSNo:@FM If Layer NE '' then Query := 'LAYER_NO':@VM:Layer:@FM If ToolClass NE '' then Query := 'TOOL':@VM:ToolClass:@FM PRSPropKey = '' PRSPropKeys = '' Flag = '' Btree.Extract(Query, 'PRS_PROP', dPRSProp, PRSPropKeys, 'E', Flag) If Flag EQ 0 then If (DCount(PRSPropKeys, @VM) EQ 1) then SpecFound = True$ PRSPropKey = PRSPropKeys // Get RAW_MIN and RAW_MAX from PRS_PROP record (MD5) MetTestRec = Field(PRSPropKey, '*', 3, 1) MetTestRec = OConv(Xlate('PRS_PROP', PRSPropKey, 'RAW_MIN', 'X'), 'MD5') MetTestRec = OConv(Xlate('PRS_PROP', PRSPropKey, 'RAW_MAX', 'X'), 'MD5') SpecToolPattern = Xlate('PRS_PROP', PRSPropKey, 'MET_RECIPE_PATTERN', 'X') If SpecToolPattern NE '' then SpecToolClass = Xlate('PRS_PROP', PRSPropKey, 'TOOL', 'X') If SpecToolClass NE '' then SampleSize = Tool_Services('GetNumPoints', SpecToolClass, SpecToolPattern) If Error_Services('NoError') then If SampleSize EQ '' then SampleSize = 1 MetTestRec = SampleSize end else ErrorMsg = Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. TOOL (i.e., TOOL_CLASS) in PRS_PROP "':PRSPropKey:'" not set.' end end else ErrorMsg = 'Error in ':Service:' service. MET_RECIPE_PATTERN in PRS_PROP "':PRSPropKey:'" not set.' end end end else ErrorMsg = 'Error in ':Service:' service. Error calling Btree.Extract on PRS_PROP table.' end end end end else ErrorMsg = 'Error in ':Service:' service. Error opening DICT.PRS_STAGE table.' end end else ErrorMsg = 'Error in ':Service:' service. Error opening DICT.PRS_PROP table.' end If ErrorMsg EQ '' then If SpecFound then // Update MET_TEST record Database_Services('WriteDataRow', 'MET_TEST', MetTestId, MetTestRec) If Error_Services('HasError') then ErrorMsg = 'Error in ':Service:' service. Error writing MET_TEST record. Error message: ':Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. Error finding metrology specs.' end end end end else ErrorMsg = 'Error in ':Service:' service. Error retrieving metrology specs. PROD_SPEC "':PSNo:'" does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Error retrieving metrology specs. Null PROD_SPEC_ID in LOT record "':LotId:'".' end end else ErrorMsg = 'Error in ':Service:' service. LotId "':LotId:'" does not exist in the LOT table.' end end else ErrorMsg = 'Error in ':Service:' service. Null LotId passed into service.' end end else ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. Failed to open MET_TEST table.' end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // ClearPropsAndLimits // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // // Output: // Returns true if successful, false otherwise. // // Clears columns PROPERTY_1, PROPERTY_1_MIN, PROPERTY_1_MAX, ... PROPERTY_N, PROPERTY_N_MIN, PROPERTY_N_MAX. //---------------------------------------------------------------------------------------------------------------------- Service ClearPropsAndLimits(MetTestId) ErrorMsg = '' If (MetTestId NE '') then If RowExists('MET_TEST', MetTestId) then MetTestRec = Database_Services('ReadDataRow', 'MET_TEST', MetTestId) If Error_Services('NoError') then For PropertyIndex = 1 to NUM_PROPERTIES$ PropColNo = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex, DICT_COLUMN_NO$, 'X') PropMinColNo = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex:'_SPEC_MIN', DICT_COLUMN_NO$, 'X') PropMaxColNo = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex:'_SPEC_MAX', DICT_COLUMN_NO$, 'X') MetTestRec = '' MetTestRec = '' MetTestRec = '' Next PropertyIndex Database_Services('WriteDataRow', 'MET_TEST', MetTestId, MetTestRec, True$, False$, False$) If Error_Services('HasError') then ErrorMsg = Error_Services('GetMessage') end end else ErrorMsg = Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId or ToolId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // SetMetTestRecipe // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // MetRecipe [Required] - Recipe name of metrology test. // // Output: // Returns true if successful, false otherwise. // // Sets the TOOL_RECIPE column of a MET_TEST record. //---------------------------------------------------------------------------------------------------------------------- Service SetMetTestRecipe(MetTestId, MetRecipe) ErrorMsg = '' If ( (MetTestId NE '') and (MetRecipe NE '') ) then If RowExists('MET_TEST', MetTestId) then Open 'MET_TEST' to hTable then WriteV MetRecipe on hTable, MetTestId, MET_TEST.TOOL_RECIPE$ else ErrorMsg = 'Error in ':Service:' service. Failed to write ':MetRecipe:' on TOOL_RECIPE column for MET_TEST ':MetTestId end end else ErrorMsg = 'Error in ':Service:' service. Failed to open MET_TEST table.' end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId or ToolId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // SetMetTestPattern // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // MetPattern [Required] - Pattern name of metrology test. // // Output: // Returns true if successful, false otherwise. // // Sets the TOOL_PATTERN column of a MET_TEST record. //---------------------------------------------------------------------------------------------------------------------- Service SetMetTestPattern(MetTestId, MetPattern) ErrorMsg = '' If ( (MetTestId NE '') and (MetPattern NE '') ) then If RowExists('MET_TEST', MetTestId) then Open 'MET_TEST' to hTable then WriteV MetPattern on hTable, MetTestId, MET_TEST.TOOL_PATTERN$ else ErrorMsg = 'Error in ':Service:' service. Failed to write ':MetPattern:' on TOOL_PATTERN column for MET_TEST ':MetTestId end end else ErrorMsg = 'Error in ':Service:' service. Failed to open MET_TEST table.' end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId or ToolId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // SetMetTestTool // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // ToolId [Required] - Key of TOOL record, which represents the tool the metrology test was performed on. // // Output: // Returns true if successful, false otherwise. // // Sets the TOOL column of a MET_TEST record. //---------------------------------------------------------------------------------------------------------------------- Service SetMetTestTool(MetTestId, ToolId) ErrorMsg = '' If ( (MetTestId NE '') and (ToolId NE '') ) then If RowExists('MET_TEST', MetTestId) then Open 'MET_TEST' to hTable then WriteV ToolId on hTable, MetTestId, MET_TEST.TOOL$ else ErrorMsg = 'Error in ':Service:' service. Failed to write ':ToolId:' on TOOL column for MET_TEST ':MetTestId end end else ErrorMsg = 'Error in ':Service:' service. Failed to open MET_TEST table.' end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId or ToolId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // SetMetTestDtm // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // TestDtm [Required] - Datetime of the metrology test. Can be internal or external datetime value. // // Output: // Returns true if successful, false otherwise. // // Sets the TEST_DTM column of a MET_TEST record. //---------------------------------------------------------------------------------------------------------------------- Service SetMetTestDtm(MetTestId, TestDtm) ErrorMsg = '' If ( (MetTestId NE '') and (TestDtm NE '') ) then If RowExists('MET_TEST', MetTestId) then Open 'MET_TEST' to hTable then If Not(Num(TestDtm)) then Swap ' AM' with 'AM' in TestDtm Swap ' PM' with 'PM' in TestDtm TestDtm = IConv(TestDtm,'DT') end WriteV TestDtm on hTable, MetTestId, MET_TEST.TEST_DTM$ else ErrorMsg = 'Error in ':Service:' service. Failed to write ':OConv(TestDtm, 'DT2/^H') ErrorMsg := ' on TEST_DTM column for MET_TEST ':MetTestId end end else ErrorMsg = 'Error in ':Service:' service. Failed to open MET_TEST table.' end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId or ToolId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // SetMetTestSlot // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // Slot [Required] - Slot number of metrology test. N/A to cassette level tests like Tencor surface scans. // // Output: // Returns true if successful, false otherwise. // // Sets the SLOT column of a MET_TEST record. //---------------------------------------------------------------------------------------------------------------------- Service SetMetTestSlot(MetTestId, Slot) ErrorMsg = '' If ( (MetTestId NE '') and (Slot NE '') ) then If RowExists('MET_TEST', MetTestId) then Open 'MET_TEST' to hTable then WriteV Slot on hTable, MetTestId, MET_TEST.SLOT$ else ErrorMsg = 'Error in ':Service:' service. Failed to write ':Slot:' on SLOT column for MET_TEST ':MetTestId end end else ErrorMsg = 'Error in ':Service:' service. Failed to open MET_TEST table.' end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId or ToolId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // AddProperty (aka parameter) // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // PropName [Required] - Slot number of metrology test. N/A to cassette level tests like Tencor surface scans. // PropMin [Optional] - Spec minimum value to validate the property values against. // PropMax [Optional] - Spec maxiumum value to validate the property values against. // // Output: // Returns true if successful, false otherwise. // // Adds a property to a MET_TEST record. // Note: If a PropMin and PropMax are not set for the property, it will not be validated within MET_TEST_DATA actions. //---------------------------------------------------------------------------------------------------------------------- Service AddProperty(MetTestId, PropName, PropMin, PropMax) ErrorMsg = '' If ( (MetTestId NE '') and (PropName NE '') ) then If RowExists('MET_TEST', MetTestId) then MetTestRec = Database_Services('ReadDataRow', 'MET_TEST', MetTestId) If Error_Services('NoError') then PosFound = False$ PropAlreadyPresent = False$ For PropertyIndex = 1 to NUM_PROPERTIES$ ThisPropName = Xlate('MET_TEST', MetTestId, "PROPERTY_":PropertyIndex, 'X') If ( (ThisPropName EQ '') and Not(PropAlreadyPresent) ) then PosFound = True$ PropNameIndex = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex, DICT_COLUMN_NO$, 'X') PropMinIndex = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex:'_SPEC_MIN', DICT_COLUMN_NO$, 'X') PropMaxIndex = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex:'_SPEC_MAX', DICT_COLUMN_NO$, 'X') end If (ThisPropName EQ PropName) then PropAlreadyPresent = True$ Until PropAlreadyPresent or PosFound Next PropertyIndex Begin Case Case PropAlreadyPresent ErrorMsg = 'Error in ':Service:' service. Property ':PropName:' already exists in MET_TEST ':MetTestId Case PosFound MetTestRec = PropName MetTestRec = PropMin MetTestRec = PropMax Database_Services('WriteDataRow', 'MET_TEST', MetTestId, MetTestRec, True$, False$, False$) Case Otherwise$ ErrorMsg = 'Error in ':Service:' service. Failed to find empty property column in MET_TEST ':MetTestId End Case end else ErrorMsg = 'Error in ':Service:' service. Error reading MET_TEST ':MetTestId:'. Error message: ':Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId, PropName, or PropMin/PropMax passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // RemoveProperty // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // PropName [Required] - Slot number of metrology test. N/A to cassette level tests like Tencor surface scans. // // Output: // Returns true if successful, false otherwise. // // Removes a property from a MET_TEST record. //---------------------------------------------------------------------------------------------------------------------- Service RemoveProperty(MetTestId, PropName) ErrorMsg = '' If ( (MetTestId NE '') and (PropName NE '') ) then If RowExists('MET_TEST', MetTestId) then MetTestRec = Database_Services('ReadDataRow', 'MET_TEST', MetTestId) If Error_Services('NoError') then PosFound = False$ For PropertyIndex = 1 to NUM_PROPERTIES$ ThisPropName = Xlate('MET_TEST', MetTestId, "PROPERTY_":PropertyIndex, 'X') If (ThisPropName EQ PropName) then PosFound = True$ PropNameIndex = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex, DICT_COLUMN_NO$, 'X') PropMinIndex = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex:'_SPEC_MIN', DICT_COLUMN_NO$, 'X') PropMaxIndex = Xlate('DICT.MET_TEST', 'PROPERTY_':PropertyIndex:'_SPEC_MAX', DICT_COLUMN_NO$, 'X') end Until PosFound Next PropertyIndex If PosFound then MetTestRec = '' MetTestRec = '' MetTestRec = '' Database_Services('WriteDataRow', 'MET_TEST', MetTestId, MetTestRec, True$, False$, False$) end else ErrorMsg = 'Error in ':Service:' service. Failed to find property column containing "':PropName:'" in MET_TEST record ':MetTestId end end else ErrorMsg = 'Error in ':Service:' service. Error reading MET_TEST ':MetTestId:'. Error message: ':Error_Services('GetMessage') end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId, PropName, or PropMin/PropMax passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service //---------------------------------------------------------------------------------------------------------------------- // ConvertRecordToJson // // Input: // MetTestId [Required] - Key of MET_TEST record to update. // // Output: // metTest JSON object containing parent and child record details // // Converts a MET_TEST and its child MET_TEST_DATA records into a JSON object. //---------------------------------------------------------------------------------------------------------------------- Service ConvertRecordToJson(MetTestId) MetTestJSON = '' ErrorMsg = '' If MetTestId NE '' then If RowExists('MET_TEST', MetTestId) then Database_Services('ActivateRecord', 'MET_TEST', MetTestId) If Error_Services('NoError') then objJSON = '' If SRP_JSON(objJSON, 'New', 'Object') then MetTestDataIds = {MET_TEST_DATA_IDS} SRP_JSON(objJSON, 'SetValue', 'keyId', @ID, 'String') SRP_JSON(objJSON, 'SetValue', 'lotId', {LOT_ID}, 'String') SRP_JSON(objJSON, 'SetValue', 'legacyLotId', {LEGACY_LOT_ID}, 'String') SRP_JSON(objJSON, 'SetValue', 'lotOperationId', {LOT_OPERATION_ID}, 'String') TestDtm = Date_Services('ConvertDateTimeToISO8601', {TEST_DTM}) SRP_JSON(objJSON, 'SetValue', 'testDtm', TestDtm) SRP_JSON(objJSON, 'SetValue', 'toolRecipe', {TOOL_RECIPE}, 'String') SRP_JSON(objJSON, 'SetValue', 'toolPattern', {TOOL_PATTERN}, 'String') SRP_JSON(objJSON, 'SetValue', 'inspectionRecipe', {INSPECTION_RECIPE}, 'String') SRP_JSON(objJSON, 'SetValue', 'sampleSize', {SAMPLE_SIZE}, 'Number') SRP_JSON(objJSON, 'SetValue', 'tool', {TOOL}, 'String') ToolClass = XLATE('TOOL', {TOOL}, TOOL_CLASS$, 'X') SRP_JSON(objJSON, 'SetValue', 'toolClass', ToolClass, 'String') SRP_JSON(objJSON, 'SetValue', 'layer', {LAYER}) SRP_JSON(objJSON, 'SetValue', 'zone', {ZONE}) SRP_JSON(objJSON, 'SetValue', 'slot', {SLOT}, 'Number') SRP_JSON(objJSON, 'SetValue', 'outOfSpec', {OUT_OF_SPEC}, 'Boolean') SRP_JSON(objJSON, 'SetValue', 'complete', {COMPLETE}, 'Boolean') objMetPropArray = '' If SRP_JSON(objMetPropArray, 'New', 'Array') then For PropIndex = 1 to NUM_PROPERTIES$ PropName = Xlate('MET_TEST', MetTestId, 'PROPERTY_':PropIndex, 'X') PropVals = Xlate('MET_TEST_DATA', MetTestDataIds, 'PROPERTY_':PropIndex:'_VALUE', 'X') If ( (PropName NE '') or (PropVals NE '') ) then objMetProp = '' If SRP_JSON(objMetProp, 'New', 'Object') then PropSpecMin = Xlate('MET_TEST', MetTestId, 'PROPERTY_':PropIndex:'_SPEC_MIN', 'X') PropSpecMax = Xlate('MET_TEST', MetTestId, 'PROPERTY_':PropIndex:'_SPEC_MAX', 'X') PropPositions = Xlate('MET_TEST_DATA', MetTestDataIds, 'POSITION', 'X') PropOutOfSpecs = Xlate('MET_TEST_DATA', MetTestDataIds, 'PROPERTY_':PropIndex:'_OUT_OF_SPEC', 'X') SRP_JSON(objMetProp, 'SetValue', 'property', PropName) SRP_JSON(objMetProp, 'SetValue', 'propertySpecMin', PropSpecMin, 'Number') SRP_JSON(objMetProp, 'SetValue', 'propertySpecMax', PropSpecMax, 'Number') objDatapointsArray = '' If SRP_JSON(objDatapointsArray, 'New', 'Array') then For each Datapoint in PropVals using @VM setting vPos If Datapoint NE '' then objDatapoint = '' If SRP_JSON(objDatapoint, 'New', 'Object') then Position = PropPositions<0, vPos> OutOfSpec = PropOutofSpecs<0, vPos> SRP_JSON(objDatapoint, 'SetValue', 'position', Position, 'Number') SRP_JSON(objDatapoint, 'SetValue', 'value', Datapoint, 'Number') SRP_JSON(objDatapoint, 'SetValue', 'outOfSpec', OutOfSpec, 'Boolean') SRP_JSON(objDatapointsArray, 'Add', objDatapoint) SRP_JSON(objDatapoint, 'Release') end else ErrorMsg = 'Error in ':Service:' service. Error initializing objDatapoint object.' end end Next Datapoint SRP_JSON(objMetProp, 'Set', 'datapoints', objDataPointsArray) SRP_JSON(objDatapointsArray, 'Release') end else ErrorMsg = 'Error in ':Service:' service. Error initializing objDatapointsArray object.' end end else ErrorMsg = 'Error in ':Service:' service. Error initializing objMetProp object.' end SRP_JSON(objMetPropArray, 'Add', objMetProp) SRP_JSON(objMetProp, 'Release') end Next PropIndex SRP_JSON(objJSON, 'Set', 'properties', objMetPropArray) SRP_JSON(objMetPropArray, 'Release') end else ErrorMsg = 'Error in ':Service:' service. Error initializing objMetPropArray object.' end MetTestJSON = SRP_JSON(objJSON, 'Stringify', 'Styled') SRP_JSON(objJSON, 'Release') end else ErrorMsg = 'Error in ':Service:' service. Error initializing objJSON object.' end end else ErrorMsg = 'Error in ':Service:' service. Error reading MET_TEST ':MetTestId:'.' end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId passed into service' end If ErrorMsg EQ '' then Response = MetTestJSON end else Error_Services('Add', ErrorMsg) end end service //---------------------------------------------------------------------------------------------------------------------- // GetMetTestsByLotId // // Input: // LotId [Required] - Key of LOT record to search MET_TEST table for related records. // // Output: // MET_TEST key ids. // //---------------------------------------------------------------------------------------------------------------------- Service GetMetTestsByLotId(LotId) ErrorMsg = '' MetTestIds = '' If LotId NE '' then If RowExists('LOT', LotId) then Extract_Si_Keys('MET_TEST', 'LOT_ID', LotId, MetTestIds) end else ErrorMsg = 'Error in ':Service:' service. LOT ':LotId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null LotId passed into service.' end If ErrorMsg EQ '' then Response = MetTestIds end else Error_Services('Add', ErrorMsg) end end service //---------------------------------------------------------------------------------------------------------------------- // GetMetTestsByLotId // // Input: // LotId [Required] - Key of LOT record to search MET_TEST table for related records. // // Output: // MET_TEST key ids. // //---------------------------------------------------------------------------------------------------------------------- Service GetMetTests(LotId, LegacyLotId, ToolId, ExcludeAssociated, MatchClass, FromDtm, ToDtm) ErrorMsg = '' MetTestIds = '' RespMetTestIds = '' If LotId NE '' then If RowExists('LOT', LotId) then Open 'DICT.MET_TEST' to DictVar then SearchString = '' If MatchClass NE '' then If DCount(MatchClass, @VM) GT 1 then SearchString := 'TOOL_CLASS':@VM:MatchClass<1,1> for i = 2 to DCount(MatchClass, @VM) SearchString := @VM : ';':MatchClass<1,i> Next i SearchString := @FM end else SearchString := 'TOOL_CLASS':@VM:MatchClass:@FM end end If LotId NE '' then SearchString := 'LOT_ID':@VM:LotId:@FM end If LegacyLotId NE '' then SearchString := 'LEGACY_LOT_ID':@VM:LegacyLotId:@FM end If ToolId NE '' then SearchString := 'TOOL':@VM:ToolId:@FM end if FromDtm NE '' then FromDtm = FromDtm + 0.000001 If ToDtm NE '' then ToDtm = ToDtm + 0.000001 end else ToDtm = DateTime() end SearchString := 'TEST_DTM':@VM:FromDtm:'~':ToDtm:@FM end Btree.Extract(SearchString, 'MET_TEST', DictVar, MetTestIds, '', '') If Not(Get_status(errCode)) then If MetTestIds NE '' then If ExcludeAssociated then for each MetTestId in MetTestIds using @VM setting mPos MetTestLotOperationId = Database_Services('ReadDataColumn', 'MET_TEST', MetTestId, MET_TEST.LOT_OPERATION_ID$, True$, 0, False$) If MetTestLotOperationId EQ '' then RespMetTestIds<1,-1> = MetTestId end Next MetTestId end else RespMetTestIds = MetTestIds end end end else ErrorMsg = ErrCode end end else ErrorMsg = 'Error in ' : Service : ' service. Error opening MET_TEST dictionary.' end end else ErrorMsg = 'Error in ':Service:' service. LOT ':LotId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null LotId passed into service.' end If ErrorMsg EQ '' then Response = RespMetTestIds end else Error_Services('Add', ErrorMsg) end end service //---------------------------------------------------------------------------------------------------------------------- // AttachMetTestToLotOperation // // Input: // MetTestId [Required] - Key of MET_TEST record to relate. // LotOperationId [Required] - Key of LOT_OPERATION record to relate. // // Output: // True$ (1) if successful, False$ (0) otherwise. // //---------------------------------------------------------------------------------------------------------------------- Service AttachMetTestToLotOperation(MetTestId, LotOperationId, UserId) ErrorMsg = '' If ( (MetTestId NE '') and (LotOperationId NE '') ) then If RowExists('MET_TEST', MetTestId) then If RowExists('LOT_OPERATION', LotOperationId) then Open 'MET_TEST' to hTable then WriteV LotOperationId on hTable, MetTestId, MET_TEST.LOT_OPERATION_ID$ then LotOperationRec = Database_Services('ReadDataRow', 'LOT_OPERATION', LotOperationId, True$, 0, False$) LotOperationRec = Insert(LotOperationRec, 1, -1, 1, MetTestId) Database_Services('WriteDataRow', 'LOT_OPERATION', LotOperationId, LotOperationRec) LotId = LotOperationRec EquipmentId = Database_Services('ReadDataColumn', 'MET_TEST', MetTestId, MET_TEST.TOOL$, True$, 0, False$) Lot_Event_Services('CreateLotEvent', LotId, DateTime(), 'MET_TEST', 'Metrology attached to lot.', EquipmentId, UserId) end else ErrorMsg = 'Error in ':Service:' service. Failed to write ':LotOperationId ErrorMsg := ' on LOT_OPERATION_ID column for MET_TEST ':MetTestId end end else ErrorMsg = 'Error in ':Service:' service. Failed to open MET_TEST table.' end end else ErrorMsg = 'Error in ':Service:' service. LOT_OPERATION ':LotOperationId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId or LotOperationId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service Service RemoveMetTestFromLotOperation(MetTestId, LotOperationId, UserId) ErrorMsg = '' If ( (MetTestId NE '') and (LotOperationId NE '') ) then If RowExists('MET_TEST', MetTestId) then If RowExists('LOT_OPERATION', LotOperationId) then MetTestRec = Database_Services('ReadDataRow', 'MET_TEST', MetTestId, True$, 0, False$) MetTestRec = '' Database_Services('WriteDataRow', 'MET_TEST', MetTestId, MetTestRec) If Error_Services('NoError') then LotOperationRec = Database_Services('ReadDataRow', 'LOT_OPERATION', LotOperationId, True$, 0, False$) Locate MetTestId in LotOperationRec using @VM setting MetTestPos then LotOperationRec = Delete(LotOperationRec, 1, MetTestPos, 1) end Database_Services('WriteDataRow', 'LOT_OPERATION', LotOperationId, LotOperationRec) LotId = LotOperationRec EquipmentId = Database_Services('ReadDataColumn', 'MET_TEST', MetTestId, MET_TEST.TOOL$, True$, 0, False$) Lot_Event_Services('CreateLotEvent', LotId, DateTime(), 'MET_TEST', 'Metrology attached to lot.', EquipmentId, UserId) end else ErrorMsg = 'Error in ':Service:' service. Error removing MET_TEST ':MetTestId:' from operation.' end end end else ErrorMsg = 'Error in ':Service:' service. MET_TEST ':MetTestId:' does not exist.' end end else ErrorMsg = 'Error in ':Service:' service. Null MetTestId or LotOperationId passed into service.' end If ErrorMsg EQ '' then Response = True$ end else Error_Services('Add', ErrorMsg) Response = False$ end end service