Function PSN_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        :   PSN_Services

    Description :   Handler program for all module related services.

    Notes       :   Service module to support environmental state issues. Environmental refers to the state of the
                    operating system, which includes version, client vs. server, and path to critical systems.

    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)
        11/30/20    djs     Original programmer.

***********************************************************************************************************************/

#pragma precomp SRP_PreCompiler

$insert LOGICAL
$insert SERVICE_SETUP
$insert PROD_SPEC_EQUATES
$insert PRS_STAGE_EQUATES
$Insert WO_MAT_QA_EQUATES
$Insert QA_MET_EQUATES
$Insert RDS_TEST_EQUATES
$Insert RDS_TEST_PROP_EQUATES
$Insert PRS_LAYER_EQU
$Insert PRS_PROP_EQUATES

Options SpecTypes = 'CLEAN', 'SURFSCAN', 'THICK', 'THICKA', 'RES', 'SRES', 'CRES', 'CONC'

Declare function   Database_Services, Psn_Services, obj_Prod_Spec, Error_Services, SRP_JSON, Cust_Epi_Part_Services
Declare function   Prod_Ver_Services, PRS_Stage_Services, SRP_Array, Tool_Class_Services, SRP_String
Declare subroutine Database_Services, Psn_Services, Error_Services, SRP_JSON, Extract_Si_Keys

GoToService else
    Error_Services('Add', Service : ' is not a valid service request within the ' : ServiceModule : ' services module.')
end

Return Response OR ''


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options BOOLEAN     = True$, False$


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//----------------------------------------------------------------------------------------------------------------------
// InitializeCritParams
//
//----------------------------------------------------------------------------------------------------------------------
Service InitializeCritParams(PSNo)
    
    PSRec                               = Database_Services('ReadDataRow', 'PROD_SPEC', PSNo)
    PSRec<PROD_SPEC_CRITICAL_WARP_MAX$> = 100
    PSRec<PROD_SPEC_CRITICAL_BOW_MIN$>  = -40
    PSRec<PROD_SPEC_CRITICAL_BOW_MAX$>  = 50
    Database_Services('WriteDataRow', 'PROD_SPEC', PSNo, PSRec, True$, False$, True$)    
    
end service


Service GetRecipes(PSNo)
    
    ErrorMsg = ''
    Recipes  = ''
    If PSNo NE '' then
        If RowExists('PROD_SPEC', PSNo) then
            PropKeys       = ''
            Extract_Si_Keys('PRS_PROP', 'PS_NO', PSNo, PropKeys)
            Recipes        = Xlate('PRS_PROP', PropKeys, 'MET_RECIPE', 'X')
            PRSStageKeys   = ''
            Extract_Si_Keys('PRS_STAGE', 'PS_NO', PSNo, PRSStageKeys)
            Recipes<0, -1> = Xlate('PRS_STAGE', PRSStageKeys, 'MET_RECIPE', 'X')
            Recipes<0, -1> = Xlate('PRS_STAGE', PRSStageKeys, 'SURFSCAN_RECIPE', 'X')
            Recipes<0, -1> = @VM : Xlate('PRS_STAGE', PRSStageKeys, 'CLEAN_RECIPE', 'X')
            Recipes        = SRP_Array('Clean', Recipes, 'TrimAndMakeUnique', @VM)            
        end else
            ErrorMsg = 'Error in ':Service:' service. PROD_SPEC record "':PSNo:'" does not exist.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null PSNo passed into service.'
    end
    
    If ErrorMsg EQ '' then
        Response = Recipes
    end else
        Error_Services('Add', ErrorMsg)
    end
    
end service

Service GetMetrologyRecipesAndPatterns(PSNo)
	
	ErrorMsg = ''
    RecipesAndPatterns  = ''
    CurrIdx = 1
    If PSNo NE '' then
        If RowExists('PROD_SPEC', PSNo) then
            PropKeys = ''
            Extract_Si_Keys('PRS_PROP', 'PS_NO', PSNo, PropKeys)
            for each PropKey in PropKeys using @VM setting pPos
            	Recipe = Xlate('PRS_PROP', PropKey, PRS_PROP_RECIPE$, 'X')
            	Recipe = SRP_String('Trim', Recipe, FB)
            	Pattern = Xlate('PRS_PROP', PropKey, PRS_PROP_PATTERN$, 'X')
            	Pattern = SRP_String('Trim', Pattern, FB)
            	Tool = Xlate('PRS_PROP', PropKey, PRS_PROP_TOOL$, 'X')
            	Tool = SRP_String('Trim', Tool, FB)
            	Convert @LOWER_CASE to @UPPER_CASE in Tool
            	
            	If Recipe NE '' and Pattern NE '' and Tool NE '' then
            		PatternSize = Tool_Class_Services('GetNumberOfPointsForPattern', Tool, Pattern)
            	
					If Error_Services('NoError') then
						Entry = Recipe:@VM:Pattern:@VM:PatternSize:@VM:Tool
						RecipesAndPatterns<CurrIdx> = Entry
						CurrIdx = CurrIdx + 1
					end
            	end
            Next PropKey
            
            PRSStageKeys   = ''
            Extract_Si_Keys('PRS_STAGE', 'PS_NO', PSNo, PRSStageKeys)
            for each PRSStageKey in PRSStageKeys using @VM setting sPos
            	Recipes = Xlate('PRS_STAGE', PRSStageKey, PRS_STAGE_MET_RECIPE$, 'X')
            	for each Recipe in Recipes using @VM setting rPos
            		Recipe = SRP_String('Trim', Recipe, FB)
            		Pattern = Xlate('PRS_STAGE', PRSStageKey, PRS_STAGE_MET_RECIPE_PATTERN$, 'X')<1, rPos>
            		Pattern = SRP_String('Trim', Pattern, FB)
					Tool = Xlate('PRS_STAGE', PRSStageKey, PRS_STAGE_MET_TOOL_CLASS$, 'X')<1, rPos>
					Tool = SRP_String('Trim', Tool, FB)
					Convert @LOWER_CASE to @UPPER_CASE in Tool
					
					If Recipe NE '' and Tool NE '' and Pattern NE '' then
						PatternSize = Tool_Class_Services('GetNumberOfPointsForPattern', Tool, Pattern)
					
						If Error_Services('NoError') then
							Entry = Recipe:@VM:Pattern:@VM:PatternSize:@VM:Tool
							RecipesAndPatterns<CurrIdx> = Entry
							CurrIdx = CurrIdx + 1
						end
					end
            	Next Recipe
            	SurfScanRecipes = Xlate('PRS_STAGE', PRSStageKey, PRS_STAGE_SURFSCAN_RECIPE$, 'X')
            	If SurfScanRecipes NE '' then
            		for each SurfScanRecipe in SurfScanRecipes using @VM setting ssPos
            			Entry = SurfScanRecipe:@VM:'':@VM:'':@VM:'TENCOR'
						RecipesAndPatterns<CurrIdx> = Entry
						CurrIdx = CurrIdx + 1
            		Next SurfScanRecipe
            	end
            Next PRSStageKey
            
            RecipesAndPatterns = SRP_Array('Clean', RecipesAndPatterns, 'TrimAndMakeUnique')            
        end else
            ErrorMsg = 'Error in ':Service:' service. PROD_SPEC record "':PSNo:'" does not exist.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null PSNo passed into service.'
    end
    
    If ErrorMsg EQ '' then
        Response = RecipesAndPatterns
    end else
        Error_Services('Add', ErrorMsg)
    end
	
