Compile function Clean_Insp_Services(@Service, @Params)
/***********************************************************************************************************************

    Name        :   Clean_Insp_Services

    Description :   Handler program for all CLEAN_INSP services.

    Notes       :   Application errors should be logged using the Error Services module. There are a few methodological
                    assumptions built into way errors are managed which are important to understand in order to properly
                    work with Error Services:

                    - The term 'top' refers to the originating procedure of a call stack and the term 'bottom' refers to
                      the last routine (or the current routine) within a call stack. Within the OpenInsight Debugger
                      this will appear backwards since the originating procedure always appears at the bottom of the
                      list and the current routine appears at the top of the list. We are using this orientation because
                      it is common to refer to the process of calling other procedures as 'drilling down'.

                    - The reason for defining the orientation of the call stack is because Error_Services allows for
                      multiple error conditions to be appended to an original error. In most cases this will happen when
                      a procedure at the bottom of the stack generates an error condition and then returns to its
                      calling procedure. This higher level procedure can optionally add more information relevant to
                      itself. This continues as the call stack 'bubbles' its way back to the top to where the
                      originating procedure is waiting.

                    - Native OpenInsight commands that handle errors (e.g., Set_Status, Set_FSError, Set_EventStatus)
                      preserve their error state until explicitly cleared. This can hinder the normal execution of code
                      since subsequent procedures (usually SSPs) will fail if a pre-existing error condition exists.
                      Our philosophy is that error conditions should automatically be cleared before a new procedure
                      is executed to avoid this problem. However, the nature of Basic+ does not make this easy to
                      automate for any given stored procedure. Therefore, if a stored procedure wants to conform to our
                      philosophy then it should include a call into the 'Clear' service request at the top of the
                      program. Alternatively this can be done through a common insert (see SERVICE_SETUP for example.)

                    - Service modules will use the SERVICE_SETUP insert and therefore automatically clear out any
                      error conditions that were set before.

    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/16/22    djs     Original programmer.
        04/12/23    djs     Fixed WO_STEP_PROD_SPEC_ID$ equate typo within UpdateCleanInsp service.

***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler

$Insert LOGICAL
$Insert SERVICE_SETUP
$Insert WO_STEP_EQUATES
$Insert CLEAN_INSP_EQUATES
$Insert PRS_STAGE_EQUATES
$Insert WO_MAT_EQUATES

Declare function   Database_Services, SRP_JSON, Error_Services, obj_Clean_Insp
Declare subroutine Database_Services, SRP_JSON, Error_Services, obj_React_Run, Clean_Insp_Services, React_Run_Services
Declare subroutine Transaction_Services, Btree.Extract

GoToService

Return Response or ""

//-----------------------------------------------------------------------------
// SERVICES
//-----------------------------------------------------------------------------

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

    If KeyID NE '' then

        If Record EQ '' then Record = Database_Services('ReadDataRow', 'CLEAN_INSP', KeyID)
        If Error_Services('NoError') then
            @DICT    = Database_Services('GetTableHandle', 'DICT.CLEAN_INSP')
            @ID      = KeyID
            @RECORD  = Record
            If SRP_JSON(objJSON, 'New', 'Object') then
                
                If SRP_JSON(objCleanInsp, 'New', 'Object') then 
                    SRP_JSON(objCleanInsp, 'SetValue', 'keyId', @ID)
                    SRP_JSON(objCleanInsp, 'SetValue', 'stage', {STAGE})
                    // Add Cleans Object
                    If SRP_JSON(objCleans, 'New', 'Object') then
                        // Add Cleans Specs Object
                        If SRP_JSON(objSpecCleans, 'New', 'Object') then
                            SRP_JSON(objSpecCleans, 'SetValueArray', 'tools', {SPEC_CLEAN_TOOL}, @VM)
                            SRP_JSON(objSpecCleans, 'SetValueArray', 'recipes', {SPEC_CLEAN_RECIPE}, @VM)
                            SRP_JSON(objCleans, 'Set', 'specs', objSpecCleans)
                            SRP_JSON(objSpecCleans, 'Release')
                        end
                        // Add Cleans Operations
                        If SRP_JSON(objCleansOpsArray, 'New', 'Array') then
                            CleanTools = {CLEAN_TOOL}
                            If CleanTools NE '' then
                                BoatIds    = {CLEAN_BOAT_ID}
                                SRDNos     = {CLEAN_SRD_NO}
                                CleanUsers = {CLEAN_SIG}
                                CleanDTMs  = {CLEAN_SIG_DTM}
                                For each CleanTool in CleanTools using @VM setting vPos
                                    If SRP_JSON(objCleansOp, 'New', 'Object') then
                                        SRP_JSON(objCleansOp, 'SetValue', 'cleanTool', CleanTool)
                                        SRP_JSON(objCleansOp, 'SetValue', 'boatId', BoatIds<0, vPos>)
                                        SRP_JSON(objCleansOp, 'SetValue', 'srdNo', SRDNos<0, vPos>)
                                        SRP_JSON(objCleansOp, 'SetValue', 'userId', CleanUsers<0, vPos>)
                                        SRP_JSON(objCleansOp, 'SetValue', 'cleadDtm', OConv(CleanDTMs<0, vPos>, 'DT2/^H'))
                                        SRP_JSON(objCleansOpsArray, 'Add', objCleansOp)
                                        SRP_JSON(objCleansOp, 'Release')
                                    end
                                Next CleanTool
                            end
                            SRP_JSON(objCleans, 'Set', 'operations', objCleansOpsArray)
                            SRP_JSON(objCleansOpsArray, 'Release')
                        end
                        SRP_JSON(objCleanInsp, 'Set', 'cleans', objCleans)
                        SRP_JSON(objCleans, 'Release')
                    end
                    // Add Inspection Object
                    If SRP_JSON(objInsp, 'New', 'Object') then
                        // Add Specs Object
                        If SRP_JSON(objInspSpecs, 'New', 'Object') then
                            SRP_JSON(objInspSpecs, 'SetValue', 'microscopeReq', {SPEC_MICROSCOPE}, 'Boolean')
                            SRP_JSON(objInspSpecs, 'SetValue', 'brightlightReq', {SPEC_BRIGHTLIGHT}, 'Boolean')
                            SRP_JSON(objInspSpecs, 'SetValue', 'lpd', {SPEC_LPD})
                            SRP_JSON(objInspSpecs, 'SetValue', 'scratches', {SPEC_SCRATCHES})
                            SRP_JSON(objInspSpecs, 'SetValue', 'scratchLen', {SPEC_SCRATCH_LEN})
                            SRP_JSON(objInspSpecs, 'SetValue', 'pits', {SPEC_PITS})
                            SRP_JSON(objInspSpecs, 'SetValue', 'mounds', {SPEC_MOUNDS})
                            SRP_JSON(objInspSpecs, 'SetValue', 'stackFaults', {SPEC_STACK_FAULTS})
                            SRP_JSON(objInspSpecs, 'SetValue', 'spikes', {SPEC_SPIKES})
                            SRP_JSON(objInspSpecs, 'SetValue', 'spots', {SPEC_SPOTS})
                            SRP_JSON(objInspSpecs, 'SetValue', 'fov', {SPEC_FOV})
                            SRP_JSON(objInspSpecs, 'SetValue', 'blDefects', {SPEC_BL_DEFECTS})
                            SRP_JSON(objInspSpecs, 'SetValue', 'bsideScratches', {SPEC_BSIDE_SCRATCHES})
                            SRP_JSON(objInspSpecs, 'SetValue', 'bsideScratchLen', {SPEC_BSIDE_SCRATCH_LEN})
                            SRP_JSON(objInspSpecs, 'SetValue', 'bsideNodules', {SPEC_BSIDE_NODULES})
                            SRP_JSON(objInspSpecs, 'SetValue', 'bsideSpikes', {SPEC_BSIDE_SPIKES})
                            SRP_JSON(objInsp, 'Set', 'specs', objInspSpecs)
                            SRP_JSON(objInspSpecs, 'Release')
                        end
                        SRP_JSON(objCleanInsp, 'Set', 'inspection', objInsp)
                        SRP_JSON(objInsp, 'Release')
                    end
                    // Add Surfscan Object
                    If SRP_JSON(objSurfScan, 'New', 'Object') then 
                        If SRP_JSON(objSpecSurfScanArray, 'New', 'Array') then
                            SurfScanRecipes = {SPEC_SURFSCAN_RECIPE}
                            SpecDefects     = {SPEC_SURF_DEFECTS}
                            SpecHazes       = {SPEC_SURF_HAZE}
                            SpecSampleQtys  = {SPEC_SS_SAMP_QTY}
                            If SurfScanRecipes NE '' then
                                For each SurfScanRecipe in SurfScanRecipes using @VM setting vPos
                                    If SRP_JSON(objSpecSurfScan, 'New', Object) then
                                        SRP_JSON(objSpecSurfScan, 'SetValue', 'recipe', SurfScanRecipe)
                                        SRP_JSON(objSpecSurfScan, 'SetValue', 'defect', SpecDefects<0, vPos>)
                                        SRP_JSON(objSpecSurfScan, 'SetValue', 'haze', SpecHazes<0, vPos>)
                                        SRP_JSON(objSpecSurfScan, 'SetValue', 'sampleQty', SpecSampleQtys<0, vPos>)
                                        SRP_JSON(objSpecSurfScanArray, 'Add', objSpecSurfScan)
                                        SRP_JSON(objSpecSurfScan, 'Release')
                                    end

                                Next SurfScanRecipe
                            end
                            SRP_JSON(objSurfScan, 'Set', 'specs', objSpecSurfScanArray)
                            SRP_JSON(objSpecSurfScanArray, 'Release')
                        end
                         SRP_JSON(objCleanInsp, 'Set', 'surfScan', objSurfScan)
                         SRP_JSON(objSurfScan, 'Release')
                    end
                    
                    SRP_JSON(objJSON, 'Set', 'cleanInsp', objCleanInsp)
                    SRP_JSON(objCleanInsp, '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


Service ConvertJSONToRecord(JSON)
    
    If JSON NE '' then
        If SRP_JSON(objJSON, 'Parse', JSON) EQ '' then
            objCleanInsp = SRP_JSON(objJSON, 'Get', 'cleanInsp')
            @ID          = SRP_JSON(objCleanInsp, 'GetValue', 'keyId')
            If @ID NE '' then
                @Record = Database_Services('ReadDataRow', 'CLEAN_INSP', @ID)
                If Error_Services('NoError') then
                    @Dict = Database_Services('GetTableHandle', 'DICT.CLEAN_INSP')
                end
            end else
                Error_Services('Add', 'Error in ':Service:' service. Null value for cleanInsp.keyID.')
            end
            SRP_JSON(objCleanInsp, 'Release')
            SRP_JSON(objJSON, 'Release')
        end else
            Error_Services('Add', 'Error in ':Service:' service. Unable to parse JSON payload.')
        end
    end else
        Error_Services('Add', 'Error in ':Service:' service. Null JSON passed in.')
    end
    
    Response = @Record
    
End Service


//----------------------------------------------------------------------------------------------------------------------
// UpdateCleanInsp
//
// CleanInspKey - [Required]
//
// Updates or deletes a CLEAN_INSP record associated with an RDS or WM_OUT record per the PSN.
// This is useful when a PSN has changed such that PRS_STAGES have been added and/or removed and/or modified.
//----------------------------------------------------------------------------------------------------------------------
Service UpdateCleanInsp(CleanInspKey)

    CIRec = ''
    ErrorMsg     = ''
    If CleanInspKey NE '' then
        CIRec  = Database_Services('ReadDataRow', 'CLEAN_INSP', CleanInspKey)
        If CIRec NE '' then
            WONo      = CIRec<CLEAN_INSP_WO_NO$>
            WOStep    = CIRec<CLEAN_INSP_WO_STEP$>
            WOStepKey = WONo:'*':WOStep
            ReactType = Xlate('WO_STEP', WOStepKey, 'REACTOR_TYPE', 'X')
            If ReactType NE 'GAN' then 
                CassNo    = CIRec<CLEAN_INSP_CASS_NO$>
                Stage     = CIRec<CLEAN_INSP_STAGE$>
                RDSNo     = CIRec<CLEAN_INSP_RDS_NO$>
                PSNo      =  XLATE('WO_STEP', WONo:'*':WOStep, WO_STEP_PROD_SPEC_ID$, 'X')
                If ( (PSNo NE '') and (Stage NE '') ) then
                    PrsStageKey = PSNo:'*':Stage
                    PRSStageRec = Database_Services('ReadDataRow', 'PRS_STAGE', PrsStageKey)
                    IF PRSStageRec NE '' THEN
                        CIRec<CLEAN_INSP_SPEC_INSP_REQ$>        = PRSStageRec<PRS_STAGE_INSP_SIG_REQ$>
                        CIRec<CLEAN_INSP_SPEC_SURFSCAN_REQ$>    = PRSStageRec<PRS_STAGE_SURFSCAN_SIG_REQ$>
                        CIRec<CLEAN_INSP_SPEC_CLEAN_REQ$>       = PRSStageRec<PRS_STAGE_CLEAN_SIG_REQ$>
                        CIRec<CLEAN_INSP_SPEC_BRIGHTLIGHT$>     = PRSStageRec<PRS_STAGE_BRIGHTLIGHT$>		;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_MICROSCOPE$>      = PRSStageRec<PRS_STAGE_MICROSCOPE$>		;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_EDGE$>            = PRSStageRec<PRS_STAGE_EDGE$>		        ;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_PITS$>            = PRSStageRec<PRS_STAGE_PITS$>				;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_MOUNDS$>          = PRSStageRec<PRS_STAGE_MOUNDS$>			;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_BL_DEFECTS$>      = PRSStageRec<PRS_STAGE_BL_DEFECTS$>		;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_SPOTS$>           = PRSStageRec<PRS_STAGE_SPOTS$>			    ;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_FOV$>             = PRSStageRec<PRS_STAGE_FOV$>				;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_SCRATCHES$>       = PRSStageRec<PRS_STAGE_SCRATCHES$>		    ;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_SCRATCH_LEN$>     = PRSStageRec<PRS_STAGE_SCRATCH_LEN$>		;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_LPD$>             = PRSStageRec<PRS_STAGE_LPD$>				;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_STACK_FAULTS$>    = PRSStageRec<PRS_STAGE_STACK_FAULTS$>		;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_SPIKES$>          = PRSStageRec<PRS_STAGE_SPIKES$>			;* Visual Inspection
                        CIRec<CLEAN_INSP_INSP_INTERVAL$>        = PRSStageRec<PRS_STAGE_INSP_INTERVAL$>     ;* Visual Inspection
                        CIRec<CLEAN_INSP_SPEC_SURFSCAN_RECIPE$> = PRSStageRec<PRS_STAGE_SURFSCAN_RECIPE$>	;* Surface Scan
                        CIRec<CLEAN_INSP_SPEC_SURF_HAZE$>       = PRSStageRec<PRS_STAGE_SURF_HAZE$>			;* Surface Scan
                        CIRec<CLEAN_INSP_SPEC_SURF_DEFECTS$>    = PRSStageRec<PRS_STAGE_SURF_DEFECTS$>		;* Surface Scan
                        CIRec<CLEAN_INSP_SPEC_SS_SAMP_QTY$>     = PRSStageRec<PRS_STAGE_SS_SAMP_QTY$>		;* Surface Scan
                        CIRec<CLEAN_INSP_SPEC_CLEAN_TOOL$>      = PRSStageRec<PRS_STAGE_CLEAN_TOOL$>		;* Cleans
                        CIRec<CLEAN_INSP_SPEC_CLEAN_RECIPE$>    = PRSStageRec<PRS_STAGE_CLEAN_RECIPE$>		;* Cleans
                        Database_Services('WriteDataRow', 'CLEAN_INSP', CleanInspKey, CIRec, True$, False$, True$)
                    end else
                        // PRS Stage no longer exists, so delete the clean & insp record
                        Database_Services('DeleteDataRow', 'CLEAN_INSP', CleanInspKey)
                        CIRec = ''
                        // Remove references to the CLEAN_INSP record
                        Begin Case
                            Case ReactType EQ 'EPP'
                                // Remove CleanInspKey from WO_MAT_EPI_CI_NO$/WO_MAT_EPO_CI_NO$
                                WOMatKey = WONo:'*':CassNo
                                WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
                                If Error_Services('NoError') then
                                    Begin Case
                                        Case Stage EQ 'PRE'
                                            WOMatRec<WO_MAT_EPI_CI_NO$> = ''
                                            Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
                                        Case Stage EQ 'POST'
                                            WOMatRec<WO_MAT_EPO_CI_NO$> = ''
                                            Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
                                        Case Otherwise$
                                            // We should never get here as EPP only supports CLEAN_INSP on the PRE and POST stages
                                            Null
                                    End Case
                                end
                            Case Otherwise$
                                // Remove CleanInspKey and stage from REACT_RUN record
                                React_Run_Services('RemCleanInsp', RDSNo, CleanInspKey, Stage)
                        End Case
                    end
                end else
                    ErrorMsg = 'Error in ':Service:' service. Could not determine PRS_STAGE key for CLEAN_INSP record ':CleanInspKey:'.'
                end
            end else
                ErrorMsg = 'Error in ':Service:' service. GaN is not supported.'
            end
        end else
            ErrorMsg = 'Error in ':Service:' service. CLEAN_INSP record ':CleanInspKey:' does not exist.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null CleanInsp key passed into service.'
    end

    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end else
        Response = CIRec
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// UpdateAllCleanInsp
//
// WOMatKey - [Required]
//
// Creates/Deletes/Updates all CLEAN_INSP records associated with an RDS or WM_OUT record per the PSN.
// This is useful when a PSN has changed such that PRS_STAGES have been added and/or removed and/or modified.
//----------------------------------------------------------------------------------------------------------------------
Service UpdateAllCleanInsp(WOMatKey)
    
    ErrorMsg     = ''
    If WOMatKey NE '' then
        WONo   = Field(WOMatKey, '*', 1)
        CassNo = Field(WOMatKey, '*', 2)
        PSNo   = Xlate('WO_STEP', WONo:'*1', WO_STEP_PROD_SPEC_ID$, 'X')
        If PSNo NE '' then
            ReactType = Xlate('PROD_SPEC', PSNo, 'REACTOR_TYPE', 'X')
            Begin Case
                Case ReactType EQ 'EPP'
                    WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
                    // Update CLEAN_INSP records associated with the WM_OUT record
                    // The app on supports CLEAN_INSP records on the PRE and POST stages
                    PreCINo  = Xlate('WO_MAT', WOMatKey, WO_MAT_EPI_CI_NO$, 'X')
                    Begin Case
                        Case PreCINo NE ''
                            // Update/delete the CLEAN_INSP record 
                            Clean_Insp_Services('UpdateCleanInsp', PreCINo)
                        Case ( (PreCINo EQ '') and (RowExists('PRS_STAGE', PSNo:'*PRE') ) )
                            // Create the CLEAN_INSP record
                            ociParms                     = WONo:@RM		;* WONo	
                            ociParms                    := 1:@RM		;* WOStep
                            ociParms                    := CassNo:@RM	;* CassNo
                            ociParms                    := 'PRE':@RM	;* Stage	;* Pre Epi Cleaning on inbound material
                            ociParms                    := '':@RM		;* RDSNo	;* No specific RDS on Epi Pro inbound material
                            ociParms                    := PSNo:@RM		;* PSNo
                            ociParms                    := ''			;* PSRec    ;* Optional
                            WOMatRec<WO_MAT_EPI_CI_NO$>  = obj_Clean_Insp('Create',ociParms)	
                            Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
                    End Case
                    PostCINo = Xlate('WO_MAT', WOMatKey, WO_MAT_EPO_CI_NO$, 'X')
                    Begin Case
                        Case PostCINo NE ''
                            // Update/delete the CLEAN_INSP record 
                            Clean_Insp_Services('UpdateCleanInsp', PostCINo)
                        Case ( (PostCINo EQ '') and (RowExists('PRS_STAGE', PSNo:'*POST') ) )
                            // Create the CLEAN_INSP record
                            ociParms                     = WONo:@RM			;* WONo	
                            ociParms                    := 1:@RM			;* WOStep
                            ociParms                    := CassNo:@RM		;* CassNo
                            ociParms                    := 'POST':@RM		;* Stage	;* Pre Epi Cleaning on inbound material
                            ociParms                    := '':@RM			;* RDSNo	;* No specific RDS on Epi Pro inbound material
                            ociParms                    := PSNo:@RM		    ;* PSNo
                            ociParms                    := ''			    ;* PSRec    ;* Optional
                            WOMatRec<WO_MAT_EPO_CI_NO$>  = obj_Clean_Insp('Create',ociParms)	
                            Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
                    End Case
                Case ReactType EQ 'GAN'
                    // Not supported
                    ErrorMsg = 'Error in ':Service:' service. GAN is not supported.'
                Case Otherwise$
                    // Update CLEAN_INSP records associated with the RDS record
                    RDSNo    = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
                    Stages   = 'PRE,FWI,LWI,POST'
                    For each Stage in Stages using ','
                        CICol     = Stage:'_CI_NO'
                        StageCINo = Xlate('RDS', RDSNo, CICol, 'X')
                        Begin Case
                            Case StageCINo NE ''
                                // Update/delete the CLEAN_INSP record 
                                Clean_Insp_Services('UpdateCleanInsp', StageCINo)
                            Case ( (StageCINo EQ '') and (RowExists('PRS_STAGE', PSNo:'*':Stage) ) )
                                // Create the CLEAN_INSP record
                                ociParms  = WONo:@RM        ;* WONo	
                                ociParms := 1:@RM			;* WOStep
                                ociParms := CassNo:@RM		;* CassNo
                                ociParms := Stage:@RM		;* Stage	;* Pre Epi Cleaning on inbound material
                                ociParms := RDSNo:@RM		;* RDSNo	
                                ociParms := PSNo:@RM		;* PSNo
                                ociParms := ''			    ;* PSRec    ;* Optional
                                NewCINo   = obj_Clean_Insp('Create',ociParms)
                                // Add the stage and key ID to the REACT_RUN record
                                React_Run_Services('AddCleanInsp', RDSNo, NewCINo, Stage)
                        End Case
                    Next Stage
            End Case
        end else
            ErrorMsg = 'Error in ':Service:' service. Could not determine PS_NO for WO_MAT record ':WOMatKey:'.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null WO_MAT key passed into service.'
    end

    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
        Response = False$
    end else
        Response = True$
    end
    
end service


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This service functions as a means to get the latest CINo for a specified RDS No where the defectivity measurements took place
// This is specifically used to have the latest defectivity data
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Service GetLatestDefectCINoByRDSId(RDSNo)
	
    LatestCINo    = ''
    LatestInspDtm = ''
    Open 'DICT CLEAN_INSP' to @DICT then
        SrchString = 'RDS_NO':@VM:RDSNo:@FM
        CIList     = ''
        Option     = ''
        Flag       = ''
        Btree.Extract(SrchString, 'CLEAN_INSP', @DICT, CIList, Option, Flag)
        If CIList NE '' then
            for each CleanInspKey in CIList using @VM
                ThisCIInspDtm    = Database_Services('ReadDataColumn', 'CLEAN_INSP', CleanInspKey, CLEAN_INSP_SCAN_SIG_DTM$ , True$, 0, False$)<1,1>
                ThisCIInspDefAvg = Database_Services('ReadDataColumn', 'CLEAN_INSP', CleanInspKey, CLEAN_INSP_SCAN_SUM_OF_DEF_AVG$ , True$, 0, False$)
                If ThisCIInspDtm GT LatestInspDtm AND ThisCIInspDefAvg NE '' then
                    LatestCINo    = CleanInspKey
                    LatestInspDtm = ThisCIInspDtm
                end
            Next CleanInspKey
        end
    end
    Response = LatestCINo
    
end service


Service PushSigProfileToWoMat(CleanInspKey)

	ErrorMsg = ''
	If (CleanInspKey NE '') then
		If RowExists('CLEAN_INSP', CleanInspKey) then
			Record    = Database_Services('ReadDataRow', 'CLEAN_INSP', CleanInspKey)
			If Error_Services('NoError') then 
				WONo      = Record<CLEAN_INSP_WO_NO$>
				ReactType = Xlate('WO_LOG', WONo, 'REACT_TYPE', 'X')
				NonEpiPro = ( (ReactType NE 'EPP') and (ReactType NE 'GAN') )
				If NonEpiPro then
					// Sync up Insp, Clean, and SurfScan signatures with WO_MAT signature profile
					Stage        = Record<CLEAN_INSP_STAGE$>
					WOMatKey     = Xlate('CLEAN_INSP', CleanInspKey, 'WO_MAT_KEY', 'X')
					If WOMatKey NE '' then
						WOMatRec     = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
						If Error_Services('NoError') then 
							WOMatSigProf = WOMatRec<WO_MAT_SIG_PROFILE$>
							WOMatSigs    = WOMatRec<WO_MAT_SIGNATURE$>
							WOMatSigDTMs = WOMatRec<WO_MAT_SIG_DTM$>
							
							InspSig         = Record<CLEAN_INSP_INSP_SIG$>
							InspSigDTM      = Record<CLEAN_INSP_INSP_SIG_DTM$>
							CleanSig        = Record<CLEAN_INSP_CLEAN_SIG$>
							CleanSigDTM     = Record<CLEAN_INSP_CLEAN_SIG_DTM$>
							ScanSig         = Record<CLEAN_INSP_SCAN_SIG$>
							ScanSigDTM      = Record<CLEAN_INSP_SCAN_SIG_DTM$>
							
							WOMatStage = '1':Stage:'I'
							Locate WOMatStage in WOMatSigProf using @VM setting vPos then
								WOMatSigs<0, vPos>    = InspSig[-1, 'B':@VM]
								WOMatSigDTMs<0, vPos> = InspSigDTM[-1, 'B':@VM]
							end
							
							WOMatStage = '1':Stage:'C'
							Locate WOMatStage in WOMatSigProf using @VM setting vPos then
								WOMatSigs<0, vPos>    = CleanSig[-1, 'B':@VM]
								WOMatSigDTMs<0, vPos> = CleanSigDTM[-1, 'B':@VM]
							end
							
							WOMatStage = '1':Stage:'S'
							Locate WOMatStage in WOMatSigProf using @VM setting vPos then
								WOMatSigs<0, vPos>    = ScanSig[-1, 'B':@VM]
								WOMatSigDTMs<0, vPos> = ScanSigDTM[-1, 'B':@VM]
							end

							NumSteps     = DCount(WOMatSigProf, @VM)
							WOMatSigs    = Field(WOMatSigs, @VM, 1, NumSteps)
							WOMatSigDTMs = Field(WOMatSigDTMs, @VM, 1, NumSteps)
							Transaction_Services('PostWriteFieldTransaction', 'WO_MAT', WOMatKey, WO_MAT_SIGNATURE$, WOMatSigs)
							Transaction_Services('PostWriteFieldTransaction', 'WO_MAT', WOMatKey, WO_MAT_SIG_DTM$, WOMatSigDTMs)
						end else
							ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
						end
					end else
						ErrorMsg = 'Error in ':Service:' service. Null WO_MAT_KEY returned for CLEAN_INSP ':CleanInspKey:'.'
					end
				end			
			end else
				ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
			end
		end else
			ErrorMsg = 'Error in ':Service:' service. CLEAN_INSP ':CleanInspKey:' does not exist.'
		end
	end else
		ErrorMsg = 'Error in ':Service:' service. Null CleanInspKey passed into service.'
	end
	
	If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
	
end service


Service UpdatePostCISampleQty(CleanInspKey)

	ErrorMsg  = ''
	If CleanInspKey NE '' then
		If RowExists('CLEAN_INSP', CleanInspKey) then
			Record = Database_Services('ReadDataRow', 'CLEAN_INSP', CleanInspKey)
			If Error_Services('NoError') then
				Stage     = Record<CLEAN_INSP_STAGE$>
				If Stage EQ 'LWI' then
					RDSNo     = Record<CLEAN_INSP_RDS_NO$>
					PostCIKey = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
					If PostCIKey NE '' then
						FailedWafers = Record<CLEAN_INSP_FAILED_WAFERS$>
						If FailedWafers NE '' then 
							NewPostSpecSampleQty = Sum(FailedWafers)
							PostCIRec            = Database_Services('ReadDataRow', 'CLEAN_INSP', PostCIKey)
							PostSpecRecipes      = PostCIRec<CLEAN_INSP_SPEC_SURFSCAN_RECIPE$>
							NumRecipes           = DCount(PostSpecRecipes, @VM)
							If NumRecipes GT 0 then
								UpdatePostRec = False$
								For RecipeIndex = 1 to NumRecipes
									CurrSpec = PostCIRec<CLEAN_INSP_SPEC_SS_SAMP_QTY$, RecipeIndex>
									If ( (CurrSpec EQ '') or (CurrSpec LT NewPostSpecSampleQty) ) then
										UpdatePostRec                                        = True$
										PostCIRec<CLEAN_INSP_SPEC_SS_SAMP_QTY$, RecipeIndex> = NewPostSpecSampleQty
									end
								Next RecipeIndex
								If UpdatePostRec then
									PostCISpecSampQty = PostCIRec<CLEAN_INSP_SPEC_SS_SAMP_QTY$>
									Transaction_Services('PostWriteFieldTransaction', 'CLEAN_INSP', PostCIKey, CLEAN_INSP_SPEC_SS_SAMP_QTY$, PostCISpecSampQty)
								end
							end
						end
					end
				end	
			end else
				ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
			end
		end else
			ErrorMsg = 'Error in ':Service:' service. CLEAN_INSP ':CleanInspKey:' does not exist.'
		end
	end else
		ErrorMsg = 'Error in ':Service:' service. Null CleanInspKey passed into service.'
	end
	
	If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
	
end service

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