end service

Service GetAllMetrologyRecipes(PSNo, GetSurfscan, GetClean, GetRes, GetThick)
    
    Recipes = ''
    If GetSurfscan EQ '' then GetSurfscan = True$
    If GetClean EQ '' then GetClean = True$
    If GetRes EQ '' then GetRes = True$
    If GetThick EQ '' then GetThick = True$
    
    If GetSurfscan then Recipes := PSN_Services('GetSurfscanRecipes', PSNo) : @FM
    If GetClean then Recipes := PSN_Services('GetCleanRecipes', PSNo) :@FM
    If GetRes then Recipes := PSN_Services('GetResRecipes', PSNo) :@FM
    If GetThick then Recipes := PSN_Services('GetThicknessRecipes', PSNo)
    
    Recipes = SRP_Array('Clean', Recipes, 'TrimAndMakeUnique', @FM) 
    Response = Recipes
    
end service

Service GetSurfscanRecipes(PSNo)
    
    Recipes = ''
    If PSNo NE '' then
        If RowExists('PROD_SPEC', PSNo) then
            PropKeys       = ''
            Extract_Si_Keys('PRS_PROP', 'PS_NO', PSNo, PropKeys)
            PRSStageKeys   = ''
            Extract_Si_Keys('PRS_STAGE', 'PS_NO', PSNo, PRSStageKeys)
            for each PRSStageKey in PRSStageKeys using @VM setting pPos
                Stage = Xlate('PRS_STAGE', PRSStageKey, 'STAGE', 'X')
                TencorRecipes = Xlate('PRS_STAGE', PRSStageKey, 'SURFSCAN_RECIPE', 'X')
                for each Recipe in TencorRecipes using @VM
                    ToolClass = 'TENCOR'
                    Recipes<-1> = ToolClass : @VM : Recipe : @VM : Stage     
                Next TencorRecipe
            Next PRSStageKey
            Recipes        = SRP_Array('Clean', Recipes, 'TrimAndMakeUnique', @FM)
        end
    end
    Response = Recipes
    
end service

Service GetCleanRecipes(PSNo)
    
    Recipes = ''
    If PSNo NE '' then
        If RowExists('PROD_SPEC', PSNo) then
            PropKeys       = ''
            Extract_Si_Keys('PRS_PROP', 'PS_NO', PSNo, PropKeys)
            PRSStageKeys   = ''
            Extract_Si_Keys('PRS_STAGE', 'PS_NO', PSNo, PRSStageKeys)
            for each PRSStageKey in PRSStageKeys using @VM setting pPos
                Stage = Xlate('PRS_STAGE', PRSStageKey, 'STAGE', 'X')
                CleanRecipes = Xlate('PRS_STAGE', PRSStageKey, 'CLEAN_RECIPE', 'X')
                for each Recipe in CleanRecipes using @VM
                    ToolClass = 'CLEAN'
                    Recipes<-1> = ToolClass : @VM : Recipe : @VM : Stage     
                Next TencorRecipe
            Next PRSStageKey
            Recipes        = SRP_Array('Clean', Recipes, 'TrimAndMakeUnique', @FM)
        end
    end
    Response = Recipes
    
end service

Service GetThicknessRecipes(PSNo)
    
    ErrorMsg = ''
    Recipes  = ''
    If PSNo NE '' then
        If RowExists('PROD_SPEC', PSNo) then
            //First get QA Recipes
            PropKeys       = ''
            Extract_Si_Keys('PRS_PROP', 'PS_NO', PSNo, PropKeys)
            Recipes        = ''
            PRSStageKeys   = ''
            Extract_Si_Keys('PRS_STAGE', 'PS_NO', PSNo, PRSStageKeys)
            for each PRSStageKey in PRSStageKeys using @VM setting pPos
                Stage = Xlate('PRS_STAGE', PRSStageKey, 'STAGE', 'X')
                PRSStageRecipes = Xlate('PRS_STAGE', PRSStageKey, 'MET_RECIPE', 'X')
                for each PRSStageRecipe in PRSStageRecipes using @VM setting rPos
                    PropType = Xlate('PRS_STAGE', PRSStageKey, 'MET_PROP', 'X')<1, rPos>
                    If PropType EQ 'THICK' OR PropType EQ 'THICKA' then
                        Recipe = PRSStageRecipe
                        ToolClass = 'FTIR'
                        Recipes<-1> = ToolClass : @VM : Recipe : @VM : Stage 
                    end
                Next PRSStageRecipe
            Next PRSStageKey
            //Next get Rathole recipes
            SpecEpiData = XLATE('PROD_SPEC', PSNo, 'SPEC_EPI', 'X')
            swap @VM with @FM in SpecEpiData
            swap '~' with @VM in SpecEpiData
            ThicknessData = SpecEpiData<13>
            ToolClass = ThicknessData<1, 1>
            Recipe = ThicknessData<1, 3>
            Stage = 'RDS_TEST'
            If ToolClass EQ 'FTIR' OR ToolClass EQ 'ADE' then
                Recipes<-1> = ToolClass : @VM : Recipe : @VM : Stage 
            end
            // Remove any duplicate entries
            Recipes        = SRP_Array('Clean', Recipes, 'TrimAndMakeUnique', @FM)                 
        end else
            ErrorMsg = 'Error in ':Service:' service. PROD_SPEC record "':PSNo:'" does not exist.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null PSNo passed into service.'
    end
    
    If ErrorMsg EQ '' then
        Response = Recipes
    end else
        Error_Services('Add', ErrorMsg)
    end
    
end service

Service GetResRecipes(PSNo)

    ErrorMsg = ''
    Recipes  = ''
    If PSNo NE '' then
        If RowExists('PROD_SPEC', PSNo) then
            //First get QA Recipes
            PropKeys       = ''
            Extract_Si_Keys('PRS_PROP', 'PS_NO', PSNo, PropKeys)
            Recipes        = ''
            PRSStageKeys   = ''
            Extract_Si_Keys('PRS_STAGE', 'PS_NO', PSNo, PRSStageKeys)
            for each PRSStageKey in PRSStageKeys using @VM setting pPos
                Stage = Xlate('PRS_STAGE', PRSStageKey, 'STAGE', 'X')
                PRSStageRecipes = Xlate('PRS_STAGE', PRSStageKey, 'MET_RECIPE', 'X')
                for each PRSStageRecipe in PRSStageRecipes using @VM setting rPos
                    PropType = Xlate('PRS_STAGE', PRSStageKey, 'MET_PROP', 'X')<1, rPos>
                    ToolClass = Xlate('PRS_STAGE', PRSStageKey, 'MET_TOOL_CLASS', 'X')<1, rPos>
                    If PropType EQ 'RES' OR PropType EQ 'SRES' OR PropType EQ 'CRES' OR PropType EQ 'CONC' then
                        Recipe = PRSStageRecipe
                        ToolClass = ToolClass
                        Recipes<-1> = ToolClass : @VM : Recipe : @VM : Stage 
                    end
                Next PRSStageRecipe
            Next PRSStageKey
            //Next get Rathole recipes
            Stage = 'RDS_TEST'
            SpecEpiData = XLATE('PROD_SPEC', PSNo, 'SPEC_EPI', 'X')
            swap @VM with @FM in SpecEpiData
            swap '~' with @VM in SpecEpiData
            ResData1 = SpecEpiData<19>
            
            If ResData1 NE '' then
                ToolClass = ResData1<1, 1>
                Recipe = ResData1<1, 3>
                Recipes<-1> = ToolClass : @VM : Recipe : @VM : Stage 
            end
            
            ResData2 = SpecEpiData<14>
            If ResData2 NE '' then
                ToolClass = ResData2<1, 1>
                Recipe = ResData2<1, 3>
                Recipes<-1> = ToolClass : @VM : Recipe : @VM : Stage  
            end
            
            // Remove any duplicate entries
            Recipes        = SRP_Array('Clean', Recipes, 'TrimAndMakeUnique', @FM)                 
        end else
            ErrorMsg = 'Error in ':Service:' service. PROD_SPEC record "':PSNo:'" does not exist.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null PSNo passed into service.'
    end
    
    If ErrorMsg EQ '' then
        Response = Recipes
    end else
        Error_Services('Add', ErrorMsg)
    end
    
end service

Service CheckPSNStages(PSNo, Stage)
    
    Begin Case
        Case Stage Eq 'PRE'
            CleansCheck = Psn_Services('CheckCleansParms', PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
            VisualCheck = Psn_Services('CheckVisualParms', PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
            
        Case  Stage Eq 'LOAD'
            InstructionsCheck = Psn_Services('CheckInstructionParms', PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
        Case Stage Eq 'FWI'
            VisualCheck = Psn_Services('CheckVisualParms', PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
        Case Stage Eq 'LWI'
            VisualCheck = Psn_Services('CheckVisualParms', PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
            SurfaceCheck = Psn_Services('CheckSurfaceParms',PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
            
        Case Stage Eq 'POST'
            CleansCheck = Psn_Services('CheckCleansParms', PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
            SurfaceCheck = Psn_Services('CheckSurfaceParms',PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
        Case Stage Eq 'QA' OR 'UNLOAD'
            QAMetCheck = Psn_Services('CheckQAMetParms', PSNo, Stage)
            IF Error_Services('GetMessage') NE '' then
                Return 
            end
            
            
    End Case
    GoSub CheckAdHoc
    Return
    
end service


Service CheckCleansParms(PSNo, Stage)

    Response    = 1
    StageRecKey = PSNo: '*' : Stage
    StageRec    = XLATE('PRS_STAGE', StageRecKey, '', 'X')

    IF StageRec<PRS_STAGE_CLEAN_SIG_REQ$> EQ 1 then
        IF StageRec<PRS_STAGE_CLEAN_TOOL$> EQ '' then
            FailReason = 'Clean Tool not selected for ' : Stage : ' stage.'
            Error_Services('Add', FailReason)
            return 
        end
        If StageRec<PRS_STAGE_CLEAN_RECIPE$> EQ '' then
            FailReason = 'Clean Recipe not selected for ' : Stage : ' stage.'
            Error_Services('Add', FailReason)
            return 
        end
    end else
        IF Stage EQ 'PRE' OR Stage EQ 'POST' then
            FailReason = 'Cleans Signature Required for ' : Stage : ' stage.'
            Error_Services('Add', FailReason)
            return 
        end
    end

    return
    
end service


Service CheckVisualParms(PSNo, Stage)
    
    Response    = 1
    StageRecKey = PSNo: '*' : Stage
    StageRec    = XLATE('PRS_STAGE', StageRecKey, '', 'X')
    If StageRec<PRS_STAGE_INSP_SIG_REQ$> EQ 1 then
        IF StageRec<PRS_STAGE_MICROSCOPE$> EQ 1 then
            IF StageRec<PRS_STAGE_LPD$> EQ '' then
                FailReason = 'Frontside LPD limits must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            //Front Side Scratch Count
            IF StageRec<PRS_STAGE_SCRATCHES$> EQ '' then
                FailReason = 'Frontside Scratch Count must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            //Front Side Scratch Length
            IF StageRec<PRS_STAGE_SCRATCH_LEN$> EQ '' then
                FailReason = 'Frontside Scratch Length must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            //Front Side Pits
            IF StageRec<PRS_STAGE_PITS$> EQ '' then
                FailReason = 'Frontside Pits count must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            //Front Side Mounds
            IF StageRec<PRS_STAGE_MOUNDS$> EQ '' then
                FailReason = 'Frontside Mounds count must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            //Front Side Spots
            IF StageRec<PRS_STAGE_SPOTS$> EQ '' then
                FailReason = 'Frontside Spots count must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            //Front Side FOV
            IF StageRec<PRS_STAGE_FOV$> EQ '' then
                Response = 'Frontside FOV must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            //Front Side BL Defects
            IF StageRec<PRS_STAGE_BL_DEFECTS$> EQ '' then
                FailReason = 'Frontside BL Defects must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end 
        end else
            IF Stage EQ 'PRE' OR Stage EQ 'FWI' OR Stage EQ 'LWI' then
                FailReason = 'Microscope Inspection Required for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            
        end
        IF StageRec<PRS_STAGE_BRIGHTLIGHT$> EQ 1 then
            //Back Side Scratches
            IF StageRec<PRS_STAGE_BSIDE_SCRATCHES$> EQ '' then
                FailReason = 'Backside Scratch Count must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            //Back Side Scratch Length
            IF StageRec<PRS_STAGE_BSIDE_SCRATCH_LEN$> EQ '' then
                FailReason = 'Backside Scratch Length must be set for ':Stage:' stage.'
                Error_Services('Add', FailReason)
                return
            end
        end else
            IF Stage EQ 'PRE' OR Stage EQ 'FWI' OR Stage EQ 'LWI' then
                FailReason = 'Brightlight Inspection Required for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            
        end
    end else
        
        IF Stage EQ 'PRE' OR Stage EQ 'FWI' OR Stage EQ 'LWI' then
            FailReason = 'Visual Inspection Signature Required for ' : Stage : ' stage.'
            Error_Services('Add', FailReason)
            return
        end
    end
    return
    
end service


Service CheckInstructionParms(PSNo, Stage)
    
    Response    = 1
    StageRecKey = PSNo: '*' : Stage
    StageRec    = XLATE('PRS_STAGE', StageRecKey, '', 'X')
    //Currently the only place where instruction would be required is the LOAD stage
    IF Stage EQ 'LOAD' then
        IF StageRec<PRS_STAGE_INST$> EQ '' then
            
            FailReason = 'Instructions Missing for ':Stage:' stage.'
            Error_Services('Add', FailReason)
            return
        end 
    end
    Return
    
end service


Service CheckSurfaceParms(PSNo, Stage)
    
    FailReason = 1
    StageRecKey = PSNo: '*' : Stage
    StageRec = XLATE('PRS_STAGE', StageRecKey, '', 'X')
    IF StageRec<PRS_STAGE_SURFSCAN_SIG_REQ$> EQ 1 then
        SSRecipes = StageRec<PRS_STAGE_SURFSCAN_RECIPE$>
        IF SSRecipes NE '' then
            For RecipePos = 1 To DCOUNT(SSRecipes, @VM)
                IF StageRec<PRS_STAGE_SURF_DEFECTS$, RecipePos> EQ '' then
                    FailReason = 'SurfScan Defect Count must be set for ':Stage:' stage.'
                    Error_Services('Add', FailReason)
                    return 
                end
                //Surf Haze
                IF StageRec<PRS_STAGE_SURF_HAZE$, RecipePos> EQ '' then
                    FailReason = 'SurfScan Haze must be set for ':Stage:' stage.'
                    Error_Services('Add', FailReason)
                    return 
                end
                //Surf sample qty
                IF StageRec<PRS_STAGE_SS_SAMP_QTY$, RecipePos> EQ '' then
                    FailReason = 'SurfScan Cassette Sample Size must be set for ':Stage:' stage.'
                    Error_Services('Add', FailReason)
                    return 
                end
            Next RecipePos
            
        end else
            FailReason = 'No SurfScan Recipe has been set for ' : Stage : 'stage.'
            Error_Services('Add', FailReason)
            return 
        end
        
    end else
        IF Stage EQ 'POST' OR Stage EQ 'LWI' then
            FailReason = 'SurfScan signature must be set to "Required" for ' : Stage : 'stage.'
            Error_Services('Add', FailReason)
            return
        end
    end
    
    return
    
end service


Service CheckQAMetParms(PSNo, Stage)
    
    FailReason = 1
    StageRecKey = PSNo: '*' : Stage
    StageRec = XLATE('PRS_STAGE', StageRecKey, '', 'X')
    QAMetTests = StageRec<PRS_STAGE_MET_TEST$>
    IF QAMetTests NE '' then
        For MetTest = 1 to DCount(QAMetTests, @VM)
            IF StageRec<PRS_STAGE_MET_TOOL_CLASS$,MetTest> EQ '' then
                FailReason = 'QA Metrology Tool class must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_RECIPE$,MetTest> EQ '' then
                FailReason = 'QA Metrology Tool Recipe must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_RECIPE_PATTERN$,MetTest> EQ '' then
                FailReason = 'QA Metrology Tool Recipe  Pattern must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_MIN$,MetTest> EQ '' then
                FailReason = 'QA Metrology Minimum must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_MAX$,MetTest> EQ '' then
                FailReason = 'QA Metrology Maximum must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_SLOT$,MetTest> EQ '' then
                FailReason = 'QA Metrology slot must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_WFR_QTY$,MetTest> EQ '' then
                FailReason = 'QA Metrology Wafer Quantity must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_INTERVAL$,MetTest> EQ '' then
                FailReason = 'QA Metrology Wafer Interval must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_START$,MetTest> EQ '' then
                FailReason = 'QA Metrology Start must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end
            IF StageRec<PRS_STAGE_MET_PHASE_MIN$,MetTest> EQ '' AND StageRec<PRS_STAGE_MET_TEST$, MetTest> EQ 'CRES' then
                FailReason = 'QA Metrology Phase min must be set for ' : Stage : ' stage.'
                Error_Services('Add', FailReason)
                return 
            end    
        Next MetTest
    end else
        IF Stage EQ 'QA' OR Stage EQ 'UNLOAD' then
            FailReason = 'No Metrology Tests exist for ' : Stage : ' stage.'
            Error_Services('Add', FailReason)
            return
        end
    end
    return
    
end service


//----------------------------------------------------------------------------------------------------------------------
// UpdateQAMetrologyLimits
//
// Purpose: Update the specification limits for a particular QA metrology test.
//----------------------------------------------------------------------------------------------------------------------
Service UpdateQAMetrologyLimits(WOMatQAKey, QAMetStage, QAMetProfile, QAMetSlot)
    
    TestFound = False$
    ErrorMsg  = ''
    If ( (WOMatQAKey NE '') and (QAMetStage NE '') and (QAMetProfile NE '') and (QAMetSlot NE '') ) then
        // Ensure QAMetProfile is prepended with a '1'.
        If QAMetProfile[1, 1] NE 1 then QAMetProfile = 1:QAMetProfile
        WOStepKey = Field(WOMatQAKey, '*', 1) : '*1'
        PSNo = Xlate('WO_STEP', WOStepKey, 'PROD_SPEC_ID', 'X')
        If PSNo NE '' then
            PSRec			= Database_Services('ReadDataRow', 'PROD_SPEC', PSNo)
            If Error_Services('NoError') then
                // Retrieve new QA metrology data from PSN.
                SpecQAMetData = obj_Prod_Spec('GetQAMet',PSNo:@RM:PSRec)
                If SpecQAMetData NE '' then
                    WOMatQARec     = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatQAKey)
                    If Error_Services('NoError') then
                        RecQAMetStages = WOMatQARec<WO_MAT_QA_STAGE$>
                        RecQAMetTests  = WOMatQARec<WO_MAT_QA_PROFILE$>
                        RecQAMetSlots  = WOMatQARec<WO_MAT_QA_SLOT$>
                        // Locate the desired stage, profile/test, and slot in the record.
                        RecPos = ''
                        For each RecQAMetStage in RecQAMetStages using @VM setting vPos
                            RecQAMetTest = RecQAMetTests<0, vPos>
                            RecQAMetSlot = RecQAMetSlots<0, vPos>
                            If ( (RecQAMetStage EQ QAMetStage) and (RecQAMetTest EQ QAMetProfile) and (RecQAMetSlot EQ QAMetSlot) ) then
                                // Found the position in the record
                                RecPos = vPos
                            end
                        Until RecPos
                        Next RecQAMetStage
                        
                        If RecPos then
                            // Locate the desired stage, profile/test, and slot in the spec QAMetData structure.
                            SpecQAMetStages = SpecQAMetData<COL$QA_MET_STAGE>
                            SpecQAMetTests  = SpecQAMetData<COL$QA_MET_TEST>
                            SpecQAMetSlots  = SpecQAMetData<COL$QA_MET_SLOT>
                            
                            For each SpecQAMetStage in SpecQAMetStages using @VM setting vPos
                                SpecQAMetTest    = SpecQAMetTests<0, vPos>
                                SpecQAMetSlot    = SpecQAMetSlots<0, vPos>
                                SpecQAMetProfile = 1:SpecQAMetTest
                                SpecPos          = ''
                                If ( (SpecQAMetStage EQ QAMetStage) and (SpecQAMetProfile EQ QAMetProfile) and (SpecQAMetSlot EQ QAMetSlot) ) then
                                    // Found the posotion in the spec QAMetData structure
                                    SpecPos = vPos
                                end
                            Until SpecPos
                            Next SpecQAMetStage
                            
                            If SpecPos then
                                // Update the limits and recipe
                                WOMatQARec<WO_MAT_QA_RECIPE$, RecPos>         = SpecQAMetData<COL$QA_MET_RECIPE, SpecPos>
                                WOMatQARec<WO_MAT_QA_RECIPE_PATTERN$, RecPos> = SpecQAMetData<COL$QA_MET_RECIPE_PATTERN, SpecPos>
                                WOMatQARec<WO_MAT_QA_MIN$, RecPos>            = SpecQAMetData<COL$QA_MET_MIN, SpecPos>
                                WOMatQARec<WO_MAT_QA_MAX$, RecPos>            = SpecQAMetData<COL$QA_MET_MAX, SpecPos>
                                WOMatQARec<WO_MAT_QA_REACT_SCHED$, RecPos>    = SpecQAMetData<COL$QA_MET_REACT_SCHED, SpecPos>
                                WOMatQARec<WO_MAT_QA_PHASE_MIN$, RecPos>      = SpecQAMetData<COL$QA_MET_PHASE_MIN, SpecPos>
                                Database_Services('WriteDataRow', 'WO_MAT_QA', WOMatQAKey, WOMatQARec, True$, True$, True$)
                                If Error_Services('HasError') then ErrorMsg = Error_Services('GetMessage')
                            end else
                                ErrorMsg = 'Failed to find desired stage, test, and slot in spec QAMetData structure.'
                            end
                        end else
                            ErrorMsg = 'Failed to find desired stage, test, and slot in WO_MAT_QA record.'
                        end
                    end else
                        // Error reading WO_MAT_QA record
                        ErrorMsg = Error_Services('GetMessage')
                    end
                end else
                    ErrorMsg = 'Error retrieving QAMetData from obj_Prod_Spec.'
                end
            end else
                // Error reading PROD_SPEC record
                ErrorMsg = Error_Services('GetMessage')
            end
        end else
            ErrorMsg = 'Failed to find PSNo in WO_STEP record ':WOStepKey:'.'
        end
    end else
        ErrorMsg = 'Null WOMatQAKey, QAMetStage, QAMetProfile, or QAMetSlot passed into service.'
    end
    
    If ErrorMsg EQ '' then
        Response = True$
    end else
        Response = False$
        Error_Services('Add', 'Error in ':Service:' service. ':ErrorMsg)
    end
    
End Service


Service UpdateAllQAMetrologyLimits(WOMatQAKey)
    
    ErrorMsg = ''
    If WOMatQAKey NE '' then
        WOMatQARec  = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatQAKey)
        RecStages   = WOMatQARec<WO_MAT_QA_STAGE$>
        RecProfiles = WOMatQARec<WO_MAT_QA_PROFILE$>
        RecSlots    = WOMatQARec<WO_MAT_QA_SLOT$>
        For each RecStage in RecStages using @VM setting vPos
            RecProfile = RecProfiles<0, vPos>
            RecSlot    = RecSlots<0, vPos>
            PSN_Services('UpdateQAMetrologyLimits', WOMatQAKey, RecStage, RecProfile, RecSlot)
            If Error_Services('HasError') then ErrorMsg = Error_Services('GetMessage')
        Next RecStage
    end else
        ErrorMsg = 'Null WOMatQAKey passed into service.'
    end
    
    If ErrorMsg EQ '' then
        Response = True$
    end else
        Response = False$
        Error_Services('Add', 'Error in ':Service:' service. ':ErrorMsg)
    end
    
end service


Service UpdateAllQAMetrologyRecord(WOMatQAKey, stageToUpdate)
	
	ErrorMsg = ''
	If WOMatQAKey NE '' then
		WOMatQARec  = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatQAKey)
		WOStepKey = Field(WOMatQAKey, '*', 1) : '*1'
		PSNo = Xlate('WO_STEP', WOStepKey, 'PROD_SPEC_ID', 'X')
		PSRec			= Database_Services('ReadDataRow', 'PROD_SPEC', PSNo)
        CurrTestCount = DCOUNT(WOMatQARec<1>, @VM)
		SpecQAMetData = obj_Prod_Spec('GetQAMet',PSNo:@RM:PSRec)
		
		for each SpecTest in SpecQAMetData<1> using @VM setting vPos then
			
			If (WOMatQARec<1,vPos> EQ 'QA' OR WOMatQARec<1,vPos> EQ '') then
                WOMatQARec<1,vPos>  = SpecQAMetData<1,vPos>
                if SpecQAMetData<2,vPos> EQ 'ADE' then SpecQAMetData<2,vPos> = '1ADE'
                if SpecQAMetData<2,vPos> EQ 'THICK_ONLY' then SpecQAMetData<2,vPos> = '1THICK_ONLY'
                if SpecQAMetData<2,vPos> EQ 'CRES' then SpecQAMetData<2,vPos> = '1CRES'
                WOMatQARec<2,vPos>  = SpecQAMetData<2,vPos>
                WOMatQARec<3,vPos>  = SpecQAMetData<3,vPos>
                WOMatQARec<4,vPos>  = SpecQAMetData<4,vPos>
                WOMatQARec<5,vPos>  = SpecQAMetData<5,vPos>
                WOMatQARec<6,vPos>  = SpecQAMetData<6,vPos>
                WOMatQARec<7,vPos>  = SpecQAMetData<7,vPos>
                WOMatQARec<8,vPos>  = SpecQAMetData<8,vPos>
                WOMatQARec<9,vPos>  = SpecQAMetData<9,vPos>
                WOMatQARec<16,vPos> = SpecQAMetData<14,vPos>
                WOMatQARec<26,vPos> = 0
			end

		Next SpecTest
		
		Database_Services('WriteDataRow', 'WO_MAT_QA', WOMatQAKey, WOMatQARec, True$, True$, True$)
	end else
		ErrorMsg = 'Null WOMatQAKey passed into service.'
	end
	
	If ErrorMsg EQ '' then
        Response = True$
    end else
        Response = False$
        Error_Services('Add', 'Error in ':Service:' service. ':ErrorMsg)
    end
    
end service


Service UpdateRDSMetrologyLimits(RDSNo)
    
    If RDSNo NE '' then
        If RowExists('RDS', RDSNo) then
            PSNo        = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X')
            If PSNo NE '' then
                RDSTestKeys = Xlate('RDS', RDSNo, 'RDS_TEST_KEYS', 'X')
                If RDSTestKeys NE '' then
                    For each RDSTestKey in RDSTestKeys using @VM setting vPos
                        // Code below adapted from obj_RDS_Test('Create')
                        RDSTestRec = Database_Services('ReadDataRow', 'RDS_TEST', RDSTestKey)
                        If Error_Services('NoError') then
                            LayerSet   = RDSTestRec<RDS_TEST_LS_ID$>
                            Zone       = RDSTestRec<RDS_TEST_ZONE$>
                            LayerSpecs = obj_Prod_Spec('GetLayerProp',PSNo:@RM:LayerSet:@RM:1)		;* Last parameter specifies no output conversion on return data

                            LayerSpecs = FIELD(LayerSpecs,@FM,2,99)		;* Returns with the layer set ID in the first field of each line

                            IF Not(Get_Status(errCode)) then

                                WaferSize		= XLATE( 'PROD_SPEC', PSNo, 'SUB_WAFER_SIZE', 'X' )
                                SubOrientation	= XLATE('PROD_SPEC',PSNo,'SUB_ORIENTATION','X')
                                ResUnits		= LayerSpecs<PRS_LAYER_RES_UNITS$>

                                ReactorType		= XLATE('PROD_SPEC',PSNo,PROD_SPEC_REACTOR_TYPE$,'X')
                                ThickFilmMet	= XLATE('PROD_SPEC',PSNo,PROD_SPEC_THICKFILM_MET$,'X')			;* Added 1/16/2009 JCH

                                RDSTestRec<RDS_TEST_RDS_NO$>				= RDSNo
                                RDSTestRec<RDS_TEST_LS_ID$>					= LayerSet
                                RDSTestRec<RDS_TEST_ZONE$>					= Zone
                                RDSTestRec<RDS_TEST_WAFER_SIZE$>			= WaferSize
                                RDSTestRec<RDS_TEST_SUB_ORIENTATION$>		= SubOrientation

                                RDSTestRec<RDS_TEST_SPEC_DOPANT$>			= LayerSpecs<PRS_LAYER_DOPANT$>
                                RDSTestRec<RDS_TEST_SPEC_RECIPE$>			= LayerSpecs<PRS_LAYER_RECIPE$>
                                RDSTestRec<RDS_TEST_SPEC_RECIPE_NAME$>		= LayerSpecs<PRS_LAYER_RECIPE_NAME$>

                                RDSTestRec<RDS_TEST_SPEC_THICK_MIN$>		= LayerSpecs<PRS_LAYER_THICK_MIN$>
                                RDSTestRec<RDS_TEST_SPEC_THICK_TARGET$>		= LayerSpecs<PRS_LAYER_THICK_TARGET$>
                                RDSTestRec<RDS_TEST_SPEC_THICK_MAX$>		= LayerSpecs<PRS_LAYER_THICK_MAX$>
                                RDSTestRec<RDS_TEST_SPEC_THICK_UNITS$>		= LayerSpecs<PRS_LAYER_THICK_UNITS$>
                                RDSTestRec<RDS_TEST_SPEC_RES_MIN$>			= LayerSpecs<PRS_LAYER_RES_MIN$>
                                RDSTestRec<RDS_TEST_SPEC_RES_TARGET$>		= LayerSpecs<PRS_LAYER_RES_TARGET$>
                                RDSTestRec<RDS_TEST_SPEC_RES_MAX$>			= LayerSpecs<PRS_LAYER_RES_MAX$>
                                RDSTestRec<RDS_TEST_SPEC_RES_UNITS$>		= LayerSpecs<PRS_LAYER_RES_UNITS$>
                                RDSTestRec<RDS_TEST_SPEC_CON_MIN$>			= LayerSpecs<PRS_LAYER_CONC_MIN$>
                                RDSTestRec<RDS_TEST_SPEC_CON_TARGET$>		= LayerSpecs<PRS_LAYER_CONC_TARGET$>
                                RDSTestRec<RDS_TEST_SPEC_CON_MAX$>			= LayerSpecs<PRS_LAYER_CONC_MAX$>
                                RDSTestRec<RDS_TEST_SPEC_CON_UNITS$>		= LayerSpecs<PRS_LAYER_CONC_UNITS$>
                                RDSTestRec<RDS_TEST_SPEC_STRESS_MIN$>		= LayerSpecs<PRS_LAYER_STRESS_MIN$>
                                RDSTestRec<RDS_TEST_SPEC_STRESS_MAX$>		= LayerSpecs<PRS_LAYER_STRESS_MAX$>
                                RDSTestRec<RDS_TEST_SPEC_TRANS$>			= LayerSpecs<PRS_LAYER_TRANS_SPEC$>

                                RDSTestRec<RDS_TEST_SPEC_CRES_MIN$>			= LayerSpecs<PRS_LAYER_CRES_MIN$>
                                RDSTestRec<RDS_TEST_SPEC_CRES_TARGET$>		= LayerSpecs<PRS_LAYER_CRES_TARGET$>
                                RDSTestRec<RDS_TEST_SPEC_CRES_MAX$>			= LayerSpecs<PRS_LAYER_CRES_MAX$>
                                RDSTestRec<RDS_TEST_SPEC_CRES_UNITS$>		= LayerSpecs<PRS_LAYER_CRES_UNITS$>


                                FOR A = 11 TO 19
                                    RDSTestRec<A> = LayerSpecs<PRS_LAYER_THICK_MEASUREMENT$,A-10>
                                NEXT A
                                    
                                FOR A = 24 TO 32
                                    RDSTestRec<A> = LayerSpecs<PRS_LAYER_RES_MEASUREMENT$,A-23>
                                NEXT A

                                FOR A = 37 TO 45
                                    RDSTestRec<A> = LayerSpecs<PRS_LAYER_CONC_MEASUREMENT$,A-36>
                                NEXT A

                                FOR A = 50 to 58
                                    RDSTestRec<A> = LayerSpecs<PRS_LAYER_STRESS_MEASUREMENT$,A-49>
                                NEXT A

                                FOR A = 121 TO 129
                                    RDSTestRec<A> = LayerSpecs<PRS_LAYER_CRES_MEASUREMENT$,A-120>
                                NEXT A

                                RDSTestRec<RDS_TEST_SPEC_THICK_MPATTERN$>	= LayerSpecs<PRS_LAYER_THICK_MEASUREMENT$,PRS_MPATTERN$>
                                RDSTestRec<RDS_TEST_SPEC_RES_MPATTERN$>		= LayerSpecs<PRS_LAYER_RES_MEASUREMENT$,PRS_MPATTERN$>
                                RDSTestRec<RDS_TEST_SPEC_CON_MPATTERN$>		= LayerSpecs<PRS_LAYER_CONC_MEASUREMENT$,PRS_MPATTERN$>
                                RDSTestRec<RDS_TEST_SPEC_STRESS_MPATTERN$>	= LayerSpecs<PRS_LAYER_STRESS_MEASUREMENT$,PRS_MPATTERN$>
                                RDSTestRec<RDS_TEST_SPEC_CRES_MPATTERN$>	= LayerSpecs<PRS_LAYER_CRES_MEASUREMENT$,PRS_MPATTERN$>


                                RDSTestRec<RDS_TEST_REACTOR_TYPE$> = ReactorType

                                IF ReactorType = 'P' OR ReactorType = 'EPP' OR ThickFilmMet = 1 THEN

                                    SpecMap = XLATE('PROD_SPEC',PSNo,PROD_SPEC_TEST_POINT_MAP$,'X')		;* Added 4/8/2009 JCH new field in PROD_SPEC
                                    
                                    IF SpecMap NE '' THEN
                                    
                                        RDSTestRec<RDS_TEST_TEST_POINT_MAP$> = SpecMap
                                        
                                    END ELSE
                                    
                                        ;* Added ThickFilmMet check - 1/16/2009 JCH
                                    
                                        AllTargetThicks = XLATE('PROD_SPEC',PSNo,'THICK_TARGET_ALL','X')
                                        TargetCnt = COUNT(AllTargetThicks,@VM) + (AllTargetThicks NE '')
                                        
                                        BEGIN CASE
                                            CASE TargetCnt = 2
                                                CombinedThick = SUM(AllTargetThicks)
                                                
                                            CASE TargetCnt = 1 OR TargetCnt = 3
                                                CombinedThick = AllTargetThicks[-1,'B':@VM]
                                                
                                        END CASE
                                        
                                        IF OCONV(CombinedThick,'MD2') > '65.0' THEN
                                            RDSTestRec<RDS_TEST_TEST_POINT_MAP$> = 'FTIR_T'
                                        END ELSE
                                            RDSTestRec<RDS_TEST_TEST_POINT_MAP$> = 'FTIR'
                                        END
                                    END
                                        
                                END ELSE
                                    RDSTestRec<RDS_TEST_TEST_POINT_MAP$> = 'ASM17'	;* 17 Point linear test pattern until PROD_SPEC is updated support other types
                                END

                                Database_Services('WriteDataRow', 'RDS_TEST', RDSTestKey, RDSTestRec)
                            end else
                                Error_Services('Add', 'Error in ':Service:' service. Error calling obj_Prod_Spec("GetLayerProp")')
                            end
                        end
                    Next RDSTestKey
                end else
                    Error_Services('Add', 'Error in ':Service:' serivce. No RDS_TEST records found for RDS record ':RDSNo:'.')
                end
            end else
                Error_Services('Add', 'Error in ':Service:' serivce. Null PSNo returned for RDS record ':RDSNo:'.')
            end
        end else
            Error_Services('Add', 'Error in ':Service:' serivce. RDS record ':RDSNo:' does not exist.')
        end
    end else
        Error_Services('Add', 'Error in ':Service:' serivce. Null RDSNo passed in.')
    end
    
end service


Service ConvertRecordToJSON(KeyID, Record, ItemURL)
    
    jsonRecord    = ''

    If KeyID NE '' then

        If Record EQ '' then Record = Database_Services('ReadDataRow', 'PROD_SPEC', KeyID)
        If Error_Services('NoError') then
            @DICT    = Database_Services('GetTableHandle', 'DICT.PROD_SPEC')
            @ID      = KeyID
            @RECORD  = Record
            If SRP_JSON(objJSON, 'New', 'Object') then
                
                If SRP_JSON(objProdSpec, 'New', 'Object') then 
                    SRP_JSON(objProdSpec, 'SetValue', 'keyId', @ID)
                    SRP_JSON(objProdSpec, 'SetValue', 'specType', OConv({SPEC_TYPE}, '[SPEC_TYPE_CONV]'))
                    SRP_JSON(objProdSpec, 'SetValue', 'status', OConv({STATUS}, '[PROD_SPEC_STATUS_CONV]'))
                    SRP_JSON(objProdSpec, 'SetValue', 'minutesPerWafer', OConv({MINUTES_PER_WAFER}, 'MD3'))
                    SRP_JSON(objProdSpec, 'SetValue', 'proveInTime', OConv({PROVEIN_MIN}, 'MD3,'))
                    SRP_JSON(objProdSpec, 'SetValue', 'layerType', {LAYER_TYPE})
                    SRP_JSON(objProdSpec, 'SetValue', 'reactorType', OConv({REACTOR_TYPE}, '[REACT_TYPE_CONV,OPSREF]'))
                    SRP_JSON(objProdSpec, 'SetValue', 'susceptorType', {SUSCEPTOR_TYPE})
                    SRP_JSON(objProdSpec, 'SetValue', 'tubePressureType', {TUBE_PRESSURE_TYPE})
                    ProdVerNos          = {PROD_VER_NO}
                    CustEpiKeys         = {CEP_KEYS}
                    StageKeys           = {STAGE}
                    QAMetStages         = {QA_MET_STAGE}
                    QAMetTests          = {QA_MET_TEST}
                    QAMetProps          = {QA_MET_PROP}
                    QAMetPropDescs      = {QA_MET_PROP_DESC}
                    QAMetToolClasses    = {QA_MET_TOOL_CLASS}
                    QAMetRecipes        = {QA_MET_RECIPE}
                    QAMetRecipePatterns = {QA_MET_RECIPE_PATTERN}
                    QAMetMins           = {QA_MET_MIN}
                    QAMetMaxes          = {QA_MET_MAX}
                    QAPhaseMins         = {QA_MET_PHASE_MIN}
                    QAMetSlots          = {QA_MET_SLOT}
                    QAMetWfrQtys        = {QA_MET_WFR_QTY}
                    QAMetReactScheds    = {QA_MET_REACT_SCHED}
                    QAMetIntervals      = {QA_MET_INTERVAL}
                    QAMetStarts         = {QA_MET_START}
                    QAMetSeqs           = {QA_MET_SEQUENCE}
                    
                    // Add recipe layer object array
                    If SRP_JSON(objLayerArray, 'New', 'Array') then
                        LayerNos           = {LAYER_NO}
                        LayerIDs           = {LS_ID}
                        LayerTypes         = {LS_TYPE}
                        LayerRecipes       = {LS_RECIPE}
                        LayerDopants       = {LS_DOPANT}
                        LayerThickMins     = OConv({LS_THICK_MIN}, '[MET_PROP_CONV,THICK]')
                        LayerThickTargets  = OConv({LS_THICK_TARG}, '[MET_PROP_CONV,THICK]')
                        LayerThickMaxes    = OConv({LS_THICK_MAX}, '[MET_PROP_CONV,THICK]')
                        LayerThickUnits    = {LS_THICK_UNITS}
                        LayerThickAMins    = OConv({LS_THICKA_MIN}, '[MET_PROP_CONV,THICKA]')
                        LayerThickATargets = OConv({LS_THICKA_TARG}, '[MET_PROP_CONV,THICKA]')
                        LayerThickAMaxes   = OConv({LS_THICKA_MAX}, '[MET_PROP_CONV,THICKA]')
                        LayerThickAUnits   = {LS_THICKA_UNITS}
                        LayerResMins       = OConv({LS_RES_MIN}, '[MET_PROP_CONV,RES]')
                        LayerResTargets    = OConv({LS_RES_TARG}, '[MET_PROP_CONV,RES]')
                        LayerResMaxes      = OConv({LS_RES_MAX}, '[MET_PROP_CONV,RES]')
                        LayerResUnits      = {LS_RES_UNITS}
                        LayerSResMins      = OConv({LS_SRES_MIN}, '[MET_PROP_CONV,SRES]')
                        LayerSResTargets   = OConv({LS_SRES_TARG}, '[MET_PROP_CONV,SRES]')
                        LayerSResMaxes     = OConv({LS_SRES_MAX}, '[MET_PROP_CONV,SRES]')
                        LayerSResUnits     = {LS_SRES_UNITS}
                        If LayerNos NE '' then
                            For each LayerNo in LayerNos using @VM setting vPos
                                If SRP_JSON(objLayer, 'New', 'Object') then
                                    SRP_JSON(objLayer, 'SetValue', 'layerNo', layerNo)
                                    SRP_JSON(objLayer, 'SetValue', 'layerId', LayerIDs<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerType', LayerTypes<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerRecipe', LayerRecipes<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerDopant', LayerDopants<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerThickMin', LayerThickMins<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerThickTarget', LayerThickTargets<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerThickMax', LayerThickMaxes<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerThickUnits', LayerThickUnits<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerThickAMin', LayerThickAMins<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerThickATarget', LayerThickATargets<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerThickAMaxes', LayerThickAMaxes<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerThickAUnits', LayerThickAUnits<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerResMin', LayerResMins<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerResTarget', LayerResTargets<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerResMax', LayerResMaxes<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerResUnits', LayerResUnits<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerSResMin', LayerSResMins<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerSResTarget', LayerSResTargets<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerSResMax', LayerSResMaxes<0, vPos>)
                                    SRP_JSON(objLayer, 'SetValue', 'layerSResUnits', LayerSResUnits<0, vPos>)
                                    SRP_JSON(objLayerArray, 'Add', objLayer)
                                    SRP_JSON(objLayer, 'Release')
                                end
                            Next LayerNo
                        end
                        SRP_JSON(objProdSpec, 'Set', 'recipeLayers', objLayerArray)
                        SRP_JSON(objLayerArray, 'Release')
                    end
                    
                    // Add Prod Ver object array
                    If ProdVerNos NE '' then
                        If SRP_JSON(objProdVerArray, 'New', 'Array') then
                            For each ProdVerNo in ProdVerNos using @VM
                                ProdVerJSON = Prod_Ver_Services('ConvertRecordToJSON', ProdVerNo)
                                If SRP_JSON(objProdVer, 'Parse', ProdVerJSON) EQ '' then
                                    objTemp = SRP_JSON(objProdVer, 'Get', 'prodVer')
                                    SRP_JSON(objProdVerArray, 'Add', objTemp)
                                    SRP_JSON(objTemp, 'Release')
                                    SRP_JSON(objProdVer, 'Release')
                                end
                            Next ProdVerNo
                            SRP_JSON(objProdSpec, 'Set', 'prodVers', objProdVerArray)
                            SRP_JSON(objProdVerArray, 'Release')
                        end
                    end
                    
                    // Epi Part object array
                    EpiPartNo = {EPI_PART_NO}
                    If EpiPartNo NE '' then
                        If SRP_JSON(objEpiPart, 'New', 'Object') then
                            SRP_JSON(objEpiPart, 'SetValue', 'keyID', EpiPartNo)
                            WaferSize = Xlate('EPI_PART', EpiPartNo, 'SUB_WAFER_SIZE', 'X')
                            SRP_JSON(objEpiPart, 'SetValue', 'waferSize', WaferSize)
                            SRP_JSON(objProdSpec, 'Set', 'epiPart', objEpiPart)
                            SRP_JSON(objEpiPart, 'Release')
                        end
                    end
                    
                    // Cust Epi Part object array
                    If CustEpiKeys NE '' then
                        If SRP_JSON(objCustEpiArray, 'New', 'Array') then
                            For each CustEpiKey in CustEpiKeys using @VM
                                CustEpiJSON = Cust_Epi_Part_Services('ConvertRecordToJSON', CustEpiKey)
                                If SRP_JSON(objCustEpi, 'Parse', CustEpiJSON) EQ '' then
                                    objTemp = SRP_JSON(objCustEpi, 'Get', 'custEpiPart')
                                    SRP_JSON(objCustEpiArray, 'Add', objTemp)
                                    SRP_JSON(objTemp, 'Release')
                                    SRP_JSON(objCustEpi, 'Release')
                                end
                            Next CustEpiKey
                        end
                        SRP_JSON(objProdSpec, 'Set', 'custEpiParts', objCustEpiArray)
                        SRP_JSON(objCustEpiArray, 'Release')
                    end
                    
                    // Add PRS Stage (Run Stage) object array
                    If StageKeys NE '' then
                        If SRP_JSON(objStageArray, 'New', 'Array') then
                            For each StageKey in StageKeys using @VM
                                PRSKey       = KeyID:'*':StageKey
                                PRSStageJSON = PRS_Stage_Services('ConvertRecordToJSON', PRSKey)
                                If SRP_JSON(objPRSStage, 'Parse', PRSStageJSON) EQ '' then
                                    objTemp = SRP_JSON(objPRSStage, 'Get', 'prsStage')
                                    SRP_JSON(objStageArray, 'Add', objTemp)
                                    SRP_JSON(objTemp, 'Release')
                                    SRP_JSON(objPRSStage, 'Release')
                                end
                            Next StageKey
                        end
                        SRP_JSON(objProdSpec, 'Set', 'prsStages', objStageArray)
                        SRP_JSON(objStageArray, 'Release')
                    end
                    
                    RecipesAndPatterns = PSN_Services('GetMetrologyRecipesAndPatterns', KeyID)
                    If SRP_JSON(objRecipesAndPatternsArray, 'New', 'Array') then
                    	for each RecipeAndPattern in RecipesAndPatterns using @FM setting rpPos
                    		If SRP_JSON(objRecipeAndPattern, 'New', 'Object') then
                    			SRP_JSON(objRecipeAndPattern, 'SetValue', 'recipe', RecipeAndPattern<1, 1>, 'String')
                    			SRP_JSON(objRecipeAndPattern, 'SetValue', 'pattern', RecipeAndPattern<1, 2>, 'String')
                    			SRP_JSON(objRecipeAndPattern, 'SetValue', 'patternSize', RecipeAndPattern<1, 3>, 'Number')
                    			SRP_JSON(objRecipeAndPattern, 'SetValue', 'tool', RecipeAndPattern<1, 4>, 'String')
                    			SRP_JSON(objRecipesAndPatternsArray, 'Add', objRecipeAndPattern)
                    			SRP_JSON(objRecipeAndPattern, 'Release')
                    		end
                    	Next RecipeAndPattern
                    	SRP_JSON(objProdSpec, 'Set', 'recipesAndPatterns', objRecipesAndPatternsArray)
                    	SRP_JSON(objRecipesAndPatternsArray, 'Release')
                    end
                    
                    SRP_JSON(objJSON, 'Set', 'prodSpec', objProdSpec)
                    SRP_JSON(objProdSpec, 'Release')
                end
                
                If itemURL NE '' then
                    // The itemURL was passed in so add HAL+JSON properties.

                    // Create the _links property and then all link objects needed for this resource.
                    If SRP_JSON(objLinks, 'New', 'Object') then
                        // Create a self link.
                        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(objJSON, 'Set', '_links', objLinks)
                        SRP_JSON(objLinks, 'Release')
                    end

                    // Create the _class property for this resource.
                    SRP_JSON(objJSON, 'SetValue', '_class', 'resource')
                end
                jsonRecord = SRP_JSON(objJSON, 'Stringify', 'Styled')
                SRP_JSON(objJSON, 'Release')
            end else
                Error_Services('Add', 'Unable to create JSON representation in the ' : Service : ' service.')
            end
        end
    end else
        Error_Services('Add', 'KeyID argument was missing in the ' : Service : ' service.')
    end

    Response    = jsonRecord
    
end service


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

* * * * * * 
CheckAdHoc:
* * * * * *
    StageRecKey = PSNo: '*' : Stage
    StageRec = XLATE('PRS_STAGE', StageRecKey, '', 'X')

    IF StageRec<PRS_STAGE_CLEAN_SIG_REQ$> EQ 1 then
        GoSub   CheckCleansParms
        IF Error_Services('GetMessage') NE '' then
            Return 
        end
    end
    IF StageRec<PRS_STAGE_INSP_SIG_REQ$> EQ 1 then
        GoSub   CheckVisualParms
        IF Error_Services('GetMessage') NE '' then
            Return 
        end
    end
    IF StageRec<PRS_STAGE_SURFSCAN_SIG_REQ$> EQ 1 then
        GoSub   CheckSurfaceParms
        IF Error_Services('GetMessage') NE '' then
            Return 
        end
    end
    QAMetTests = StageRec<PRS_STAGE_MET_TEST$>
    IF QAMetTests<1,1> NE '' then
        GoSub   CheckQAMetParms
        IF Error_Services('GetMessage') NE '' then
            Return 
        end
    end

return








